• 拦截器的使用


    穿越:从0开始,构建前后端分离应用

    拦截器的作用

    拦截器是web项目不可或缺的组成部分,一般使用拦截器实现以下功能

    1、登录session验证

      防止浏览器端绕过登录,直接进入到应用

      或者session超时后,返回到登录页面

    2、记录系统日志

      一个完善的应用系统,应该具备监控功能,通过完善的系统日志记录系统运行过程中都经历了什么,当发生错误的时候及时通知管理人员,将损失降到最低。同时通过系统日志的监控,也能监控每次访问的响应时长,作为性能调优的参考

    3、对请求进行前置或后置的操作

      比如对于服务端返回的异常信息,可以通过拦截器统一的进行后处理,使其格式统一

    拦截器的实现方式

    有两种方式

    1、基于Spring AOP 的切面方式
    2、基于Servlet规范的拦截器
     

    实战

    下面分享一下拦截器,在我的项目中是如何使用的。

    我分别用基于Spring AOP的拦截器实现了登录验证及系统日志

    使用基于Servlet规范的拦截器实现了跨域请求

    基于Spring AOP的拦截器-登录验证

    实现过程

    1、pom中添加依赖
    <dependency>
        <groupId>org.aspectj</groupId>
        <artifactId>aspectjweaver</artifactId>
        <version>1.9.1</version>
    </dependency>
    2、开启Spring对@AspectJ的支持
    在spring-mybatis.xml配置文件中,加入下面的内容
    <!--开启Spring对@AspectJ的支持-->
    <aop:aspectj-autoproxy/>
    当然,要先在xml文件头部加上aop的命名空间(红色字体部分)
    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xmlns:aop="http://www.springframework.org/schema/aop"
           xmlns:tx="http://www.springframework.org/schema/tx"
           xsi:schemaLocation="http://www.springframework.org/schema/beans
           http://www.springframework.org/schema/beans/spring-beans.xsd
           http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd
           http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd"> 
    3、新建拦截器类 LoginInterceptor
    4、在类上添加注解
         @Component :将类的实例纳入到Spring 容器中管理
         @Aspect :声明是基于@ASpectJ的注解实现
    5、新建通知方法
        当应用中的方法处于切点表达式声明的范围内的时候,通知将被执行
    6、使用@Around、@Before、@After来生命通知的类型是环绕通知、前置通知、后置通知
    7、定义切点表达式

    具体实现

     1 package com.wt.common.security.interceptor;
     2 
     3 import com.wt.common.core.annotations.IgnoreAuth;
     4 import com.wt.common.core.result.HttpResultEntity;
     5 import com.wt.common.core.result.HttpResultHandle;
     6 import com.wt.common.core.utils.ServletNativeObjectUtil;
     7 import com.wt.common.security.handler.HttpSessionHandler;
     8 import com.wt.common.security.model.SysUser;
     9 import org.aspectj.lang.ProceedingJoinPoint;
    10 import org.aspectj.lang.annotation.Around;
    11 import org.aspectj.lang.annotation.Aspect;
    12 import org.aspectj.lang.reflect.MethodSignature;
    13 import org.springframework.core.annotation.Order;
    14 import org.springframework.stereotype.Component;
    15 
    16 import javax.servlet.http.HttpServletRequest;
    17 import java.lang.reflect.Method;
    18 
    19 /**
    20  * @ProjectName: syInfo
    21  * @Package: com.wt.common.core.interceptor
    22  * @Description:
    23  * @Author: lichking2017@aliyun.com
    24  * @CreateDate: 2018/5/16 上午8:20
    25  * @Version: v1.0
    26  */
    27 
    28 @Component
    29 @Order(1)
    30 @Aspect
    31 public class LoginInterceptor {
    32 //    Logger logger = LoggerFactory.getLogger(LoginInterceptor.class);
    33 
    34     @Around("@within(org.springframework.web.bind.annotation.RestController)")
    35     public HttpResultEntity loginCheck(ProceedingJoinPoint pjp) throws Throwable {
    36         HttpServletRequest request = ServletNativeObjectUtil.getRequest();
    37         SysUser loginUser = (SysUser) request.getSession().getAttribute(HttpSessionHandler.Items.LOGINUSER.name());
    38 
    39         final MethodSignature methodSignature = (MethodSignature) pjp.getSignature();
    40         final Method method = methodSignature.getMethod();
    41         boolean ignoreAuth = method.isAnnotationPresent(IgnoreAuth.class);
    42 
    43         if ((null == loginUser)&&!ignoreAuth) {
    44             return new HttpResultEntity(HttpResultHandle.HttpResultEnum.NOTLOG);
    45         }
    46         return (HttpResultEntity) pjp.proceed();
    47     }
    48 }
    View Code

    一些说明

    在上述过程中需要理解一下的有以下两点
    1、切点表达式
    @within(org.springframework.web.bind.annotation.RestController)
    它的意思代表了,通知的范围是只要有类添加了@RestController的注解,那么类中的方法,只要被调用,都会执行相应的通知
    2、为什么这么配置呢?
    为什么这么配置:因为我的项目是基于SpringMVC框架的,并且使用的请求都是基于Restful规范的。所以所有的Action都会配置@RestController这个注解,也就是说,所有的后台请求,
    3、上述配置要完成的功能是什么?
    如果用户没有登录,那么请求就会被打回,并在页面上给与用户提示
    4、对于@Around环绕通知的执行过程是什么样的?
    正常流:浏览器发起请求-》通知被执行-》在通知的内部,根据业务逻辑判断,该请求是否合法,也就是前置的一些处理,如果合法调用pjp.proceed()方法-》进入controller的方法执行,执行完成后-》返回到通知内部,继续执行pjp.proceed()后面的代码-》返回客户端
    异常流:浏览器发起请求-》通知被执行-》在通知的内部,根据业务逻辑判断,该请求是否合法,也就是前置的一些处理,如果不合法,直接return-》浏览器显示处理结果

    关于@AspectJ的相关知识就不再这里介绍了,感兴趣的朋友可以查看:@Aspect注解教程

    基于Spring AOP的拦截器-系统日志

    具体实现

      1 package com.wt.common.security.interceptor;
      2 
      3 
      4 import com.google.gson.Gson;
      5 import com.wt.common.core.exception.BaseErrorException;
      6 import com.wt.common.core.exception.BaseLogicException;
      7 import com.wt.common.core.result.HttpResultEntity;
      8 import com.wt.common.core.result.HttpResultHandle;
      9 import com.wt.common.core.utils.ServletNativeObjectUtil;
     10 import com.wt.common.security.handler.HttpSessionHandler;
     11 import com.wt.common.security.model.SysUser;
     12 import com.wt.common.security.model.SyslogPerformance;
     13 import com.wt.common.security.service.SyslogPerformanceService;
     14 import org.apache.commons.lang3.StringUtils;
     15 import org.aspectj.lang.ProceedingJoinPoint;
     16 import org.aspectj.lang.annotation.Around;
     17 import org.aspectj.lang.annotation.Aspect;
     18 import org.slf4j.Logger;
     19 import org.slf4j.LoggerFactory;
     20 import org.springframework.beans.factory.annotation.Autowired;
     21 import org.springframework.core.annotation.Order;
     22 import org.springframework.stereotype.Component;
     23 
     24 import javax.servlet.http.HttpServletRequest;
     25 
     26 /**
     27  * @ProjectName: syInfo
     28  * @Package: com.wt.common.core.interceptor
     29  * @Description:
     30  * @Author: lichking2017@aliyun.com
     31  * @CreateDate: 2018/5/16 下午4:14
     32  * @Version: v1.0
     33  */
     34 
     35 @Component
     36 @Aspect
     37 @Order(2)
     38 public class LogInterceptor {
     39 
     40     Logger logger = LoggerFactory.getLogger(LoginInterceptor.class);
     41 
     42     @Autowired
     43     private SyslogPerformanceService syslogPerformanceService;
     44 
     45 
     46     @Around("@within(org.springframework.web.bind.annotation.RestController)")
     47     public HttpResultEntity logRecord(ProceedingJoinPoint pjp) {
     48         Gson gson = new Gson();
     49         HttpServletRequest request = ServletNativeObjectUtil.getRequest();
     50         SyslogPerformance syslogPerformance = this.setLog(request);
     51         syslogPerformance.setParameters(gson.toJson(pjp.getArgs()));
     52 
     53         long startTime = System.currentTimeMillis(), endTime = 0, consume = 0;
     54 
     55         String requestInfo = String.format("⭐️{User-Agent:[%s],Protocol:[%s],Remote Addr:[%s],Method:[%s],uri:[%s],Cookie:[%s],operator:[%s],parameters:[%s]}⭐️",
     56                 request.getHeader("User-Agent"), request.getProtocol(), request.getRemoteAddr(),
     57                 request.getMethod(), request.getRequestURI(), request.getHeader("Cookie"),
     58                 "ceshi",
     59                 gson.toJson(pjp.getArgs()));
     60         try {
     61             HttpResultEntity result = (HttpResultEntity) pjp.proceed();
     62             endTime = System.currentTimeMillis();
     63             logger.info(requestInfo);
     64             return result;
     65         } catch (Throwable throwable) {
     66             endTime = System.currentTimeMillis();
     67             if (throwable instanceof BaseLogicException) {
     68                 String errorMessage = ((BaseLogicException) throwable).getExceptionBody().getMessage();
     69                 String errorCode = ((BaseLogicException) throwable).getExceptionBody().getMessage();
     70                 logger.error(StringUtils.join(requestInfo, errorMessage), throwable);
     71                 return HttpResultHandle.getErrorResult(errorCode, errorMessage);
     72             }
     73             if (throwable instanceof BaseErrorException) {
     74                 logger.error(StringUtils.join(requestInfo, throwable.getMessage()), throwable);
     75                 return HttpResultHandle.getErrorResult();
     76             }
     77 
     78             logger.error(StringUtils.join(requestInfo, throwable.getMessage()), throwable);
     79             return HttpResultHandle.getErrorResult();
     80 
     81         } finally {
     82             consume = endTime - startTime;
     83             syslogPerformance.setTimeConsuming(String.valueOf(consume));
     84             syslogPerformanceService.save(syslogPerformance);
     85         }
     86     }
     87 
     88     private SyslogPerformance setLog(HttpServletRequest request) {
     89         SysUser currentUser = (SysUser) request.getSession().getAttribute(HttpSessionHandler.Items.LOGINUSER.name());
     90         SyslogPerformance syslogPerformance = new SyslogPerformance();
     91         syslogPerformance
     92                 .setRemoteHost(request.getRemoteHost())
     93                 .setRemotePort(request.getRemotePort())
     94                 .setRequestType(request.getMethod())
     95                 .setRequestURI(request.getRequestURI());
     96         if(currentUser!=null){
     97             syslogPerformance.setOperatorId(currentUser.getUserId()).setOperatorName(currentUser.getUserName());
     98         }
     99         return syslogPerformance;
    100     }
    101 }
    View Code

    一些说明

    1、如果后台的请求执行正常,那么放行并记录日志

    2、如果出现错误,同一处理结果,并返回结果到浏览器

    3、无论处理过程是否异常,都会记录到数据库表当中

    效果

    1、功能如下图,每当一次请求被执行,在日志表中都会进行记录,包括时长,及时间。可以再扩展一下,加上操作人

    基于Servlet规范的拦截器-跨域请求

    实现过程

    1、新建拦截器类CrossDomainInterceptor,并继承自HandlerInterceptor
    2、对拦截器进行配置,在spring配置文件中,添加下面的内容
    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xmlns:mvc="http://www.springframework.org/schema/mvc"
           xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd">
    
        <mvc:interceptors>
            <mvc:interceptor>
                <mvc:mapping path="/**"/>
                <mvc:exclude-mapping path="/index.html"/>
                <bean class="com.wt.common.core.interceptor.CrossDomainInterceptor"/>
            </mvc:interceptor>
        </mvc:interceptors>
    
    </beans>
    3、重写以下方法
        preHandle:在请求调用之前调用
        postHandle:在请求执行完成,且返回视图渲染之前调用
        afterCompletion:在请求执行完成,并且完成视图渲染之后执行

    具体实现

     1 package com.wt.common.core.interceptor;
     2 
     3 import org.slf4j.Logger;
     4 import org.slf4j.LoggerFactory;
     5 import org.springframework.core.annotation.Order;
     6 import org.springframework.web.servlet.HandlerInterceptor;
     7 
     8 import javax.servlet.http.HttpServletRequest;
     9 import javax.servlet.http.HttpServletResponse;
    10 
    11 /**
    12  * @ProjectName: syInfo
    13  * @Package: com.wt.common.core.interceptor
    14  * @Description:
    15  * @Author: lichking2017@aliyun.com
    16  * @CreateDate: 2018/5/15 下午11:21
    17  * @Version: v1.0
    18  */
    19 @Order(1)
    20 public class CrossDomainInterceptor implements HandlerInterceptor {
    21     Logger logger = LoggerFactory.getLogger(CrossDomainInterceptor.class);
    22     @Override
    23     public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
    24         response.setHeader("Access-Control-Allow-Headers", "X-Requested-With, accept, content-type, xxxx");
    25         response.setHeader("Access-Control-Allow-Methods", "GET, HEAD, POST, PUT, DELETE, TRACE, OPTIONS, PATCH");
    26         response.setHeader("Access-Control-Allow-Origin", "*");
    27         response.setHeader("Access-Control-Allow-Credentials", "true");
    28         return true;
    29     }
    30 }

    一些说明

    1、这个比较简单,没什么太多说的地方,注意方法的返回值即可,根据项目的业务逻辑,如果请求通行,那么就return true,否则返回false。
    2、如果有多个拦截器,执行顺序会按照拦截器在spring配置文件中声明的先后顺序执行,执行过程如下
        如果有A、B两个拦截器,A声明在先,B声明在后,执行顺序为
        A.preHandle-》B.preHandle-》B.postHandle-》A.postHandle

  • 相关阅读:
    2020杭电多校第二场 1006.The Oculus
    2020杭电多校第一场 1005.Fibonacci Sum
    数论——中国剩余定理
    数论——线性同余方程
    数论——乘法逆元
    数论——裴蜀定理
    javascript预解析和作用域
    数组的排序..........加深难度
    值类型和引用类型
    js中的==和===
  • 原文地址:https://www.cnblogs.com/lichking2017/p/9053625.html
Copyright © 2020-2023  润新知