SpringBoot 自定义注解之脱敏注解详解

网友投稿 865 2022-11-14

SpringBoot 自定义注解之脱敏注解详解

SpringBoot 自定义注解之脱敏注解详解

目录自定义注解之脱敏注解一、脱敏后的效果二、代码1.脱敏注解2.定义脱敏类型3.敏感工具类4.脱敏序列化信息小结一下自己手写的一个高效自定义字符串脱敏注解自己写了个 仅供参考

自定义注解之脱敏注解

数据脱敏是指对某些敏感信息通过脱敏规则进行数据的变形,实现敏感隐私数据的可靠保护。需求是把返回到前端的数据进行脱敏,以免造成隐私信息的泄露。

一、脱敏后的效果

这样显示很不好吧,所有信息都泄露了

这样就很好了吧

二、代码

1.脱敏注解

@Retention(RetentionPolicy.RUNTIME)

@Target(ElementType.FIELD)

@JacksonAnnotationsInside

@jsonSerialize(using = SensitiveSerialize.class)

public @interface Sensitive {

/**

* 脱敏数据类型

*/

SensitiveTypeEnum type() default SensitiveTypeEnum.CUSTOMER;

/**mVmBgDRW

* 前置不需要打码的长度

*/

int prefixNoMaskLen() default 0;

/**

* 后置不需要打码的长度

*/

int suffixNoMaskLen() default 0;

/**

* 用什么打码

*/

String symbol() default "*";

}

2.定义脱敏类型

public enum SensitiveTypeEnum {

/**

* 自定义

*/

CUSTOMER,

/**

* 姓名

*/

NAME,

/**

* 身份证

*/

ID_NUM,

/**

* 手机号码

*/

PHONE_NUM

}

3.敏感工具类

public class DesensitizedUtils {

/**

* 对字符串进行脱敏操作

*

* @param origin 原始字符串

* @param prefixNoMaskLen 左侧需要保留几位明文字段

* @param suffixNoMaskLen 右侧需要保留几位明文字段

* @param maskStr 用于遮罩的字符串, 如'*'

* @return 脱敏后结果

*/

public static String desValue(String origin, int prefixNoMaskLen, int suffixNoMaskLen, String maskStr) {

if (origin == null) {

return null;

}

StringBuilder sb = new StringBuilder();

for (int i = 0, n = origin.length(); i < n; i++) {

if (i < prefixNoMaskLen) {

sb.append(origin.charAt(i));

continue;

}

if (i > (n - suffixNoMaskLen - 1)) {

sb.append(origin.charAt(i));

continue;

}

sb.append(maskStr);

}

return sb.toString();

}

/**

* 【中文姓名】只显示最后一个汉字,其他隐藏为星号,比如:**梦

*

* @param fullName 姓名

* @return 结果

*/

public static String chineseName(String fullName) {

if (fullName == null) {

return null;

}

return desValue(fullName, 1, 0, "*");

}

/**

* 【身份证号】显示前4位, 后2位,其他隐藏。

*

* @param id 身份证号码

* @return 结果

*/

public static String idCardNum(String id) {

return desValue(id, 4, 2, "*");

}

/**

* 【手机号码】前三位,后四位,其他隐藏。

*

* @param num 手机号码

* @return 结果

*/

public static String mobilePhone(String num) {

return desValue(num, 3, 4, "*");

}

}

4.脱敏序列化信息

@NoArgsConstructor

@AllArgsConstructor

public class SensitiveSerialize extends JsonSerializer implements ContextualSerializer {

/**

* 脱敏类型

*/

private SensitiveTypeEnum sensitiveTypeEnum;

/**

* 前几位不脱敏

*/

private Integer prefixNoMaskLen;

/**

* 最后几位不脱敏

*/

private Integer suffixNoMaskLen;

/**

* 用什么打码

*/

private String symbol;

@Override

public void serialize(final String origin, final JsonGenerator jsonGenerator,

final SerializerProvider serializerProvider) throws IOException {

switch (sensitiveTypeEnum) {

case CUSTOMER:

jsonGenerator.writeString(DesensitizedUtils.desValue(origin, prefixNoMaskLen, suffixNoMaskLen, symbol));

break;

case NAME:

jsonGenerator.writeString(DesensitizedUtils.chineseName(origin));

break;

case ID_NUM:

jsonGenerator.writeString(DesensitizedUtils.idCardNum(origin));

break;

case PHONE_NUM:

jsonGenerator.writeString(DesensitizedUtils.mobilePhone(origin));

break;

default:

throw new IllegalArgumentException("unknown sensitive type enum " + sensitiveTypeEnum);

}

}

@Override

public JsonSerializer> createContextual(final SerializerProvider serializerProvider,

final BeanProperty beanProperty) throws JsonMappingException {

if (beanProperty != null) {

if (Objects.equals(beanProperty.getType().getRawClass(), String.class)) {

Sensitive sensitive = beanProperty.getAnnotation(Sensitive.class);

if (sensitive == null) {

sensitive = beanProperty.getContextAnnotation(Sensitive.class);

}

if (sensitive != null) {

return new SensitiveSerialize(sensitive.type(), sensitive.prefixNoMaskLen(),

sensitive.suffixNoMaskLen(), sensitive.symbol());

}

}

return serializerProvider.findValueSerializer(beanProperty.getType(), beanProperty);

}

return serializerProvider.findNullValueSerializer(null);

}

}

