ruoyi-common/src/main/java/com/ruoyi/common/annotation/Sensitive.java | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
ruoyi-common/src/main/java/com/ruoyi/common/config/serializer/SensitiveJsonSerializer.java | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
ruoyi-common/src/main/java/com/ruoyi/common/enums/DesensitizedType.java | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
ruoyi-common/src/main/java/com/ruoyi/common/utils/DesensitizedUtil.java | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
ruoyi-common/src/main/java/com/ruoyi/common/utils/StringUtils.java | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 |
ruoyi-common/src/main/java/com/ruoyi/common/annotation/Sensitive.java
对比新文件 @@ -0,0 +1,24 @@ package com.ruoyi.common.annotation; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; import com.fasterxml.jackson.annotation.JacksonAnnotationsInside; import com.fasterxml.jackson.databind.annotation.JsonSerialize; import com.ruoyi.common.config.serializer.SensitiveJsonSerializer; import com.ruoyi.common.enums.DesensitizedType; /** * 数据脱敏注解 * * @author ruoyi */ @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.FIELD) @JacksonAnnotationsInside @JsonSerialize(using = SensitiveJsonSerializer.class) public @interface Sensitive { DesensitizedType desensitizedType(); } ruoyi-common/src/main/java/com/ruoyi/common/config/serializer/SensitiveJsonSerializer.java
对比新文件 @@ -0,0 +1,67 @@ package com.ruoyi.common.config.serializer; import java.io.IOException; import java.util.Objects; import com.fasterxml.jackson.core.JsonGenerator; import com.fasterxml.jackson.databind.BeanProperty; import com.fasterxml.jackson.databind.JsonMappingException; import com.fasterxml.jackson.databind.JsonSerializer; import com.fasterxml.jackson.databind.SerializerProvider; import com.fasterxml.jackson.databind.ser.ContextualSerializer; import com.ruoyi.common.annotation.Sensitive; import com.ruoyi.common.core.domain.model.LoginUser; import com.ruoyi.common.enums.DesensitizedType; import com.ruoyi.common.utils.SecurityUtils; /** * 数据脱敏序列化过滤 * * @author ruoyi */ public class SensitiveJsonSerializer extends JsonSerializer<String> implements ContextualSerializer { private DesensitizedType desensitizedType; @Override public void serialize(String value, JsonGenerator gen, SerializerProvider serializers) throws IOException { if (desensitization()) { gen.writeString(desensitizedType.desensitizer().apply(value)); } else { gen.writeString(value); } } @Override public JsonSerializer<?> createContextual(SerializerProvider prov, BeanProperty property) throws JsonMappingException { Sensitive annotation = property.getAnnotation(Sensitive.class); if (Objects.nonNull(annotation) && Objects.equals(String.class, property.getType().getRawClass())) { this.desensitizedType = annotation.desensitizedType(); return this; } return prov.findValueSerializer(property.getType(), property); } /** * 是否需要脱敏处理 */ private boolean desensitization() { try { LoginUser securityUser = SecurityUtils.getLoginUser(); // 管理员不脱敏 return !securityUser.getUser().isAdmin(); } catch (Exception e) { return true; } } } ruoyi-common/src/main/java/com/ruoyi/common/enums/DesensitizedType.java
对比新文件 @@ -0,0 +1,59 @@ package com.ruoyi.common.enums; import java.util.function.Function; import com.ruoyi.common.utils.DesensitizedUtil; /** * 脱敏类型 * * @author ruoyi */ public enum DesensitizedType { /** * 姓名,第2位星号替换 */ USERNAME(s -> s.replaceAll("(\\S)\\S(\\S*)", "$1*$2")), /** * 密码,全部字符都用*代替 */ PASSWORD(DesensitizedUtil::password), /** * 身份证,中间10位星号替换 */ ID_CARD(s -> s.replaceAll("(\\d{4})\\d{10}(\\d{4})", "$1** **** ****$2")), /** * 手机号,中间4位星号替换 */ PHONE(s -> s.replaceAll("(\\d{3})\\d{4}(\\d{4})", "$1****$2")), /** * 电子邮箱,仅显示第一个字母和@后面的地址显示,其他星号替换 */ EMAIL(s -> s.replaceAll("(^.)[^@]*(@.*$)", "$1****$2")), /** * 银行卡号,保留最后4位,其他星号替换 */ BANK_CARD(s -> s.replaceAll("\\d{15}(\\d{3})", "**** **** **** **** $1")), /** * 车牌号码,包含普通车辆、新能源车辆 */ CAR_LICENSE(DesensitizedUtil::carLicense); private final Function<String, String> desensitizer; DesensitizedType(Function<String, String> desensitizer) { this.desensitizer = desensitizer; } public Function<String, String> desensitizer() { return desensitizer; } } ruoyi-common/src/main/java/com/ruoyi/common/utils/DesensitizedUtil.java
对比新文件 @@ -0,0 +1,49 @@ package com.ruoyi.common.utils; /** * 脱敏工具类 * * @author ruoyi */ public class DesensitizedUtil { /** * 密码的全部字符都用*代替,比如:****** * * @param password 密码 * @return 脱敏后的密码 */ public static String password(String password) { if (StringUtils.isBlank(password)) { return StringUtils.EMPTY; } return StringUtils.repeat('*', password.length()); } /** * 车牌中间用*代替,如果是错误的车牌,不处理 * * @param carLicense 完整的车牌号 * @return 脱敏后的车牌 */ public static String carLicense(String carLicense) { if (StringUtils.isBlank(carLicense)) { return StringUtils.EMPTY; } // 普通车牌 if (carLicense.length() == 7) { carLicense = StringUtils.hide(carLicense, 3, 6); } else if (carLicense.length() == 8) { // 新能源车牌 carLicense = StringUtils.hide(carLicense, 3, 7); } return carLicense; } } ruoyi-common/src/main/java/com/ruoyi/common/utils/StringUtils.java
@@ -23,6 +23,9 @@ /** 下划线 */ private static final char SEPARATOR = '_'; /** 星号 */ private static final char ASTERISK = '*'; /** * 获取参数不为空值 * @@ -164,6 +167,49 @@ } /** * 替换指定字符串的指定区间内字符为"*" * * @param str 字符串 * @param startInclude 开始位置(包含) * @param endExclude 结束位置(不包含) * @return 替换后的字符串 */ public static String hide(CharSequence str, int startInclude, int endExclude) { if (isEmpty(str)) { return NULLSTR; } final int strLength = str.length(); if (startInclude > strLength) { return NULLSTR; } if (endExclude > strLength) { endExclude = strLength; } if (startInclude > endExclude) { // 如果起始位置大于结束位置,不替换 return NULLSTR; } final char[] chars = new char[strLength]; for (int i = 0; i < strLength; i++) { if (i >= startInclude && i < endExclude) { chars[i] = ASTERISK; } else { chars[i] = str.charAt(i); } } return new String(chars); } /** * 截取字符串 * * @param str 字符串