spring boot自定义注解 对返回隐私字段进行模糊化处理

需求背景:部分数据涉及用户隐私,需要进行模糊化处理,如“”张三“, 模糊化处理后为 张*。

实现方式:

1.硬编码(太Low)

2.拦截器(对参数名称等有严格的要求,代码易用性差)

3.sql解析(性能慢,对sql和字段名有严格要求)

4.自定义注解(可以,为什么可以,请看后文)

为什么使用自定义注解的方式:

 

思路分析:

1.使用自定义注解+aop方式,实现使用了自定义注解的方法的拦截。

2.对方法体的返回值进行模糊化处理

3.模糊化处理肯定要知道模糊化的属性,故需要模糊化的属性也需要使用该注解

4.如果是对象嵌套的话还需要解决嵌套对象中有该注解的情况

5.肯定不是所有类型都可以处理(复杂对象的本质必须是 List<POJO>,POJO)

如果解决了上述问题,好像解决这个问题了呢?

上代码:
1.自定义注解类:

package com.test.common.annotation;

import org.springframework.context.annotation.Import;

import javax.annotation.Resource;
import java.lang.annotation.Documented;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;

import static java.lang.annotation.ElementType.*;
import static java.lang.annotation.ElementType.PARAMETER;
import static java.lang.annotation.RetentionPolicy.RUNTIME;

@Target({ METHOD, FIELD, CONSTRUCTOR, PARAMETER,TYPE})
@Retention(RUNTIME)
@Documented
@Inherited
public @interface Vague {
    String type();
}

2.aop方式的注解处理类

package com.keyou.common.annotation;


import com.github.pagehelper.Page;
import com.github.pagehelper.PageInfo;
import com.test.course.controller.NewCourseController;
import com.test.course.domain.po.NewCourse;
import com.test.util.Result;
import lombok.Data;
import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.hibernate.validator.constraints.ModCheck;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

import java.lang.reflect.AnnotatedType;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;

@Aspect
@Component
public class VagueResolver  {
    final static Logger log = LoggerFactory.getLogger(VagueResolver.class);
    private long beginTime=0;


    @Pointcut("@annotation(com.keyou.common.annotation.Vague)")
    public void serviceStatistics() {

    }

    @Before("serviceStatistics()")
    public void doBefore(JoinPoint joinPoint) {
        beginTime=System.currentTimeMillis();
    }

    /**
     * 自定义注解处理逻辑
     * 1.通过aop方式拦截自定义注解方法
     * 2.获取到方法返回值
     * 3.判断方法返回值类型
     * 4.对返回值类型进行判断,是否为list<pojo>和pojo
     * 5.将list中pojo或者pojo的属性进行扫描,判断是否使用了自定义注解
     * 6.如果属性使用了注解,根据注解属性值 type 进行相应的处理
     * 7.将修改后的值通过反射的方式放入List<pojo>或者Pojo中,达到修改方法返回值的目的
     * @param joinPoint
     * @param value
     */
    @AfterReturning(value = "@annotation(com.test.common.annotation.Vague)",returning ="value")
    public void doAfter(JoinPoint joinPoint,Object value) {
        log.info("----数据模糊处理开始----");
        value=this.dataHandle(value);
        log.info("----数据模糊处理结束----");
    }

    /**
     * 数据处理
     * 逻辑说明:查询结果只可在pojo中,目前常用返回值类型为pojo,list<pojo> Page,PageInfo Result等
     * 最终实际上都是为pojo,此处后续可以扩展到其余类型
     * 通过一层一层剥离的方式得到pojo进行处理
     * 以Page为例,进入handListType方法后,handListType进行了遍历然后调用dataHandle方法,目的就是让最终dataHandle的入参是pojo
     * 所有后续又新返回类型,可参考一下方式实现
     * @param value
     * @return
     */
    public Object dataHandle(Object value){
        try {
            if (value instanceof com.github.pagehelper.Page) {
                return this.handListType((List<Object>) value);
            }
            else if(value instanceof ArrayList){
                return this.handListType((List<Object>) value);
            }
            else if(value instanceof PageInfo){
                PageInfo pageInfo= (PageInfo) value;
                return this.dataHandle(pageInfo.getList());
            }
            else if(value instanceof List){
                return this.handListType((List<Object>) value);
            }
            else if(value instanceof Result){
                Result result= (Result) value;
                return this.dataHandle(result.getData());
            }
            else if(value.getClass().getTypeName().indexOf("com.keyou")!=-1){
                return this.handPojoType(value);
            }
            else{
                return value;
            }
        }catch (Exception e){
            return value;
        }
    }

