1、认识拦截器

  SpringMVC的拦截器(Interceptor)不是Filer,同样可以实现请求的预处理、后处理。使用拦截器仅需要两个步骤

  实现拦截器

  注册拦截器

1.1实现拦截器

  实现拦截器可以自定义实现HandleInterceptor接口,也可以继承HandleInterceptorAdatper类,后者是前者的实现类。

  下面是拦截器实现的一个例子,目的是判断用户是否登录。如果preHandle方法return true ,则后续方法继续执行。

  1 package zsjmsdemo.interceptor;
2
3 import java.io.PrintWriter;
4 import java.util.Set;
5 import java.util.concurrent.TimeUnit;
6
7 import javax.servlet.http.Cookie;
8 import javax.servlet.http.HttpServletRequest;
9 import javax.servlet.http.HttpServletResponse;
10
11 import org.springframework.data.redis.core.RedisTemplate;
12 import org.springframework.util.ObjectUtils;
13 import org.springframework.web.servlet.ModelAndView;
14 import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
15
16 import com.alibaba.fastjson.JSON;
17 import com.ezhiyang.xxxb.common.DataResult;
18 import com.ezhiyang.xxxb.utils.MobileUtil;
19
20 public class LoginInterceptor extends HandlerInterceptorAdapter {
21
22 private RedisTemplate<String, Object> redisTemplate;
23
24 /**
25 * 预处理回调方法,实现处理器的预处理(如登陆检查/判断同一对象短时间内是否重复调用接口等) 第三个参数为相应的处理器即controller
26 * f返回true表示流程继续,调用下一个拦截器或者处理器,返回false表示流程中断,通过response产生响应
27 */
28 @Override
29 public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
30 throws Exception {
31 String token = getToken(request);
32 if (!ObjectUtils.isEmpty(token)) {
33 //判断同一用户短时间内是否重复请求接口
34 String method = request.getMethod();
35 if (method!=null&&("POST".equalsIgnoreCase(method)||"GET".equalsIgnoreCase(method))) {
36 String requestUri = request.getRequestURI();
37 String url = requestUri.substring(request.getContextPath().length());//获取此次请求访问的是哪个接口
38 String ip=MobileUtil.getIpAddr(request);//获取当前的ip地址值
39 String requestKey=ip+"_"+token+"_"+url;
40 Object object = redisTemplate.opsForValue().get("requestKey");
41 //若重复请求则提示
42 if (!ObjectUtils.isEmpty(object)) {
43 DataResult dataResult=new DataResult();
44 dataResult.setCode("4001");
45 dataResult.setMsg("请求太频繁,请稍后再试");
46 response.setCharacterEncoding("utf-8");
47 response.setContentType("text/html; charset=utf-8");
48 PrintWriter writer=response.getWriter();
49 writer.print(JSON.toJSON(dataResult));
50 writer.close();
51 response.flushBuffer();
52 return false;
53 }
54 //若是第一次请求则记录请求标记,在处理器执行完成后删除标记
55 redisTemplate.opsForValue().set(requestKey, url, 5, TimeUnit.SECONDS);//设置5秒,只是为了防止拦截器的后置处理方法没执行到(比如突然断电),导致后续的同类请求都不能执行
56 //如果请求允许,就记住key,请求处理完后,还要删除标识
57 request.setAttribute("ACCESS_KEY", requestKey);
58 }
59
60 //根据token从redis中获取登录信息
61 Set<String> keys = redisTemplate.keys(token);
62 if (!ObjectUtils.isEmpty(keys) && keys.size() == 1) {
63 Object nimitokenvalue = redisTemplate.opsForValue().get(keys.iterator().next());
64 if (ObjectUtils.isEmpty(nimitokenvalue)) {
65 return true;
66 }
67 } else {
68 Object loginInfo = redisTemplate.boundValueOps(token)
69 .get();
70 if (loginInfo != null) {
71 return true;
72 }
73 }
74 }else{
75 response.setContentType("text/html; charset=utf-8");
76 PrintWriter writer = response.getWriter();
77 writer.print(new DataResult("4001", "没登陆", new Object()));
78 writer.close();
79 response.flushBuffer();
80 return false;
81 }
82 return super.preHandle(request, response, handler);
83 }
84
85
86 /**
87 * 当请求进行处理之后,也就是controller方法调用之后执行,但是他会在DispatcherServlet进行视图渲染之前被调用
88 * 此时我们可以通过modelAndView对模型数据进行处理或对视图进行处理
89 */
90 @Override
91 public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
92 ModelAndView modelAndView) throws Exception {
93 System.out.println("-------------------postHandle");
94 String requestKey = (String)request.getAttribute("ACCESS_KEY");
95 if (requestKey!=null) {
96 redisTemplate.delete(requestKey);
97 }
98 }
99
100 /**
101 * 方法将在整个请求结束之后,也就是DispatcheServlet进行视图渲染之后执行,这个方法的主要作用是对资源的清理工作
102 */
103 @Override
104 public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
105 throws Exception {
106 System.out.println("-------------------afterCompletion");
107 }
108
109 @Override
110 public void afterConcurrentHandlingStarted(HttpServletRequest request, HttpServletResponse response, Object handler)
111 throws Exception {
112 }
113
114
115 /**
116 * 获取token
117 * @param req
118 * @return
119 */
120 public static String getToken(HttpServletRequest req) {
121 String token = req.getParameter("token");
122 if (!ObjectUtils.isEmpty(token)) {
123 return token;
124 } else {
125 Cookie[] cks = req.getCookies();
126 if (cks != null) {
127 for (Cookie ck : cks) {
128 if (ck.getName().equals("token")) {
129 return ck.getValue();
130 }
131 }
132 }
133 return req.getHeader("token");
134 }
135 }
136
137 }

