关于Spring Boot 中AOP拦截入参记录日志报错的原因分析

异常:java.lang.IllegalStateException: It is illegal to call this method if the current request is not in asynchronous mode (i.e. isAsyncStarted() returns false)


package cn.bainuo.gene.aspect;

import cn.bainuo.gene.common.utils.RedisUserUtil;
import cn.bainuo.gene.security.domain.User;
import cn.bainuo.gene.security.domain.vo.UserInfoVo;
import cn.bainuo.gene.security.service.LoginService;
import cn.bainuo.gene.utils.utils.HttpRequestUtil;
import cn.bainuo.gene.utils.utils.StringUtils;
import cn.bainuo.gene.utils.utils.SystemControllerLog;
import cn.bainuo.gene.utils.utils.SystemServiceLog;
import com.alibaba.fastjson.JSON;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;

@Aspect
@Component
@SuppressWarnings("all")
public class SystemLogAspect {
    Logger logger = LoggerFactory.getLogger(this.getClass());

    @Autowired
    private LoginService loginService;


    @Autowired
    private RedisUserUtil redisUserUtil;

    @Pointcut("@annotation(cn.bainuo.gene.utils.utils.SystemServiceLog)")
    public void serviceAspect(){
    }

    @Pointcut("@annotation(cn.bainuo.gene.utils.utils.SystemControllerLog)")
    public void controllerAspect(){
    }

    @Before("controllerAspect()")
    public  void doBefore(JoinPoint joinPoint) {
        HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
        String token =(String)request.getAttribute("token");
        String userName="";
        if(StringUtils.isNotBlank(token)) {
            //读取session中的用户,注意:proai项目中读取redis缓存用户数据异常.
//            UserInfoVo user = redisUserUtil.getCurrentUser(token);
            User userInfoVo = loginService.getUserInfo(token);
            userName=userInfoVo.getUserName();
        }
        //请求的IP
        String ip = request.getRemoteAddr();
        String params = "";
        Object[] arguments  = new Object[joinPoint.getArgs().length];
        if (joinPoint.getArgs() != null && joinPoint.getArgs().length > 0) {
            for (int i = 0; i < joinPoint.getArgs().length; i++) {
              //这里出现参数不能转换问题,想了半天最后发现里面有request对象不能被序列化
               //最后把这些对象过滤了。
//                params += JSON.toJSON(joinPoint.getArgs()[i]) + ";";  
                if (joinPoint.getArgs()[i] instanceof ServletRequest || joinPoint.getArgs()[i] instanceof ServletResponse || joinPoint.getArgs()[i] instanceof MultipartFile) {
                    //HttpServletRequest不能序列化,从入参里排除,否则报异常:java.lang.IllegalStateException:
                    // It is illegal to call this method if the current request is not in asynchronous mode
                    // (i.e. isAsyncStarted() returns false)
                    continue;
                }
                arguments[i] = joinPoint.getArgs()[i];
            }
        }
        if (arguments != null  && arguments.length > 0) {
            try {
//这里有个很严重的问题,因为JSON序列化有问题,所以这里会报错,改为Google 的Gson类来转换即可
                params = JSON.toJSONString(arguments);
            } catch (Exception e) {
                params = arguments.toString();
            }
        }

        try {
            //*========控制台输出=========*//
            logger.info("=====前置通知开始=====");
            logger.info("请求方法:" + (joinPoint.getTarget().getClass().getName() + "." + joinPoint.getSignature().getName() + "()"));
            logger.info("方法描述:" + getControllerMethodDescription(joinPoint));
            logger.info("请求人:" + userName);
            logger.info("请求IP:" + ip);
            logger.info("请求参数:" + params);
            logger.info("=====前置通知结束=====");
        }  catch (Exception e) {
            //记录本地异常日志
            logger.error("==前置通知异常==");
            logger.error("异常信息:{}", e.getMessage());
        }
    }

    @AfterThrowing(pointcut = "serviceAspect()",throwing = "e")
    public void doAfterThrowing(JoinPoint joinPoint, Throwable e){
        HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
        String token =(String)request.getAttribute("token");
        String userName="";
        if(StringUtils.isNotBlank(token)) {
            //读取session中的用户 注意:proai项目中读取redis缓存用户数据异常.
//            UserInfoVo user = redisUserUtil.getCurrentUser(token);
            User userInfoVo = loginService.getUserInfo(token);
            userName=userInfoVo.getUserName();
        }
        //获取请求ip
        String ip = HttpRequestUtil.getHost(request);
        //获取用户请求方法的参数并序列化为JSON格式字符串
        String params = "";
        Object[] arguments  = new Object[joinPoint.getArgs().length];
        if (joinPoint.getArgs() != null && joinPoint.getArgs().length > 0) {
            for (int i = 0; i < joinPoint.getArgs().length; i++) {
//                params += JSON.toJSON(joinPoint.getArgs()[i]) + ";";
                if (joinPoint.getArgs()[i] instanceof ServletRequest || joinPoint.getArgs()[i] instanceof ServletResponse || joinPoint.getArgs()[i] instanceof MultipartFile) {
                    //HttpServletRequest不能序列化,从入参里排除,否则报异常:java.lang.IllegalStateException:
                    // It is illegal to call this method if the current request is not in asynchronous mode
                    // (i.e. isAsyncStarted() returns false)
                    continue;
                }
                arguments[i] = joinPoint.getArgs()[i];
            }
        }
        if (arguments != null  && arguments.length > 0) {
            try {
                params = JSON.toJSONString(arguments);
            } catch (Exception e1) {
                params = arguments.toString();
            }
        }
        try {
            StackTraceElement s= e.getStackTrace()[0];
            /*========控制台输出=========*/
            logger.info("=====异常通知开始=====");
            logger.info("异常代码类/方法/行数:" +joinPoint.getTarget().getClass().getName()+"/"+s.getMethodName()+"/"+s.getLineNumber());
            /*logger.info("异常方法:" + (joinPoint.getTarget().getClass().getName() + "." + joinPoint.getSignature().getName() + "()"));*/
            logger.info("方法描述:" + getServiceMethodDescription(joinPoint));
            logger.info("请求人:" + userName);
//            logger.info("请求IP:" + ip);
            logger.info("请求参数:" + params);
            logger.info("=====异常通知结束=====");
        } catch (Exception ex) {
            //记录本地异常日志
            logger.error("==异常通知异常==");
            logger.error("异常信息:{}", ex.getMessage());
        }
    }