    /**
     * 通过反射模糊化处理pojo属性,如果属性是list,继续向下处理,递归逻辑
     * @param object
     * @return
     */
    public Object handPojoType(Object object){
        if (object != null) {
            Class clz =object.getClass();
            // 获取到对象所有属性,并且遍历
            Field[] fields = clz.getDeclaredFields();
            for (Field field : fields) {
                String classType=field.getType().getTypeName();
                boolean fieldHasAnno = field.isAnnotationPresent(Vague.class);
                //判断属性上是否有注解,如果有进入逻辑,如果没有,返回对象
                if (fieldHasAnno) {
                    //如果属性是String 模糊化他(模糊化处理只能处理String了,不要问为什么)
                    if(classType.equals("java.lang.String")){
                        Vague vague=field.getAnnotation(Vague.class);
                        String type=vague.type();
                        object=this.handleValue(field,object,type);
                    }
                    //这儿就相当于递归处理了,处理对象嵌套对象的方式的模糊化
                    //如果不是,获取到他的值,继续走dataHandle(为什么又要走dataHandle?因为万一他又是List<POJO>这些呢?)
                    else{
                        Class fieldClass =object.getClass();
                        String name=this.firstUpperCase(field.getName());
                        field.setAccessible(true);// 设置操作权限为true
                        Method getMethod= null;
                        try {
                            getMethod = fieldClass.getMethod("get"+name);
                            Object value=getMethod.invoke(object);
                            if(value!=null){
                                value=this.dataHandle(value);
                                Method setMethod=fieldClass.getMethod("set"+name,field.getType());
                                setMethod.invoke(object,value);
                            }
                        } catch (Exception e) {
                            e.printStackTrace();
                        }
                    }
                }
            }
        }
        return object;
    }
    public Object handListType(List<Object> page){
        if (page != null && !page.isEmpty()) {
           for(int i=0;i<page.size();i++){
               page.set(i, this.dataHandle(page.get(i)));
           }
        }
        return page;
    }
    public Object handPageType(Page page){
        if (page != null && !page.isEmpty()) {
            Object object = page.get(0);
            Class clz =object.getClass();
            // 判断类上是否有次注解
            Field[] fields = clz.getDeclaredFields();
            for (Field field : fields) {
                boolean fieldHasAnno = field.isAnnotationPresent(Vague.class);
                if (fieldHasAnno) {
                    Vague vague=field.getAnnotation(Vague.class);
                    String type=vague.type();
                    for(int i=0;i<page.size();i++){
                        Object o=page.get(i);
                        o=this.handleValue(field,o,type);
                        page.set(i,o);
                    }
                }
            }
        }
        return page;
    }
    public Object handleValue(Field field,Object object,String type){
        try {
            Class clz =object.getClass();
            String name=this.firstUpperCase(field.getName());
            field.setAccessible(true);// 设置操作权限为true
            Method getMethod=clz.getMethod("get"+name);
            Object value=getMethod.invoke(object);
            Method setMethod=clz.getMethod("set"+name,field.getType());
            value=this.handleValue(value,type);
            setMethod.invoke(object,value);
            return object;
        }catch (Exception e){
            e.printStackTrace();
            return object;
        }
    }
    public Object handleValue(Object object,String type){
        switch (type){
            case Constants.NAME:
                return handleNAME(object);
            case Constants.ID_CODE:
                return handleIDCODE(object);
            case Constants.BANK_CODE:
                return handleBANK_CODE(object);
            case Constants.ADDRESS:
                return handleNAME(object);
            case Constants.PHONE_NO:
                return handlePHONE_NO(object);
        }
        return object;
    }
    public Object handlePHONE_NO(Object object) {
        if(object!=null){
            String phone_no=object.toString();
            if(phone_no.length()==11){
                String phoneNumber = phone_no.substring(0, 3) + "****" + phone_no.substring(7, phone_no.length());
                return phoneNumber;
            }
        }
        return object;
    }
    /**
     * 银行卡替换,保留后四位
     *
     * 如果银行卡号为空 或者 null ,返回null ;否则,返回替换后的字符串;
     *
     * @param object 银行卡号
     * @return
     */
    public Object handleBANK_CODE(Object object) {
        if(object!=null){
            String bankCard=object.toString();
            if (bankCard.isEmpty() || bankCard == null) {
                return null;
            } else {
                return bankCard.substring(0,4)+"**********"+bankCard.substring(bankCard.length()-4,bankCard.length());
            }
        }
        return object;
    }
    /**
     * 身份证号替换,保留前四位和后四位
     *
     * 如果身份证号为空 或者 null ,返回null ;否则,返回替换后的字符串;
     *
     * @param object 身份证号
     * @return
     */
    public Object handleIDCODE(Object object) {
        if(object!=null){
            String idCode=object.toString();
            if (idCode.isEmpty() || idCode == null) {
                return object;
            } else {
                return idCode.substring(0,4)+"**********"+idCode.substring(idCode.length()-4,idCode.length());
            }
        }
        return object;
    }

