spring切面注解失效

在项目中使用切面注解做数据脱敏时,导出的数据也需要脱敏处理,遇到了在一个类里面调用本类的方法切面失效,解决方法如下:

切面注解:

package com.t3.ts.driver.resume.aspect;

import java.lang.annotation.*;

/**
 * @Description: 数据脱敏注解 Filed
 * @Date: 2019/9/10
 * @Author: wm yu
 */
@Inherited
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface EncryptField {
}
package com.t3.ts.driver.resume.aspect;

import java.lang.annotation.*;

/**
 * @Description: 数据脱敏注解 Method
 * @Date: 2019/9/10
 * @Author: wm yu
 */
@Inherited
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface EncryptMethod {
}

切面类:

package com.t3.ts.driver.resume.aspect;

import com.alibaba.fastjson.JSON;
import com.t3.ts.driver.resume.utils.MD5Util;
import com.t3.ts.driver.resume.utils.StringUtils;
import com.t3.ts.driver.resume.utils.excel.FieldReflectionUtil;
import com.t3.ts.result.PageResult;
import com.t3.ts.result.Response;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

import java.lang.reflect.Field;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;

/**
 * @class_name: DecryptAspect
 * @description:
 * @author: wm_yu
 * @create: 2019/09/10
 **/
@Aspect
@Component
@Order(-1)
public class EncryptAspect {

    private final static Logger log = LoggerFactory.getLogger(EncryptAspect.class);
    /**
     * 身份证脱敏正则 保留前后四位
     */
    private final static String IDENTITY_CARD_DESENSITIZATION = "(?<=\\d{4})\\d(?=\\d{4})";
    /**
     *银行卡脱敏正则  保留前后四位
     */
    private final static String BLANK_CARD_DESENSITIZATION = "(?<=\\d{4})\\d(?=\\d{4})";
    /**
     * 手机号脱敏正则 保留前三后四位
     */
    private final static String MOBILE_DESENSITIZATION = "(?<=\\d{3})\\d(?=\\d{4})";
    /**
     * 定义其他字段脱敏长度
     */
    private final static Integer OTHER_DESENSITIZATION_LENGTH = 3;

    private final static Integer IDENTITY_CARD_LENGTH_18 = 18;
    private final static Integer IDENTITY_CARD_LENGTH_15 = 15;
    private final static Integer MOBILE_LENGTH = 11;

    @Pointcut("@annotation(com.t3.ts.driver.resume.aspect.EncryptMethod)")
    public void pointCut(){}

    /**
     * 注明切点
     */
    @Around("pointCut()")
    public Object around(ProceedingJoinPoint joinPoint){
        Object responseObj = null;
        try {
            responseObj = joinPoint.proceed();
             //数据脱敏
            handleEncrypt(responseObj);
        } catch (Throwable throwable) {
           log.error("数据脱敏异常:{}", JSON.toJSONString(responseObj),throwable);
        }
        return responseObj;
    }

    /**
     * 处理加密
     *
     * @param responseObj
     */
    private void handleEncrypt(Object responseObj) throws IllegalAccessException {
        if (!Optional.ofNullable(responseObj).isPresent()) {
            return;
        }
        Object var = null;
        if(responseObj instanceof List){
            var = responseObj;
        }else{
            Response response = (Response) responseObj;
             var = response.getData();
        }
        if(!Optional.ofNullable(var).isPresent()){
            return;
        }
        this.dealDateByType(var);
    }

    /**
     * 类型判断处理
     * @param var
     * @throws IllegalAccessException
     */
    private void dealDateByType(Object var) throws IllegalAccessException {
        Field[] fields = {};
        if(var instanceof PageResult){
            //分页列表数据
            PageResult pageResult = (PageResult) var;
            List list = pageResult.getList();
            List filterList = (List) list.stream().filter(x -> Optional.ofNullable(x).isPresent()).collect(Collectors.toList());
            for (Object o : filterList) {
                fields = FieldReflectionUtil.getAllFields(o.getClass());
                dealPrecisionField(fields,o);
            }
        }
        if(var instanceof List){
            List list = (List) var;
            List filterList = (List) list.stream().filter(x -> Optional.ofNullable(x).isPresent()).collect(Collectors.toList());
            for (Object o : filterList) {
                fields = FieldReflectionUtil.getAllFields(o.getClass());
                dealPrecisionField(fields,o);
            }
        }else{
            //详情页面等 --- 数据加密处理,需要前端配合解密展示
            fields = FieldReflectionUtil.getAllFields(var.getClass());
            dealEncryptField(fields,var);
        }
    }

    /**
     * 处理数据加密 前端配合解密处理
     * @param fields
     * @param var
     */
    private void dealEncryptField(Field[] fields,Object var) throws IllegalAccessException {
        for (Field field : fields) {
            if(!Optional.ofNullable(field).isPresent() || !field.isAnnotationPresent(EncryptField.class)){
                continue;
            }
            if(!field.isAccessible()){
                field.setAccessible(true);
            }
            Object o = field.get(var);
            if(!Optional.ofNullable(o).isPresent()){
                continue;
            }
            if(!(o instanceof String)){
                //递归处理
                Field[] allFields = FieldReflectionUtil.getAllFields(o.getClass());
                this.dealEncryptField(allFields,o);
            }else{
                 String value = encryptField((String) o);
                field.set(var,value);
            }
        }
    }