    public static String getServiceMethodDescription(JoinPoint joinPoint)throws Exception{
        String targetName = joinPoint.getTarget().getClass().getName();
        String methodName = joinPoint.getSignature().getName();
        Object[] arguments = joinPoint.getArgs();
        Class targetClass = Class.forName(targetName);
        Method[] methods = targetClass.getMethods();
        String description = "";
        for (Method method:methods) {
            if (method.getName().equals(methodName)){
                Class[] clazzs = method.getParameterTypes();
                if (clazzs.length==arguments.length){
                    description = method.getAnnotation(SystemServiceLog.class).description();
                    break;
                }
            }
        }
        return description;
    }

    public  static String getControllerMethodDescription(JoinPoint joinPoint)  throws Exception {
        String targetName = joinPoint.getTarget().getClass().getName();
        String methodName = joinPoint.getSignature().getName();
        Object[] arguments = joinPoint.getArgs();
        Class targetClass = Class.forName(targetName);
        Method[] methods = targetClass.getMethods();
        String description = "";
        for (Method method : methods) {
            if (method.getName().equals(methodName)) {
                Class[] clazzs = method.getParameterTypes();
                if (clazzs.length == arguments.length) {
                    description = method.getAnnotation(SystemControllerLog. class).description();
                    break;
                }
            }
        }
        return description;
    }
}

最初的观察并没有发现明显的问题,因为paramter本身就是String类型; 后进行debug后,定位出问题所在位置: paramter = JSON.toJSONString(args); 在执行后报错:java.lang.IllegalStateException: It is illegal to call this method if the current request is not in asynchronous mode (i.e. isAsyncStarted() returns false)

上网查询资料后发现,是因为使用 Object[] args = joinPoint.getArgs(); 获取入参的时候,args还包含了一些其他的内容,比如ServletRequest等,而这些入参并不能进行序列化,所以JSON.toJSONString时报错,只能注释这行代码了,重新改造一下,在上面的代码中已经实现了。

if (joinPoint.getArgs() != null && joinPoint.getArgs().length > 0) {
            for (int i = 0; i < joinPoint.getArgs().length; i++) {
              //这里出现参数不能转换问题,想了半天最后发现里面有request对象不能被序列化
               //最后把这些对象过滤了。
//                params += JSON.toJSON(joinPoint.getArgs()[i]) + ";";  
               
                arguments[i] = joinPoint.getArgs()[i];
            }
        }
       

 

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
Spring Boot可以通过AOP(面向切面编程)来实现数据脱敏。 数据脱敏是指在保持数据结构不变的情况下,对敏感数据进行加密或者替换等操作,以保护数据的安全性和隐私。在Spring Boot,可以使用AOP拦截需要脱敏的方法,然后对方法返回的敏感数据进行处理。 首先,我们需要定义一个脱敏的注解,例如@SensitiveData,用于标记需要进行脱敏的方法或者字段。接着,我们可以自定义一个AOP的切面类,使用@Around注解来拦截带有@SensitiveData注解的方法。 在切面类,我们可以使用反射来获取方法返回的数据,并进行脱敏处理。对于不同类型的数据,可以采取不同的脱敏策略,例如对字符串进行部分隐藏、手机号码替换为部分星号、银行卡号进行掩码等。脱敏处理完成后,可以将处理后的数据替换原始数据,并返回给调用方。 在Spring Boot的配置文件,我们需要开启AOP的自动配置,并配置切面类的扫描路径。这样,当有方法被调用时,带有@SensitiveData注解的方法会被切面类拦截,并进行脱敏处理。 通过使用AOP实现数据脱敏,可以将脱敏的逻辑与业务逻辑分离,提高了代码的可读性和可维护性。同时,使用注解标记需要脱敏的方法,使得代码更加清晰明了。 总之,Spring Boot通过AOP可以很方便地实现数据脱敏,保护敏感数据的安全性和隐私。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值