1.2注册拦截器

 1 package com.cxs.allmodel.interceptor;
2
3 import javax.annotation.Resource;
4
5 import org.springframework.context.annotation.Configuration;
6 import org.springframework.data.redis.core.RedisTemplate;
7 import org.springframework.web.servlet.config.annotation.InterceptorRegistration;
8 import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
9 import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
10
11 /**
12 * 为了使自定义的拦截器生效,需要注册拦截器到spring容器中,具体的做法是继承WebMvcConfigurerAdapter类,
13 * 覆盖其addInterceptors(InterceptorRegistry registry)方法。最后别忘了把Bean注册到Spring容器中,
14 * 可以选择@Component 或者 @Configuration。
15 *
16 */
17 @Configuration
18 public class InterceptorConfig implements WebMvcConfigurer{
19 //在拦截器执行时实例化redisTemplate
20 @Resource
21 private RedisTemplate<String, Object> redisTemplate;
22
23 @Override
24 public void addInterceptors(InterceptorRegistry registry) {
25 // 注册拦截器
26 InterceptorRegistration ir = registry.addInterceptor(new LoginInterceptor(redisTemplate));
27 // 配置拦截的路径
28 ir.addPathPatterns("/**");
29 // 配置不拦截的路径
30 ir.excludePathPatterns("/user/info","/user/add");
31
32 // 还可以在这里注册其它的拦截器
33 //registry.addInterceptor(new OtherInterceptor()).addPathPatterns("/**");
34 }
35 }

 也可以直接在SpringBoot的启动类中继承WebMvcConfigurerAdaoter类

  1 package cn.wowkai.mall;