    /**
     * 字段加密处理
     * @return
     */
    private String encryptField(String source){
        if(StringUtils.isEmpty(source)){
            return source;
        }
        String encryptValue = MD5Util.MD5Encode(source).toUpperCase();
        return encryptValue;
    }

    /**
     * 处理数据脱敏
     * @param fields
     * @param var
     * @throws IllegalAccessException
     */
    private void dealPrecisionField(Field[] fields,Object var) throws IllegalAccessException {
        for (Field field : fields) {
            if(!Optional.ofNullable(field).isPresent() || !field.isAnnotationPresent(EncryptField.class)){
                continue;
            }
            if(!field.isAccessible()){
                field.setAccessible(true);
            }
            Object o = field.get(var);
            if(!Optional.ofNullable(o).isPresent()){
                continue;
            }
            if(!(o instanceof String) && !(o instanceof Integer)){
                //递归处理
                Field[] allFields = FieldReflectionUtil.getAllFields(o.getClass());
                this.dealPrecisionField(allFields,o);
            }else{
                Object value = null;
                if(o instanceof String){
                    value = dealFieldValue(o);
                }
                if(o instanceof Integer){
                    value = dealFieldValue( o);
                }
                 field.set(var,value);
            }
        }
    }

    /**
     * 字段数据脱敏
     * @param obj
     * @return
     */
    private Object dealFieldValue(Object obj){
        //integer类型枚举的直接返回
        if(obj instanceof Integer){
            return null;
        }
        String value = (String) obj;
        if(StringUtils.isEmpty(value)){
            return value;
        }
        if(value.length() == IDENTITY_CARD_LENGTH_18 || value.length() == IDENTITY_CARD_LENGTH_15){
            value = idCardReplace(value);
        }
        if(value.length() == MOBILE_LENGTH){
            value = mobileReplace(value);
        }
        if(value.length() <= OTHER_DESENSITIZATION_LENGTH){
            value = dealLessField(value);
        }
        return value;
    }


    private String dealLessField(String value){
        if(StringUtils.isEmpty(value) || value.length() > 3){
            return value;
        }
        StringBuilder builder = new StringBuilder();
        for (int i = 0; i < value.length(); i++) {
            builder.append("*");
        }
        return builder.toString();
    }

    /**
     * 身份证号脱敏,保留前四位和后四位
     * @param idCard 身份证号
     * @return
     */
    public  String idCardReplace(String idCard) {
        if (StringUtils.isEmpty(idCard)) {
            return null;
        }
        return replaceAction(idCard, IDENTITY_CARD_DESENSITIZATION);
    }


    /**
     * 银行卡替换,保留后四位
     * @param bankCard 银行卡号
     * @return
     */
    public  String bankCardReplace(String bankCard) {
        if (StringUtils.isEmpty(bankCard)) {
            return null;
        }
         return replaceAction(bankCard, BLANK_CARD_DESENSITIZATION);
    }

    /**
     *手机号脱敏,保留前三后四位
     * @param mobile
     * @return
     */
    public String mobileReplace(String mobile){
        if(StringUtils.isEmpty(mobile)){
            return mobile;
        }
        return replaceAction(mobile,MOBILE_DESENSITIZATION);
    }

    /**
     * 脱敏操作
     * @param source
     * @param regular  正则
     * @return
     */
    private  String replaceAction(String source, String regular) {
        return source.replaceAll(regular, "*");
    }


}

 

业务调用的方法:

public void export(ChargingSubsidiesReq chargingSubsidiesReq, HttpServletResponse servletResponse) {
        if (null == chargingSubsidiesReq) {
            chargingSubsidiesReq = new ChargingSubsidiesReq();
        }
        chargingSubsidiesReq.setPageSize(1);
        chargingSubsidiesReq.setCurrPage(1);
        ChargingSubsidiesReqDto reqDto = ObjectCheckUtil.createClass(ChargingSubsidiesReqDto.class);
        this.setCondition(chargingSubsidiesReq,reqDto);
        Response> response = chargingSubsidiesService.queryChargingSubsidies(reqDto);
        if (response.isSuccess() && Optional.ofNullable(response.getData()).isPresent()) {
            PageResult pageResult = response.getData();
            ChargeSubsidyServiceImpl proxyObj = SpringContextUtil.getBean(ChargeSubsidyServiceImpl.class);
            List voList = proxyObj.getExcelData(pageResult, reqDto);
            String excelTitle = StringUtils.isEmpty(chargingSubsidiesReq.getExcelTitle()) ? DriverEnum.DRIVER_CHARGE_SUBSIDIES_EXCEL_TITLE.getMsg() : chargingSubsidiesReq.getExcelTitle();
            List headList = new ArrayList<>();
            CommonUtil.setHeadList(ChargingSubsidiesResVo.class, headList);
            ExcelUtil.downloadExcelFile(excelTitle, headList, voList, servletResponse);
        }
    }


 @EncryptMethod
    public List getExcelData(PageResult pageResult,ChargingSubsidiesReqDto reqDto){
        ThreadPoolExecutor poolExecutor = BussinessThreadPool.getThreadPoolExecutor();
        int threadCount = CommonUtil.getThreadCount(pageResult);
        List>> futureList = new ArrayList<>();
        //多线程查询
        for (int i = 1; i <= threadCount; i++) {
            ChargingSubsidiesReqDto dto = ObjectCheckUtil.createClass(ChargingSubsidiesReqDto.class);
            BeanUtils.copyProperties(reqDto,dto);
            Future> submit = poolExecutor.submit(new ChargingSubsidiesTask(i, ValidateConstant.EXCEL_EXPORT_DEFAULT_SIZE, dto, chargingSubsidiesService));
            futureList.add(submit);
        }
        List voList = new ArrayList<>();
        List tempList = new ArrayList<>();
        if (CollectionUtils.isNotEmpty(futureList)) {
            for (Future> future : futureList) {
                try {
                    List dtoList = future.get();
                    tempList.addAll(dtoList);
                } catch (InterruptedException | ExecutionException e) {
                    future.cancel(true);
                    log.error("获取线程数据异常{}:", e.getMessage(), e);
                }
            }
            tempList.stream().forEach(var -> {
                ChargingSubsidiesResVo vo = ObjectCheckUtil.createClass(ChargingSubsidiesResVo.class);
                BeanUtils.copyProperties(var, vo);
                vo.setIdentityCard(var.getIdNumber());
                vo.setMobile(var.getDriverMobile());
                voList.add(vo);
            });
        }
        return voList;
    }

 

该业务是使用多线程获取excel导出的数据,在再使用多线程填充excel,具体见我的另外一篇博客:

https://blog.csdn.net/qq_42151769/article/details/100674862

 

如果你在导出中使用:

this.getExcelData()的方法,那么不好意思,切面是无效的,原因是this是真实对象,不是一个代理对象,而aop切面必须是代理对象才能生效的,那么,我们就需要想办法获取到代理对象,因为spring IOC中存放的就是代理类对象,所以我们需要拿到它

如下: 注意别忘记了打上注解@Component

package com.t3.ts.driver.resume.context;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.aop.framework.AdvisedSupport;
import org.springframework.aop.support.AopUtils;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;

import java.lang.reflect.Field;

/**
 * @class_name: SpringContextUtil
 * @description: 获取spring ioc中的bean
 * @author: wm_yu
 * @create: 2019/09/12
 **/
@Component
public class SpringContextUtil implements ApplicationContextAware {

    private final static Logger log = LoggerFactory.getLogger(SpringContextUtil.class);

    private static ApplicationContext applicationContext;

    @Override
    public void setApplicationContext(ApplicationContext applicationContextParam) throws BeansException {
        applicationContext = applicationContextParam;
    }

    public static Object getObject(String id) {
        Object object = null;
        object = applicationContext.getBean(id);
        return object;
    }
    /**
     * 类路径获取
     * @param tClass
     * @return
     */
    public static Object getBean(String tClass) {
        return applicationContext.getBean(tClass);
    }

    /**
     * 字节码对象获取
     * @param tClass
     * @param  代理对象
     * @return
     */
    public static  T getBean(Class tClass) {
        return applicationContext.getBean(tClass);
    }


    /**
     * 根据传入获取真实对象
     * @param beanInstance
     * @return
     */
    public static T getTarget(T beanInstance) {
        if (!AopUtils.isAopProxy(beanInstance)) {
            return beanInstance;
        } else if (AopUtils.isCglibProxy(beanInstance)) {
            try {
                Field h = beanInstance.getClass().getDeclaredField("CGLIB$CALLBACK_0");
                h.setAccessible(true);
                Object dynamicAdvisedInterceptor = h.get(beanInstance);
                Field advised = dynamicAdvisedInterceptor.getClass().getDeclaredField("advised");
                advised.setAccessible(true);
                T target = (T)((AdvisedSupport) advised.get(dynamicAdvisedInterceptor)).getTargetSource().getTarget();
                return target;
            } catch (Exception e) {
                log.error("获取真实对象异常:{}",e.getMessage(),e);
            }
        }
        return null;
    }





}

通过这个类,我们就能拿到代理对象了:

在代码中引用他:

ChargeSubsidyServiceImpl proxyObj = SpringContextUtil.getBean(ChargeSubsidyServiceImpl.class);
 List voList = proxyObj.getExcelData(pageResult, reqDto);

 

我们可以debug调试下:

spring切面注解失效_第1张图片

可以看到获取到了本类的代理对象,再看下用this调用也就是不获取代理对象的情况:

spring切面注解失效_第2张图片

可以看到this是真实对象

这里看到的代理对象是cglib的,原因是我没有定义接口,所以spring使用的是cglib代理的

好了,完美解决了,aop切面失效的原因

 

 

 

 

 

 

 

你可能感兴趣的:(Java)