shiro框架配合拦截器实现请求同步写入数据库

项目场景:

最近维护项目的时候,需要增加一个日志模块来记录用户每次的操作请求,一开始习惯的使用了aop切面功能,后面发现要配置整个项目的话,需要在每个请求前都加一个自定义注解才能实现切面日志记录,不太实际,后面选用了shiro框架配合拦截器就能实现全局的日志写入,下面简单的说下这两种方法步骤吧。

方法一:AOP实现请求日志记录:

1.导入maven

<!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-aop -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-aop</artifactId>
    <version>2.6.1</version>
</dependency>

2.创建对应的日志实体类和自定义注解

根据项目需求创建相应的实体类,接着创建自定义注解。

/**
 * @author wangCj
 * @date 2021/12/13
 *  操作日志自定义注解类
 */
@Target(ElementType.METHOD) //注解放置的目标位置,METHOD是可注解在方法级别上
@Retention(RetentionPolicy.RUNTIME) //注解在哪个阶段执行
@Documented //生成文档
public @interface OperationLog {
    
    
    String value() default "";
}

3.创建对应的aop切面类

/**
 * @author wangCj
 * @date 2021/12/13
 *  系统日志:切面处理类
 */
@Aspect
@Component
public class OperationLogAspect {
    
    
    //定义切点 @Pointcut
    //在注解的位置切入代码
    @Pointcut("@annotation(com.ylzinfo.framework.log.loginlog.domain.OperationLog)")
    public void logPoinCut() {
    
    
    }
    //切面 配置通知
    @AfterReturning("logPoinCut()")
    public void writeLog(JoinPoint joinPoint) {
    
    
        //保存日志
        Log log = new Log();
        //从切面织入点处通过反射机制获取织入点处的方法
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        //获取切入点所在的方法
        Method method = signature.getMethod();
        //获取操作
        OperationLog operationLog = method.getAnnotation(OperationLog.class);
        if (operationLog != null) {
    
    
            String value = operationLog.value();
            log.setOperation(value);//保存获取的操作
        }
        //获取请求的类名
        String className = joinPoint.getTarget().getClass().getName();
        //获取请求的方法名
        String methodName = method.getName();
        log.setMethod(className + "." + methodName);
        //请求的参数
        Object[] args = joinPoint.getArgs();
        //将参数所在的数组转换成json
        String params = JSON.toJSONString(args);
        log.setParams(params);
        log.setCreateDate(new Date());
        //获取用户账号、ip地址、uuid
        Subject subject = SecurityUtils.getSubject();
        Session session = subject.getSession();
        String name = session.getAttribute("key").toString();
        log.setUsername(name);
        String host = subject.getSession().getHost();
        log.setIp(host);
        log.setId(UUID.randomUUID().toString().replace("-",""));
       //对应service 将日志对象log写入数据库
    }
}

4.在需要记录日志的请求上添加自定义注解

 @RequestMapping(value = "/findAll")
    @ResponseBody
    @RequiresPermissions("log:loginlog:view")
    @OperationLog(value = "查询登录日志")  //AOP的自定义注解
    public AjaxPageResponse query(@RequestParam(required = false) String username,
                                  @RequestParam(required = false) String status,
                                  @RequestParam(required = false) @DateTimeFormat(pattern = "yyyy-MM-dd") Date logindatebegin,
                                  @RequestParam(required = false) @DateTimeFormat(pattern = "yyyy-MM-dd") Date logindateend,
                                  AjaxPageResponse page) {
    
    
        loginLogRecordService.query(username, status,logindatebegin,logindateend, page);
        return page;
    }

方法二:shiro框架配合拦截器实现日志写入:

1.引入shiro框架后,配置相应文件spring-shiro-kisso.xml

在这里插入图片描述

2.spring-mvc中如下配置

在这里插入图片描述

3.对应的AccessLogInterceptor类

package com.ylzinfo.framework.log.accesslog.interceptor;

import com.ylzinfo.eva.core.util.IpUtils;
import com.ylzinfo.framework.log.accesslog.domain.AccessLog;
import com.ylzinfo.framework.log.accesslog.service.AccessLogService;
import com.ylzinfo.framework.log.loginlog.service.SysLogService;
import com.ylzinfo.framework.sys.utils.UserUtils;
import com.ylzinfo.framework.util.LogUtils;
import org.apache.commons.lang3.exception.ExceptionUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.NamedThreadLocal;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Date;

public class AccessLogInterceptor implements HandlerInterceptor {
    
    

    @Autowired(required = false)
    private AccessLogService logService;

    @Autowired(required = false)
    private SysLogService sysLogService;

    private static final ThreadLocal<Date> startTimeThreadLocal = new NamedThreadLocal<Date>("ThreadLocal StartTime");

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
    
    
        startTimeThreadLocal.set(new Date()); // 线程绑定变量(该数据只有当前请求的线程可见)
        return true;
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
    
    
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
    
    
        if (logService == null) {
    
    
            return;
        }
        String sessionid = request.getRequestedSessionId().trim();
        String ip = IpUtils.getIpAddr(request).trim();
        String accept = request.getHeader("accept").trim();
        String referer = request.getHeader("Referer").trim();
        String url = request.getRequestURI().trim();
        String method = request.getMethod().trim();
        String headers = LogUtils.getHeaders(request).trim();
        String useragent = request.getHeader("User-Agent").trim();
        String params = LogUtils.getParams(request).trim();
        Date begindate = startTimeThreadLocal.get();// 得到线程绑定的局部变量(开始时间)
        Date enddate = new Date(); // 2、结束时间
        long c = enddate.getTime() - begindate.getTime();
        double time = c / 1000;
        AccessLog log = new AccessLog(sessionid, ip, accept, referer, url, method, headers, useragent, params, begindate, enddate, time);

        log.setUserid(UserUtils.getUserid());
        log.setUsername(UserUtils.getUsername().trim());

        if (ex != null) {
    
    
            log.setExceptionname(ex.getClass().getName());
            log.setException(ExceptionUtils.getStackTrace(ex));
        }
        startTimeThreadLocal.remove();
        sysLogService.save(log);
    }
}

实现的功能界面截图如下

在这里插入图片描述

总结

由于是已经上线的项目,在接口都已经编写好的情况下,我更倾向于用第二种方法来实现请求的日志写入功能。

猜你喜欢

转载自blog.csdn.net/weixin_50927151/article/details/121948730