    public Object handleNAME(Object object){
        if(object!=null){
            String userName=object.toString();
            int length=object.toString().length();
            if (length <= 1) {
                return "*";
            } else if (length == 2) {
                return userName.substring(0, 1) + "*";
            }
            else if (length == 3) {
                return userName.substring(0, 1) + "**";
            }
            else{
                StringBuffer sb=new StringBuffer();
                sb.append(userName.substring(0, 2));
                for(int i=0;i<length-2;i++){
                    sb.append("*");
                }
                return sb.toString();
            }
        }
        return object;
    }
    public String firstUpperCase(String str){
        return StringUtils.replaceChars(str, str.substring(0, 1),str.substring(0, 1).toUpperCase());
    }
}

value的意思,拦截使用了Vague注解的方法,returning获取到该方法的返回值

调用dataHandle方法对数据进行模糊化处理

重点说下dataHandle的逻辑:

我自己的项目中使用了mybatis的分页对象和自定义了Result类型,所以我兼容了他们

不论是什么类型,最终都是POJO,所以在handListType方法中,你可以看到我是循环了list调用dataHandle,所以得到Pojo肯定是固定包名的,最终干货是最最后一个else if的逻辑即handPojoType(好像只能说这么明白。。。)

使用:

Pom.xml加入

<dependency>             

          <groupId>org.springframework.boot</groupId>  

          <artifactId>spring-boot-starter-aop</artifactId>  

</dependency>  

启动类上加入注解:

@EnableAspectJAutoProxy

在返回值需要模糊化处理的方法上加上自定义注解:

type是表示模糊化什么,因为模糊化手机号,名字的方式各有不同,所以增加了一个常量类,方法体上type无效,属性上才有效。

在需要处理的实体的属性上加入注解即可

 

 


文章写得不够清晰,凑合看吧---------------------

或者看源代码更明了,源代码:

package com.keyou.common.annotation;

import org.springframework.context.annotation.Import;

import javax.annotation.Resource;
import java.lang.annotation.Documented;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;

import static java.lang.annotation.ElementType.*;
import static java.lang.annotation.ElementType.PARAMETER;
import static java.lang.annotation.RetentionPolicy.RUNTIME;

@Target({ METHOD, FIELD, CONSTRUCTOR, PARAMETER,TYPE})
@Retention(RUNTIME)
@Documented
@Inherited
public @interface Vague {
    String type();
   
}
package com.keyou.common.annotation;


import com.github.pagehelper.Page;
import com.github.pagehelper.PageInfo;
import com.keyou.course.controller.NewCourseController;
import com.keyou.course.domain.po.NewCourse;
import com.keyou.util.Result;
import lombok.Data;
import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.hibernate.validator.constraints.ModCheck;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

import java.lang.reflect.AnnotatedType;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;

@Aspect
@Component
public class VagueResolver  {
    final static Logger log = LoggerFactory.getLogger(VagueResolver.class);
    private long beginTime=0;


    @Pointcut("@annotation(com.keyou.common.annotation.Vague)")
    public void serviceStatistics() {

    }