2
3 import java.nio.charset.Charset;
4 import java.util.Arrays;
5
6 import javax.annotation.PostConstruct;
7 import javax.annotation.Resource;
8 import javax.sql.DataSource;
9
10 import org.springframework.boot.SpringApplication;
11 import org.springframework.boot.autoconfigure.SpringBootApplication;
12 import org.springframework.context.annotation.Bean;
13 import org.springframework.data.redis.core.RedisTemplate;
14 import org.springframework.data.redis.serializer.StringRedisSerializer;
15 import org.springframework.http.HttpHeaders;
16 import org.springframework.scheduling.annotation.EnableScheduling;
17 import org.springframework.web.cors.CorsConfiguration;
18 import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
19 import org.springframework.web.filter.CorsFilter;
20 import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
21 import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
22
23 import com.fd.myshardingfordata.helper.ConnectionManager;
24 import com.fd.myshardingfordata.helper.TransManager;
25
26 import cn.wowkai.mall.web.interceptor.AuthInterceptor;
27
28 @EnableScheduling
29 @SpringBootApplication
30 public class ShopSaasMallApplication implements WebMvcConfigurer {
31
32 public static void main(String[] args) {
33 SpringApplication.run(ShopSaasMallApplication.class, args);
34 }
35
36 @Resource
37 protected RedisTemplate<String, Object> redisTemplate;
38
39 @PostConstruct
40 private void init() {
41 redisTemplate.setKeySerializer(new StringRedisSerializer(Charset.forName("UTF8")));
42 redisTemplate.setValueSerializer(new StringRedisSerializer(Charset.forName("UTF8")));
43 }
44
45 @Override
46 public void addInterceptors(InterceptorRegistry registry) {
47 registry.addInterceptor(new AuthInterceptor(redisTemplate)).excludePathPatterns("/mall/mp/*",
48 "/mall/notify/panganNotify", "/mall/notify/panganRechargeNotify", "/mall/notify/panganbillNotify",
49 "/mall/notify/wxpayrechargenotify", "/mall/notify/wxpaybillnotify", "/mall/notify/wxpaynotify",
50 "/mall/api/getToken", "/mall/store/getStoreListWithLongitudeLatitude",
51 "/mall/sett/getIndexTemplateListWithXcx", "/mall/product/productListForXcxSelect",
52 "/mall/product/custFindProduct", "/mall/product/custFindProductSpeceInventoryAndPrice",
53 "/mall/sett/xcx/getIndexTemplateMastList");
54 }
55
56 @Resource
57 private DataSource dataSource;
58
59 @Bean
60 public TransManager transManager() {
61 TransManager trans = new TransManager();
62 trans.setConnectionManager(connectionManager());
63 return trans;
64 }
65
66 @Bean
67 public ConnectionManager connectionManager() {
68 ConnectionManager conm = new ConnectionManager();
69 conm.setGenerateDdl(true);
70 conm.setShowSql(false);
71 conm.setInitConnect("set names utf8mb4");
72 conm.setDataSource(dataSource);
73 conm.setReadDataSources(Arrays.asList(dataSource));
74
75 return conm;
76 }
77
78 @Bean
79 public CorsFilter corsFilter() {
80 // 1.添加CORS配置信息
81 CorsConfiguration config = new CorsConfiguration();
82 // 放行哪些原始域
83 config.addAllowedOrigin("*");
84 // 是否发送Cookie信息
85 config.setAllowCredentials(true);
86 // 放行哪些原始域(请求方式)
87 config.addAllowedMethod("*");
88 // 放行哪些原始域(头部信息)
89 config.addAllowedHeader("*");
90 // 暴露哪些头部信息(因为跨域访问默认不能获取全部头部信息)
91 config.addExposedHeader(HttpHeaders.LOCATION);
92 config.setExposedHeaders(Arrays.asList("JSESSIONID", "SESSION", "token", HttpHeaders.LOCATION,
93 HttpHeaders.ACCEPT, HttpHeaders.ACCESS_CONTROL_ALLOW_HEADERS, HttpHeaders.ACCESS_CONTROL_EXPOSE_HEADERS,
94 HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN, HttpHeaders.COOKIE, HttpHeaders.SET_COOKIE,
95 HttpHeaders.SET_COOKIE2));
96 // 2.添加映射路径
97 UrlBasedCorsConfigurationSource configSource = new UrlBasedCorsConfigurationSource();
98 configSource.registerCorsConfiguration("/**", config);
99
100 // 3.返回新的CorsFilter.
101 return new CorsFilter(configSource);
102 }
103
104 }

