正在显示
5 个修改的文件
包含
245 行增加
和
0 行删除
| 1 | +package com.ruoyi.common.annotation; | ||
| 2 | + | ||
| 3 | +import java.lang.annotation.ElementType; | ||
| 4 | +import java.lang.annotation.Retention; | ||
| 5 | +import java.lang.annotation.RetentionPolicy; | ||
| 6 | +import java.lang.annotation.Target; | ||
| 7 | +import com.fasterxml.jackson.annotation.JacksonAnnotationsInside; | ||
| 8 | +import com.fasterxml.jackson.databind.annotation.JsonSerialize; | ||
| 9 | +import com.ruoyi.common.config.serializer.SensitiveJsonSerializer; | ||
| 10 | +import com.ruoyi.common.enums.DesensitizedType; | ||
| 11 | + | ||
| 12 | +/** | ||
| 13 | + * 数据脱敏注解 | ||
| 14 | + * | ||
| 15 | + * @author ruoyi | ||
| 16 | + */ | ||
| 17 | +@Retention(RetentionPolicy.RUNTIME) | ||
| 18 | +@Target(ElementType.FIELD) | ||
| 19 | +@JacksonAnnotationsInside | ||
| 20 | +@JsonSerialize(using = SensitiveJsonSerializer.class) | ||
| 21 | +public @interface Sensitive | ||
| 22 | +{ | ||
| 23 | + DesensitizedType desensitizedType(); | ||
| 24 | +} |
ruoyi-common/src/main/java/com/ruoyi/common/config/serializer/SensitiveJsonSerializer.java
0 → 100644
| 1 | +package com.ruoyi.common.config.serializer; | ||
| 2 | + | ||
| 3 | +import java.io.IOException; | ||
| 4 | +import java.util.Objects; | ||
| 5 | +import com.fasterxml.jackson.core.JsonGenerator; | ||
| 6 | +import com.fasterxml.jackson.databind.BeanProperty; | ||
| 7 | +import com.fasterxml.jackson.databind.JsonMappingException; | ||
| 8 | +import com.fasterxml.jackson.databind.JsonSerializer; | ||
| 9 | +import com.fasterxml.jackson.databind.SerializerProvider; | ||
| 10 | +import com.fasterxml.jackson.databind.ser.ContextualSerializer; | ||
| 11 | +import com.ruoyi.common.annotation.Sensitive; | ||
| 12 | +import com.ruoyi.common.core.domain.model.LoginUser; | ||
| 13 | +import com.ruoyi.common.enums.DesensitizedType; | ||
| 14 | +import com.ruoyi.common.utils.SecurityUtils; | ||
| 15 | + | ||
| 16 | +/** | ||
| 17 | + * 数据脱敏序列化过滤 | ||
| 18 | + * | ||
| 19 | + * @author ruoyi | ||
| 20 | + */ | ||
| 21 | +public class SensitiveJsonSerializer extends JsonSerializer<String> implements ContextualSerializer | ||
| 22 | +{ | ||
| 23 | + private DesensitizedType desensitizedType; | ||
| 24 | + | ||
| 25 | + @Override | ||
| 26 | + public void serialize(String value, JsonGenerator gen, SerializerProvider serializers) throws IOException | ||
| 27 | + { | ||
| 28 | + if (desensitization()) | ||
| 29 | + { | ||
| 30 | + gen.writeString(desensitizedType.desensitizer().apply(value)); | ||
| 31 | + } | ||
| 32 | + else | ||
| 33 | + { | ||
| 34 | + gen.writeString(value); | ||
| 35 | + } | ||
| 36 | + } | ||
| 37 | + | ||
| 38 | + @Override | ||
| 39 | + public JsonSerializer<?> createContextual(SerializerProvider prov, BeanProperty property) | ||
| 40 | + throws JsonMappingException | ||
| 41 | + { | ||
| 42 | + Sensitive annotation = property.getAnnotation(Sensitive.class); | ||
| 43 | + if (Objects.nonNull(annotation) && Objects.equals(String.class, property.getType().getRawClass())) | ||
| 44 | + { | ||
| 45 | + this.desensitizedType = annotation.desensitizedType(); | ||
| 46 | + return this; | ||
| 47 | + } | ||
| 48 | + return prov.findValueSerializer(property.getType(), property); | ||
| 49 | + } | ||
| 50 | + | ||
| 51 | + /** | ||
| 52 | + * 是否需要脱敏处理 | ||
| 53 | + */ | ||
| 54 | + private boolean desensitization() | ||
| 55 | + { | ||
| 56 | + try | ||
| 57 | + { | ||
| 58 | + LoginUser securityUser = SecurityUtils.getLoginUser(); | ||
| 59 | + // 管理员不脱敏 | ||
| 60 | + return !securityUser.getUser().isAdmin(); | ||
| 61 | + } | ||
| 62 | + catch (Exception e) | ||
| 63 | + { | ||
| 64 | + return true; | ||
| 65 | + } | ||
| 66 | + } | ||
| 67 | +} |
| 1 | +package com.ruoyi.common.enums; | ||
| 2 | + | ||
| 3 | +import java.util.function.Function; | ||
| 4 | +import com.ruoyi.common.utils.DesensitizedUtil; | ||
| 5 | + | ||
| 6 | +/** | ||
| 7 | + * 脱敏类型 | ||
| 8 | + * | ||
| 9 | + * @author ruoyi | ||
| 10 | + */ | ||
| 11 | +public enum DesensitizedType | ||
| 12 | +{ | ||
| 13 | + /** | ||
| 14 | + * 姓名,第2位星号替换 | ||
| 15 | + */ | ||
| 16 | + USERNAME(s -> s.replaceAll("(\\S)\\S(\\S*)", "$1*$2")), | ||
| 17 | + | ||
| 18 | + /** | ||
| 19 | + * 密码,全部字符都用*代替 | ||
| 20 | + */ | ||
| 21 | + PASSWORD(DesensitizedUtil::password), | ||
| 22 | + | ||
| 23 | + /** | ||
| 24 | + * 身份证,中间10位星号替换 | ||
| 25 | + */ | ||
| 26 | + ID_CARD(s -> s.replaceAll("(\\d{4})\\d{10}(\\d{4})", "$1** **** ****$2")), | ||
| 27 | + | ||
| 28 | + /** | ||
| 29 | + * 手机号,中间4位星号替换 | ||
| 30 | + */ | ||
| 31 | + PHONE(s -> s.replaceAll("(\\d{3})\\d{4}(\\d{4})", "$1****$2")), | ||
| 32 | + | ||
| 33 | + /** | ||
| 34 | + * 电子邮箱,仅显示第一个字母和@后面的地址显示,其他星号替换 | ||
| 35 | + */ | ||
| 36 | + EMAIL(s -> s.replaceAll("(^.)[^@]*(@.*$)", "$1****$2")), | ||
| 37 | + | ||
| 38 | + /** | ||
| 39 | + * 银行卡号,保留最后4位,其他星号替换 | ||
| 40 | + */ | ||
| 41 | + BANK_CARD(s -> s.replaceAll("\\d{15}(\\d{3})", "**** **** **** **** $1")), | ||
| 42 | + | ||
| 43 | + /** | ||
| 44 | + * 车牌号码,包含普通车辆、新能源车辆 | ||
| 45 | + */ | ||
| 46 | + CAR_LICENSE(DesensitizedUtil::carLicense); | ||
| 47 | + | ||
| 48 | + private final Function<String, String> desensitizer; | ||
| 49 | + | ||
| 50 | + DesensitizedType(Function<String, String> desensitizer) | ||
| 51 | + { | ||
| 52 | + this.desensitizer = desensitizer; | ||
| 53 | + } | ||
| 54 | + | ||
| 55 | + public Function<String, String> desensitizer() | ||
| 56 | + { | ||
| 57 | + return desensitizer; | ||
| 58 | + } | ||
| 59 | +} |
| 1 | +package com.ruoyi.common.utils; | ||
| 2 | + | ||
| 3 | +/** | ||
| 4 | + * 脱敏工具类 | ||
| 5 | + * | ||
| 6 | + * @author ruoyi | ||
| 7 | + */ | ||
| 8 | +public class DesensitizedUtil | ||
| 9 | +{ | ||
| 10 | + /** | ||
| 11 | + * 密码的全部字符都用*代替,比如:****** | ||
| 12 | + * | ||
| 13 | + * @param password 密码 | ||
| 14 | + * @return 脱敏后的密码 | ||
| 15 | + */ | ||
| 16 | + public static String password(String password) | ||
| 17 | + { | ||
| 18 | + if (StringUtils.isBlank(password)) | ||
| 19 | + { | ||
| 20 | + return StringUtils.EMPTY; | ||
| 21 | + } | ||
| 22 | + return StringUtils.repeat('*', password.length()); | ||
| 23 | + } | ||
| 24 | + | ||
| 25 | + /** | ||
| 26 | + * 车牌中间用*代替,如果是错误的车牌,不处理 | ||
| 27 | + * | ||
| 28 | + * @param carLicense 完整的车牌号 | ||
| 29 | + * @return 脱敏后的车牌 | ||
| 30 | + */ | ||
| 31 | + public static String carLicense(String carLicense) | ||
| 32 | + { | ||
| 33 | + if (StringUtils.isBlank(carLicense)) | ||
| 34 | + { | ||
| 35 | + return StringUtils.EMPTY; | ||
| 36 | + } | ||
| 37 | + // 普通车牌 | ||
| 38 | + if (carLicense.length() == 7) | ||
| 39 | + { | ||
| 40 | + carLicense = StringUtils.hide(carLicense, 3, 6); | ||
| 41 | + } | ||
| 42 | + else if (carLicense.length() == 8) | ||
| 43 | + { | ||
| 44 | + // 新能源车牌 | ||
| 45 | + carLicense = StringUtils.hide(carLicense, 3, 7); | ||
| 46 | + } | ||
| 47 | + return carLicense; | ||
| 48 | + } | ||
| 49 | +} |
| @@ -23,6 +23,9 @@ public class StringUtils extends org.apache.commons.lang3.StringUtils | @@ -23,6 +23,9 @@ public class StringUtils extends org.apache.commons.lang3.StringUtils | ||
| 23 | /** 下划线 */ | 23 | /** 下划线 */ |
| 24 | private static final char SEPARATOR = '_'; | 24 | private static final char SEPARATOR = '_'; |
| 25 | 25 | ||
| 26 | + /** 星号 */ | ||
| 27 | + private static final char ASTERISK = '*'; | ||
| 28 | + | ||
| 26 | /** | 29 | /** |
| 27 | * 获取参数不为空值 | 30 | * 获取参数不为空值 |
| 28 | * | 31 | * |
| @@ -164,6 +167,49 @@ public class StringUtils extends org.apache.commons.lang3.StringUtils | @@ -164,6 +167,49 @@ public class StringUtils extends org.apache.commons.lang3.StringUtils | ||
| 164 | } | 167 | } |
| 165 | 168 | ||
| 166 | /** | 169 | /** |
| 170 | + * 替换指定字符串的指定区间内字符为"*" | ||
| 171 | + * | ||
| 172 | + * @param str 字符串 | ||
| 173 | + * @param startInclude 开始位置(包含) | ||
| 174 | + * @param endExclude 结束位置(不包含) | ||
| 175 | + * @return 替换后的字符串 | ||
| 176 | + */ | ||
| 177 | + public static String hide(CharSequence str, int startInclude, int endExclude) | ||
| 178 | + { | ||
| 179 | + if (isEmpty(str)) | ||
| 180 | + { | ||
| 181 | + return NULLSTR; | ||
| 182 | + } | ||
| 183 | + final int strLength = str.length(); | ||
| 184 | + if (startInclude > strLength) | ||
| 185 | + { | ||
| 186 | + return NULLSTR; | ||
| 187 | + } | ||
| 188 | + if (endExclude > strLength) | ||
| 189 | + { | ||
| 190 | + endExclude = strLength; | ||
| 191 | + } | ||
| 192 | + if (startInclude > endExclude) | ||
| 193 | + { | ||
| 194 | + // 如果起始位置大于结束位置,不替换 | ||
| 195 | + return NULLSTR; | ||
| 196 | + } | ||
| 197 | + final char[] chars = new char[strLength]; | ||
| 198 | + for (int i = 0; i < strLength; i++) | ||
| 199 | + { | ||
| 200 | + if (i >= startInclude && i < endExclude) | ||
| 201 | + { | ||
| 202 | + chars[i] = ASTERISK; | ||
| 203 | + } | ||
| 204 | + else | ||
| 205 | + { | ||
| 206 | + chars[i] = str.charAt(i); | ||
| 207 | + } | ||
| 208 | + } | ||
| 209 | + return new String(chars); | ||
| 210 | + } | ||
| 211 | + | ||
| 212 | + /** | ||
| 167 | * 截取字符串 | 213 | * 截取字符串 |
| 168 | * | 214 | * |
| 169 | * @param str 字符串 | 215 | * @param str 字符串 |
-
请 注册 或 登录 后发表评论