前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >拦截器的使用

拦截器的使用

作者头像
用户2193479
发布2018-06-28 17:50:05
9200
发布2018-06-28 17:50:05
举报
文章被收录于专栏:全栈全栈

穿越:从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 }

一些说明

在上述过程中需要理解一下的有以下两点 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 }

一些说明

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

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2018-05-17 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 拦截器的作用
  • 拦截器的实现方式
  • 实战
    • 基于Spring AOP的拦截器-登录验证
      • 实现过程
      • 具体实现
      • 一些说明
    • 基于Spring AOP的拦截器-系统日志
      • 具体实现
      • 一些说明
      • 效果
    • 基于Servlet规范的拦截器-跨域请求
      • 实现过程
      • 具体实现
      • 一些说明
相关产品与服务
容器服务
腾讯云容器服务(Tencent Kubernetes Engine, TKE)基于原生 kubernetes 提供以容器为核心的、高度可扩展的高性能容器管理服务,覆盖 Serverless、边缘计算、分布式云等多种业务部署场景,业内首创单个集群兼容多种计算节点的容器资源管理模式。同时产品作为云原生 Finops 领先布道者,主导开源项目Crane,全面助力客户实现资源优化、成本控制。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档