1.3拦截器的应用场景

  拦截器的本质是面向切面编程(AOP),符合橫切关注点的功能都可以放在拦截器中来实现,主要的应用场景包括:

  1、登录验证,判断用户是否登录。

  2、权限验证,判断用户是否具有访问权限。

  3、日志记录,记录请求日志,以便统计请求访问量。

  4、处理cookie、本地化、国际化、主题等。

  5、性能监控,监控请求处理时长等。

  6、防止同一用户在短时间内重复请求接口

2、原理

2.1、工作原理

  拦截器不是Filter,却实现了filter的功能,其原理在于:

  所有的拦截器(Interceptor)和处理器(Handler)都注册在HandlerMapping中。

  Spring MVC中所有的请求都是由DispatcherServlet分发的。

  当请求进入DispatcherServlet.doDispatch()时候,首先会得到处理该请求的Handler(即Controller中对应的方法)以及所有拦截该请求的拦截器。拦截器就是在这里被调用开始工作的。
 
2.2、拦截器工作流程

  一个拦截器,只有preHandle方法返回true,postHandleafterCompletion才有可能被执行;如果preHandle方法返回false,则该拦截器的postHandleafterCompletion必然不会被执行。

假设我们有两个拦截器,例如叫Interceptor1和Interceptor2,当一个请求过来,正常的流程和中断的流程分别如下。

2.2.1正常流程

  注意两个拦截器在执行preHandle方法和执行postHandleafterCompletion方法时,顺序是颠倒的。

 1 Interceptor1.preHandle
2
3 Interceptor2.preHandle
4
5 //Controller处理请求
6
7 Interceptor2.postHandle
8
9 Interceptor1.postHandle
10
11 //渲染视图
12
13 Interceptor2.afterCompletion
14
15 Interceptor1.afterCompletion

2.2.2中断流程

  假设执行Interceptor2.preHandle中报错,那么流程被中断,之前被执行过的拦截器的afterCompletion仍然会执行。在本例中,即执行了Interceptor1.afterCompletion
1 Interceptor1.preHandle
2
3 Interceptor2.preHandle
4
5 //中间流程被中断,不再执行
6
7 Interceptor1.afterCompletion

2.3和Filter共存时的执行顺序

  拦截器是在DispatcherServlet这个servlet中执行的,因此所有的请求最先进入Filter,最后离开Filter。其顺序如下。

 1 Filter
2
3 Interceptor.preHandle
4
5 Handler
6
7 Interceptor.postHandle
8
9 Interceptor.afterCompletion
10
11 Filter

在SpringBoot项目中添加SpringMVC拦截器的更多相关文章

  1. Spring Boot项目中如何定制拦截器

    本文首发于个人网站:Spring Boot项目中如何定制拦截器 Servlet 过滤器属于Servlet API,和Spring关系不大.除了使用过滤器包装web请求,Spring MVC还提供Han ...

  2. 在SpringBoot项目中添加logback的MDC

    在SpringBoot项目中添加logback的MDC     先看下MDC是什么 Mapped Diagnostic Context,用于打LOG时跟踪一个“会话“.一个”事务“.举例,有一个web ...

  3. 在springboot项目中引入quartz任务调度器。

    quartz是一个非常强大的任务调度器.我们可能使用它来管理我们的项目,常见的是做业绩统计等等.当然它的功能远不止这些.我们在这里不介绍quartz的原理,下面讲讲如何在springboot中使用qu ...

  4. SpringBoot项目中,异常拦截

    SpringBoot自带异常拦截@ControllerAdvice 1.创建一个SellerExceptionHandler类打上@ControllerAdvice标签 @ControllerAdvi ...

  5. Springboot中SpringMvc拦截器配置与应用(实战)

    一.什么是拦截器,及其作用 拦截器(Interceptor): 用于在某个方法被访问之前进行拦截,然后在方法执行之前或之后加入某些操作,其实就是AOP的一种实现策略.它通过动态拦截Action调用的对 ...

  6. springboot(五).如何在springboot项目中使用拦截器

    在每个项目中,拦截器都是我们经常会去使用的东西,基本上任一一个项目都缺不了拦截器的使用. 如日志记录.登录验证,session验证等,都需要拦截器来拦截URL请求,那springboot中的拦截器是如 ...

  7. taotao用户登录springMVC拦截器的实现

    在springMVC中写拦截器,只需要两步: 一.写 java 拦截器类,实现 interceptor 拦截器接口. 二.在 springMVC 的xml配置文件中,配置我们创建的拦截器对象及其拦截目 ...

  8. 基于SpringMVC拦截器和注解实现controller中访问权限控制

    SpringMVC的拦截器HandlerInterceptorAdapter对应提供了三个preHandle,postHandle,afterCompletion方法. preHandle在业务处理器 ...

  9. SpringMVC之八:基于SpringMVC拦截器和注解实现controller中访问权限控制

    SpringMVC的拦截器HandlerInterceptorAdapter对应提供了三个preHandle,postHandle,afterCompletion方法. preHandle在业务处理器 ...

