智慧保安后台管理-外网项目备份
钟日健
2026-06-01 62eb499b0c969f246d3245d1429a97da4de1ce28
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
package org.springblade.common.desensitization;
 
import cn.hutool.core.convert.Convert;
import cn.hutool.core.util.DesensitizedUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.JSONPath;
import liquibase.util.StringUtils;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springblade.core.secure.utils.AuthUtil;
import org.springframework.stereotype.Component;
 
import static cn.hutool.core.util.DesensitizedUtil.DesensitizedType.FIXED_PHONE;
 
/**
 * 数据脱敏切面
 * @author zhongrj
 * @date 2023-09-11
 */
@Slf4j
@Aspect
@Component
public class DesensitizationAspect {
 
    /**
     * 脱敏环绕通知
     * @param pjp
     * @param desensitizationWord
     * @return
     * @throws Throwable
     */
    @Around("@annotation(desensitizationWord)")
    public Object around(ProceedingJoinPoint pjp, DesensitizationWord desensitizationWord) throws Throwable{
        Object object = pjp.proceed();
        // 判断是否需要脱敏处理
        if (isDesensitization()) {
            try {
                Class<?> aClass = object.getClass();
                // 改变返回值
                Desensitization[] value = desensitizationWord.value();
                JSONObject ob = JSON.parseObject(JSON.toJSONString(object));
 
                // 循环处理每一个 json 路径下的值
                for (Desensitization s : value) {
                    replace(ob, s);
                }
                object = JSON.parseObject(JSON.toJSONString(ob), aClass);
            } catch (Exception e) {
                log.info("数据脱敏失败:" + e.getMessage());
            }
        }
        return object;
    }
 
    /**
     * 判断是否需要脱敏
     * @return
     */
    private boolean isDesensitization() {
        // 获取用户角色,根据角色判断是否进行脱敏处理
        String userRole = AuthUtil.getUserRole();
        // 如果用户角色为null,则需要脱敏
        if (null!=userRole && !userRole.equals("")){
            // 判断角色是否为民警角色,如果是则不需要脱敏,否则需要脱敏处理
            if (userRole.equals("民警")){
                return false;
            }
        }
        return true;
    }
 
    /**
     * 脱敏
     */
    private Object sensitiveByString(Object value) {
        if (StringUtils.isNotEmpty(value.toString())) {
            String st = Convert.toStr(value);
            st = st.substring(0, st.length() - 3 > 0 ? 3 : st.length()) + "****" + st.substring(Math.max(st.length() - 4, 0));
            return st;
        }
        return value;
    }
 
    /**
     * 敏感数据替换
     *
     * @param jsonObject
     * @param s
     */
    private void replace(JSONObject jsonObject, Desensitization s) {
        // 只有传入的 JSON 路径在 这个 JSONObject 中才会进行脱敏处理
        if (JSONPath.contains(jsonObject, s.jsonPath())) {
 
            // 查询是否有数组 列表 类型的数据需要脱敏
            int index = s.jsonPath().lastIndexOf("[*]");
            if (index > -1) {
                String prefix = StrUtil.subPre(s.jsonPath(), index);
                String suffix = StrUtil.subSuf(s.jsonPath(), index + 3);
                // 提取json 路径下的 数组\链表 元素
                Object eval = JSONPath.eval(jsonObject, prefix);
 
                // 将数组\链表 元素 转为 JSONArray 方便做 统一格式处理
                JSONArray jsonArray = (JSONArray) eval;
                int size = jsonArray.size();
                for (int i = 0; i < size; i++) {
                    // 由于脱敏数组内部的参数传入格式为 :jsonPath = "$.data.records[*].username"
                    // 所以需要重新组装 jsonPath 将 * 号 替换成具体的值
                    String indexJsonPath = StrUtil.strBuilder().append(prefix).append("[").append(i).append("]").append(suffix).toString();
                    // 使用 cn.hutool.core.convert Convert.toStr 转换为字符串 如果给定的值为null,或者转换失败,返回默认值null,这样可以减少报错,避免程序异常
                    String desensitized = Convert.toStr(JSONPath.eval(jsonObject, indexJsonPath));
                    if (StrUtil.isBlank(desensitized)) {
                        continue;
                    }
                    // 如果是默认指定 则使用默认方式脱敏
                    if (s.desensitizedType() == FIXED_PHONE) {
                        desensitized = sensitiveByString(desensitized).toString();
                    }else {
                        // 否则使用 cn.hutool.core.util 进行数据脱敏
                        desensitized = DesensitizedUtil.desensitized(desensitized, s.desensitizedType());
                    }
 
                    // 使用JSON 路径操作,将已经脱敏的新数据,放入之前未脱敏的数据地址处,替换未脱敏数据
                    JSONPath.set(jsonObject, indexJsonPath, desensitized);
                }
            } else {
                // 使用 cn.hutool.core.convert Convert.toStr 转换为字符串 如果给定的值为null,或者转换失败,返回默认值null,这样可以减少报错,避免程序异常
                Object eval = JSONPath.eval(jsonObject, Convert.toStr(s.jsonPath()));
                String desensitized = "";
                if (s.desensitizedType() == FIXED_PHONE) {
                    desensitized = sensitiveByString(Convert.toStr(eval)).toString();
                }else {
                    desensitized = DesensitizedUtil.desensitized(Convert.toStr(eval), s.desensitizedType());
                }
                JSONPath.set(jsonObject, s.jsonPath(), desensitized);
            }
        }
    }
}