小结一下

该注解用于隐私数据的脱敏,只作用于类的属性上。该注解有四个属性,type表示脱敏数据类型(默认为CUSTOMER自定义,后面三个属性才有效),prefixNoMaskLen表示前置不需要打码的长度(默认为0),suffixNoMaskLen表示后置不需要打码的长度(默认为0),symbol表示用什么打码(默认为*)。

一般用于返回对象给前端对象中包含隐私数据mVmBgDRW例如身份证、详细地址需要进行脱敏的情况。

示例:

public class UserInfo {

@Sensitive(type = SensitiveTypeEnum.NAME)

private String name;

@Sensitive(type = SensitiveTypeEnum.ID_NUM)

private String idNum;

@Sensitive(type = SensitiveTypeEnum.PHONE_NUM)

private String phone;

@Sensitive(type = SensitiveTypeEnum.CUSTOMER, prefixNoMaskLen = 3, suffixNoMaskLen = 2, symbol = "#")

private String address;

@Sensitive(prefixNoMaskLen = 1, suffixNoMaskLen = 2, symbol = "*")

private String password;

}

如果还有疑问我写了个demo,可以-下来运行看看

链接: 脱敏注解demo.

自己手写的一个高效自定义字符串脱敏注解

经理要求写一个自定义脱敏注解,百度查了一堆。都是效率比较低的

自己写了个 仅供参考

/**

* description: 数据脱敏

* 1、默认不传部位、不http://传显示*号数量时字段全部脱敏

*

* 原始字符串 adminis 总长度从0计算 总数6

* index=(0,2) size = 1 下标即从0到2以内的字符标注“ * ”,size=1 则只填充一个* size 不能超过截取字符

* index=(2,3) size =2 下标即从2到3以内的字符标注“ * ”,size=2 则只填充二个* size 不能超过截取字符

*

* date: 2020/3/13 15:56

*

* @author oakdog

* @version 1.0

*/

@Target({ElementType.FIELD})

@Retention(RetentionPolicy.RUNTIME)

@JacksonAnnotationsInside

@JsonSerialize(using = Desensitization.ConvertDesensitization.class)