随机推荐

  1. 羽夏看Win系统内核——句柄表篇

    写在前面   此系列是本人一个字一个字码出来的,包括示例和实验截图.由于系统内核的复杂性,故可能有错误或者不全面的地方,如有错误,欢迎批评指正,本教程将会长期更新. 如有好的建议,欢迎反馈.码字不易, ...

  2. mac每次打开终端都需要source ~/.bashrc以及~/.bash_profile问题

    问题描述 在学习git的时候在~/.bashrc下面配置了git log命令的别名 #用于输出git提交日志 alias git-log='git log --pretty=oneline --all ...

  3. Java 重写paint绘图

    感谢原文:https://zhidao.baidu.com/question/260060153.html 这个方法需要注意的地方不多,也就是重写时,先调用 super.paint(g) 方法 pai ...

  4. iOS9新特性之常见关键字

    /* nullable:1.怎么使用(语法) 2.什么时候使用(作用) nullable作用:可能为空 nullable 语法1 @property (nonatomic, strong, nulla ...

  5. RPC原理及RPC实例分析(转)

    出处:https://my.oschina.net/hosee/blog/711632 在学校期间大家都写过不少程序,比如写个hello world服务类,然后本地调用下,如下所示.这些程序的特点是服 ...

  6. php7.3编译安装 支持微擎2.0

    再次整理   //一下配置在命令粘贴时注意句尾加 \ , 在 \ 后不能有空格,不然会自动执行,相当于回车./configure --prefix=/usr/local/php \ --with-co ...

  7. 把 Navigation Bar 下面那条线删掉的最简单的办法! — By: 昉

    系统默认的 Navigation Bar 下面一直有条线,翻尽了文档却没找到能把它弄走的相关接口,处女座的简直木法忍啊有木有!!!! 研究了一下navigationBar下的子视图,原来只需要几行代码 ...

  8. kali中安装arpspoof 报错

    情境今天在使用arpspoof这个命令的时候,提示没有命令找不到,此时就想着安装一下没想到,碰上kali源不支持的问题  解决所以,此时需要做的就是安装阿里云或者其他镜像  步骤1. vim /etc ...

  9. Java产生指定范围内的随机日期

    要想产生指定范围内的随机日期,首先我们要指定一个范围,那么我们可以通过SImpleDateFormat格式化日期,然后再通过parse()方法设置日期,返回一个Date类型的日期对象,再转化为时间戳( ...

  10. Large Sacle Distributed Deep Networks

    本文是谷歌发表在NeurIPS 2012上的一篇论文,主要讨论了在几万个CPU节点上训练大规模深度网络的问题,并提出了一个名为DistBelief的软件框架.在该框架下实现了两种大规模分布式训练算法: ...