    @Before("serviceStatistics()")
    public void doBefore(JoinPoint joinPoint) {
        beginTime=System.currentTimeMillis();
    }

    /**
     * 自定义注解处理逻辑
     * 1.通过aop方式拦截自定义注解方法
     * 2.获取到方法返回值
     * 3.判断方法返回值类型
     * 4.对返回值类型进行判断,是否为list<pojo>和pojo
     * 5.将list中pojo或者pojo的属性进行扫描,判断是否使用了自定义注解
     * 6.如果属性使用了注解,根据注解属性值 type 进行相应的处理
     * 7.将修改后的值通过反射的方式放入List<pojo>或者Pojo中,达到修改方法返回值的目的
     * @param joinPoint
     * @param value
     */
    @AfterReturning(value = "@annotation(com.test.common.annotation.Vague)",returning ="value")
    public void doAfter(JoinPoint joinPoint,Object value) {
        log.info("----数据模糊处理开始----");
        value=this.dataHandle(value);
        log.info("----数据模糊处理结束----");
    }

    /**
     * 数据处理
     * 逻辑说明:查询结果只可在pojo中,目前常用返回值类型为pojo,list<pojo> Page,PageInfo Result等
     * 最终实际上都是为pojo,此处后续可以扩展到其余类型
     * 通过一层一层剥离的方式得到pojo进行处理
     * 以Page为例,进入handListType方法后,handListType进行了遍历然后调用dataHandle方法,目的就是让最终dataHandle的入参是pojo
     * 所有后续又新返回类型,可参考一下方式实现
     * @param value
     * @return
     */
    public Object dataHandle(Object value){
        try {
            if (value instanceof com.github.pagehelper.Page) {
                return this.handListType((List<Object>) value);
            }
            else if(value instanceof ArrayList){
                return this.handListType((List<Object>) value);
            }
            else if(value instanceof PageInfo){
                PageInfo pageInfo= (PageInfo) value;
                return this.dataHandle(pageInfo.getList());
            }
            else if(value instanceof List){
                return this.handListType((List<Object>) value);
            }
            else if(value instanceof Result){
                Result result= (Result) value;
                return this.dataHandle(result.getData());
            }
            else if(value.getClass().getTypeName().indexOf("com.keyou")!=-1){
                return this.handPojoType(value);
            }
            else{
                return value;
            }
        }catch (Exception e){
            return value;
        }
    }