public @interface Desensitization {

/**

* 传入的下标索引

* 规则 第一位起始下标 第二位是结束下标 默认值6位下标

**/

int[] index() default {0,6};

/**

* 需要脱敏的字符长度

* 规则 输入 3 :则根据index下标索引对应脱敏3个字符 默认6个长度脱敏

**/

int size() default 6;

class ConvertDesensitization extends StdSerializer implements ContextualSerializer {

private int[] index;

private int size;

public ConvertDesensitization() {

super(Object.class);

}

private ConvertDesensitization(int[] index,int size) {

super(Object.class);

this.size = size;

this.index = index;

}

@Override

public void serialize(Object value, JsonGenerator jgen,

SerializerProvider provider) throws IOException {

char[] str = value.toString().toCharArray();

StringBuilder builder = new StringBuilder();

String char1 = (String) value;

if(str.length > 0) {

//字符长度超长处理

if(index[0] < str.length && index[1] < str.length) {

//使用默认初始值的脱敏处理

if(index[0] == 0) {

//如果输入脱敏大小长度小于0或大于原始脱敏字符长度,则全脱敏字符

if (size < 0 || size < str.length) {

char[] charStr = char1.substring(index[1], str.length).toCharArray();

char[] charStr1 = char1.substring(index[0], index[1]).toCharArray();

builder.append(charStr1);

for (int i = 0; i < charStr.length; i++) {

if(size > i) {

builder.append("*");

}else {

builder.append(charStr[i]);

}

}

}else {

builder.append(getDefaultChar((String) value,"left"));

}

}else {

//从中间位置截取脱敏处理

//如果输入脱敏大小长度小于0或大于原始脱敏字符长度,则全脱敏字符

if (size < 0 || size < str.length) {

char[] charStr = char1.substring(index[0], str.length - index[1] + 1).toCharArray(); //2 6-4 2 //中间截取部分

List prefix = getPrefix(index[0], (String) value);

//List suffix = getSuffix(index[0],index[1], (String) value);

for (Integer integer : prefix) {

builder.append(str[integer]);

}

for (int i = 0; i < charStr.length; i++) {

if (size > i) {

builder.append("*");

} else {

builder.append(charStr[i]);

}

}

char[] chars = Arrays.copyOfRange(str, index[1], str.length);

builder.append(String.valueOf(chars));

}else {

builder.append(getDefaultChar((String) value,"right"));

}

}

}else {

//默认处理

builder.append(getDefaultChar((String) value,""));

}

}

jgen.writeString(builder.toString());

}

/**

* 默认的填充方式

* @param str 原始字符串

* @param position 位置

* @return

*/

String getDefaultChar(String str,String position){

char[] desensitizationStr = str.toCharArray();

for(int i=0;i

if("left".equals(position)){

if(i != 0){

desensitizationStr[i] = '*';

}

}else if("right".equals(position)){

if(i != desensitizationStr.length-1){

desensitizationStr[i] = '*';

}

}else {

if(i != 0 && i != desensitizationStr.length-1){

desensitizationStr[i] = '*';

}

}

}

return String.valueOf(desensitizationStr);

}

/**

* 获取字符前缀下标

* @param index 下标

* @param val 原始字符串

* @return

*/

List getPrefix(int index,String val){

//int[] chars = {};

List listIndex = new ArrayList<>();

for(int i=0;i

if(i != index){ //0 1 != 2

listIndex.add(i);

continue;

}

break;

}

return listIndex;

}

@Override

public JsonSerializer> createContextual(SerializerProvider prov, BeanProperty property) {

int[] index = {0,6}; //初始值

int size = 6; //初始值

Desensitization ann = null;

if (property != null) {

ann = property.getAnnotation(Desensitization.class);

}

if (ann != null) {

index = ann.index();

size = ann.size();

}

return nehttp://w Desensitization.ConvertDesensitization(index,size);

}

}

}

if("left".equals(position)){

if(i != 0){

desensitizationStr[i] = '*';

}

}else if("right".equals(position)){

if(i != desensitizationStr.length-1){

desensitizationStr[i] = '*';

}

}else {

if(i != 0 && i != desensitizationStr.length-1){

desensitizationStr[i] = '*';

}

}

}

return String.valueOf(desensitizationStr);

}

/**

* 获取字符前缀下标

* @param index 下标

* @param val 原始字符串

* @return

*/

List getPrefix(int index,String val){

//int[] chars = {};

List listIndex = new ArrayList<>();

for(int i=0;i

if(i != index){ //0 1 != 2

listIndex.add(i);

continue;

}

break;

}

return listIndex;

}

@Override

public JsonSerializer> createContextual(SerializerProvider prov, BeanProperty property) {

int[] index = {0,6}; //初始值

int size = 6; //初始值

Desensitization ann = null;

if (property != null) {

ann = property.getAnnotation(Desensitization.class);

}

if (ann != null) {

index = ann.index();

size = ann.size();

}

return nehttp://w Desensitization.ConvertDesensitization(index,size);

}

}

}

if(i != index){ //0 1 != 2

listIndex.add(i);

continue;

}

break;

}

return listIndex;

}

@Override

public JsonSerializer> createContextual(SerializerProvider prov, BeanProperty property) {

int[] index = {0,6}; //初始值

int size = 6; //初始值

Desensitization ann = null;

if (property != null) {

ann = property.getAnnotation(Desensitization.class);

}

if (ann != null) {

index = ann.index();

size = ann.size();

}

return nehttp://w Desensitization.ConvertDesensitization(index,size);

}

}

}

版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:深入浅出接口幂等性的实现方式
下一篇:log4j的8个日志级别(OFF、FATAL、ERROR、WARN、INFO、DEBUG、TRACE、 ALL)
相关文章

 发表评论

暂时没有评论,来抢沙发吧~