    /**
     * 通过反射模糊化处理pojo属性,如果属性是list,继续向下处理,递归逻辑
     * @param object
     * @return
     */
    public Object handPojoType(Object object){
        if (object != null) {
            Class clz =object.getClass();
            // 获取到对象所有属性,并且遍历
            Field[] fields = clz.getDeclaredFields();
            for (Field field : fields) {
                String classType=field.getType().getTypeName();
                boolean fieldHasAnno = field.isAnnotationPresent(Vague.class);
                //判断属性上是否有注解,如果有进入逻辑,如果没有,返回对象
                if (fieldHasAnno) {
                    //如果属性是String 模糊化他(模糊化处理只能处理String了,不要问为什么)
                    if(classType.equals("java.lang.String")){
                        Vague vague=field.getAnnotation(Vague.class);
                        String type=vague.type();
                        object=this.handleValue(field,object,type);
                    }
                    //这儿就相当于递归处理了,处理对象嵌套对象的方式的模糊化
                    //如果不是,获取到他的值,继续走dataHandle(为什么又要走dataHandle?因为万一他又是List<POJO>这些呢?)
                    else{
                        Class fieldClass =object.getClass();
                        String name=this.firstUpperCase(field.getName());
                        field.setAccessible(true);// 设置操作权限为true
                        Method getMethod= null;
                        try {
                            getMethod = fieldClass.getMethod("get"+name);
                            Object value=getMethod.invoke(object);
                            if(value!=null){
                                value=this.dataHandle(value);
                                Method setMethod=fieldClass.getMethod("set"+name,field.getType());
                                setMethod.invoke(object,value);
                            }
                        } catch (Exception e) {
                            e.printStackTrace();
                        }
                    }
                }
            }
        }
        return object;
    }
    public Object handListType(List<Object> page){
        if (page != null && !page.isEmpty()) {
           for(int i=0;i<page.size();i++){
               page.set(i, this.dataHandle(page.get(i)));
           }
        }
        return page;
    }
    public Object handPageType(Page page){
        if (page != null && !page.isEmpty()) {
            Object object = page.get(0);
            Class clz =object.getClass();
            // 判断类上是否有次注解
            Field[] fields = clz.getDeclaredFields();
            for (Field field : fields) {
                boolean fieldHasAnno = field.isAnnotationPresent(Vague.class);
                if (fieldHasAnno) {
                    Vague vague=field.getAnnotation(Vague.class);
                    String type=vague.type();
                    for(int i=0;i<page.size();i++){
                        Object o=page.get(i);
                        o=this.handleValue(field,o,type);
                        page.set(i,o);
                    }
                }
            }
        }
        return page;
    }
    public Object handleValue(Field field,Object object,String type){
        try {
            Class clz =object.getClass();
            String name=this.firstUpperCase(field.getName());
            field.setAccessible(true);// 设置操作权限为true
            Method getMethod=clz.getMethod("get"+name);
            Object value=getMethod.invoke(object);
            Method setMethod=clz.getMethod("set"+name,field.getType());
            value=this.handleValue(value,type);
            setMethod.invoke(object,value);
            return object;
        }catch (Exception e){
            e.printStackTrace();
            return object;
        }
    }
    public Object handleValue(Object object,String type){
        switch (type){
            case Constants.NAME:
                return handleNAME(object);
            case Constants.ID_CODE:
                return handleIDCODE(object);
            case Constants.BANK_CODE:
                return handleBANK_CODE(object);
            case Constants.ADDRESS:
                return handleNAME(object);
            case Constants.PHONE_NO:
                return handlePHONE_NO(object);
        }
        return object;
    }
    public Object handlePHONE_NO(Object object) {
        if(object!=null){
            String phone_no=object.toString();
            if(phone_no.length()==11){
                String phoneNumber = phone_no.substring(0, 3) + "****" + phone_no.substring(7, phone_no.length());
                return phoneNumber;
            }
        }
        return object;
    }
    /**
     * 银行卡替换,保留后四位
     *
     * 如果银行卡号为空 或者 null ,返回null ;否则,返回替换后的字符串;
     *
     * @param object 银行卡号
     * @return
     */
    public Object handleBANK_CODE(Object object) {
        if(object!=null){
            String bankCard=object.toString();
            if (bankCard.isEmpty() || bankCard == null) {
                return null;
            } else {
                return bankCard.substring(0,4)+"**********"+bankCard.substring(bankCard.length()-4,bankCard.length());
            }
        }
        return object;
    }
    /**
     * 身份证号替换,保留前四位和后四位
     *
     * 如果身份证号为空 或者 null ,返回null ;否则,返回替换后的字符串;
     *
     * @param object 身份证号
     * @return
     */
    public Object handleIDCODE(Object object) {
        if(object!=null){
            String idCode=object.toString();
            if (idCode.isEmpty() || idCode == null) {
                return object;
            } else {
                return idCode.substring(0,4)+"**********"+idCode.substring(idCode.length()-4,idCode.length());
            }
        }
        return object;
    }

    public Object handleNAME(Object object){
        if(object!=null){
            String userName=object.toString();
            int length=object.toString().length();
            if (length <= 1) {
                return "*";
            } else if (length == 2) {
                return userName.substring(0, 1) + "*";
            }
            else if (length == 3) {
                return userName.substring(0, 1) + "**";
            }
            else{
                StringBuffer sb=new StringBuffer();
                sb.append(userName.substring(0, 2));
                for(int i=0;i<length-2;i++){
                    sb.append("*");
                }
                return sb.toString();
            }
        }
        return object;
    }
    public String firstUpperCase(String str){
        return StringUtils.replaceChars(str, str.substring(0, 1),str.substring(0, 1).toUpperCase());
    }
}

package com.keyou.common.annotation;

public class Constants {
    public static final String NAME = "NAME";
    public static final String ID_CODE = "ID_CODE";
    public static final String PHONE_NO = "PHONE_NO";
    public static final String ADDRESS = "ADDRESS";
    public static final String BANK_CODE = "BANK_CODE";

}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值