一、什么是拦截器,及其作用

拦截器(Interceptor): 用于在某个方法被访问之前进行拦截,然后在方法执行之前或之后加入某些操作,其实就是AOP的一种实现策略。它通过动态拦截Action调用的对象,允许开发者定义在一个action执行的前后执行的代码,也可以在一个action执行前阻止其执行。同时也是提供了一种可以提取action中可重用的部分的方式。

拦截器的使用场景越来越多,尤其是面向切片编程流行之后。那通常拦截器可以做什么呢?
之前我们在Agent介绍中,提到过统计函数的调用耗时。这个思路其实和AOP的环绕增强如出一辙。

那一般来说,场景如下:

1、日志记录:记录请求信息的日志,以便进行信息监控、信息统计、计算PV(Page View)等。

2、权限检查:如登录检测,进入处理器检测检测是否登录,如果没有直接返回到登录页面;

3、函数增强:比如对一个函数进行参数检查,或者结果过滤等。甚至可以对函数就行权限认证。

4、性能监控:统计函数性能,有时候系统在某段时间莫名其妙的慢,可以通过拦截器在进入处理器之前记录开始时间,在处理完后记录结束时间,从而得到该请求的处理时间(如果有反向代理,如apache可以自动记录);

5、通用行为:读取cookie得到用户信息并将用户对象放入请求,从而方便后续流程使用,还有如提取Locale、Theme信息等,只要是多个处理器都需要的即可使用拦截器实现。

6、OpenSessionInView:如Hibernate,在进入处理器打开Session,在完成后关闭Session。

…………本质也是AOP(面向切面编程),也就是说符合横切关注点的所有功能都可以放入拦截器实现。

二、springmvc拦截器相关接口和实现类

package org.springframework.web.servlet;
public interface HandlerInterceptor {
boolean preHandle(
HttpServletRequest request, HttpServletResponse response,
Object handler)
throws Exception; void postHandle(
HttpServletRequest request, HttpServletResponse response,
Object handler, ModelAndView modelAndView)
throws Exception; void afterCompletion(
HttpServletRequest request, HttpServletResponse response,
Object handler, Exception ex)
throws Exception;
}
public interface AsyncHandlerInterceptor extends HandlerInterceptor {

    void afterConcurrentHandlingStarted(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception; }
public abstract class HandlerInterceptorAdapter implements AsyncHandlerInterceptor {

    // 在目标方法执行前执行
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
return true;
} // 在目标方法执行后执行,但在请求返回前,我们仍然可以对 ModelAndView进行修改
@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 {} // 用来处理异步请求, 当Controller中有异步请求方法的时候会触发该方法
@Override
public void afterConcurrentHandlingStarted(
HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {}
}

三、如何配置拦截器

1、springboot项目中如何配置拦截器

实现自定义拦截器只需要3步: 
1、创建我们自己的拦截器类并实现 HandlerInterceptor 接口。 
2、创建一个Java类继承WebMvcConfigurerAdapter,并重写 addInterceptors 方法。 
3、实例化我们自定义的拦截器,然后将对像手动添加到拦截器链中(在addInterceptors方法中添加)。

package com.springboot.study.interceptors;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse; import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView; public class MyInterceptor1 implements HandlerInterceptor{ @Override
public void afterCompletion(HttpServletRequest arg0, HttpServletResponse arg1, Object arg2, Exception arg3)
throws Exception {
System.out.println("=====>(1)在整个请求之后调用,即在dispatcherServlet渲染了对应的视图之后(主要是进行资源清理工作)");
} @Override
public void postHandle(HttpServletRequest arg0, HttpServletResponse arg1, Object arg2, ModelAndView arg3)
throws Exception {
System.out.println("=====>(1)在请求处理之后调用,即在controller方法执行之后调用");
} @Override
public boolean preHandle(HttpServletRequest arg0, HttpServletResponse arg1, Object arg2) throws Exception {
System.out.println("=====>(1)在请求处理之前调用,即在Controller方法调用之前!");
return true;//只有返回true才会往下执行,返回FALSE的话就会取消当前请求
} }
package com.springboot.study.interceptors;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse; import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView; public class MyInterceptor2 implements HandlerInterceptor{ @Override
public void afterCompletion(HttpServletRequest arg0, HttpServletResponse arg1, Object arg2, Exception arg3)
throws Exception {
System.out.println("=====>(2)在整个请求之后调用,即在dispatcherServlet渲染了对应的视图之后(主要是进行资源清理工作)");
} @Override
public void postHandle(HttpServletRequest arg0, HttpServletResponse arg1, Object arg2, ModelAndView arg3)
throws Exception {
System.out.println("=====>(2)在请求处理之后调用,即在controller方法执行之后调用");
} @Override
public boolean preHandle(HttpServletRequest arg0, HttpServletResponse arg1, Object arg2) throws Exception {
System.out.println("=====>(2)在请求处理之前调用,即在Controller方法调用之前!");
return true;//只有返回true才会往下执行,返回FALSE的话就会取消当前请求
} }
package com.springboot.study.controller;

import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController; @RestController
public class MyController { @RequestMapping("/index")
public String index(){
return "hello!";
} }
package com.springboot.study.config;

import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter; import com.springboot.study.interceptors.MyInterceptor1;
import com.springboot.study.interceptors.MyInterceptor2; @Configuration
public class MyWebAppConfigurer extends WebMvcConfigurerAdapter{ @Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new MyInterceptor1()).addPathPatterns("/**");
registry.addInterceptor(new MyInterceptor2()).addPathPatterns("/**");
super.addInterceptors(registry);
}
}
运行结果:
=====>(1)在请求处理之前调用,即在Controller方法调用之前!
=====>(2)在请求处理之前调用,即在Controller方法调用之前!
=====>(2)在请求处理之后调用,即在controller方法执行之后调用
=====>(1)在请求处理之后调用,即在controller方法执行之后调用
=====>(2)在整个请求之后调用,即在dispatcherServlet渲染了对应的视图之后(主要是进行资源清理工作)
=====>(1)在整个请求之后调用,即在dispatcherServlet渲染了对应的视图之后(主要是进行资源清理工作)

四、实例

自定义权限注解

定义一个@interface

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Access { String[] value() default {}; String[] authorities() default {}; String[] roles() default {}; }

@Target注解是标注这个类它可以标注的位置:
常用的元素类型(ElementType):

public enum ElementType {
/** Class, interface (including annotation type), or enum declaration */
// TYPE类型可以声明在类上或枚举上或者是注解上
TYPE,
/** Field declaration (includes enum constants) */
// FIELD声明在字段上
FIELD,
/** Method declaration */
// 声明在方法上
METHOD,
/** Formal parameter declaration */
// 声明在形参列表中
PARAMETER,
/** Constructor declaration */
// 声明在构造方法上
CONSTRUCTOR,
/** Local variable declaration */
// 声明在局部变量上
LOCAL_VARIABLE,
/** Annotation type declaration */
ANNOTATION_TYPE,
/** Package declaration */
PACKAGE,
/**
* Type parameter declaration
*
* @since 1.8
*/
TYPE_PARAMETER,
/**
* Use of a type
*
* @since 1.8
*/
TYPE_USE
}

@Retention注解表示的是本注解(标注这个注解的注解保留时期)

public enum RetentionPolicy {
/**
* Annotations are to be discarded by the compiler.
*/
// 源代码时期
SOURCE,
/**
* Annotations are to be recorded in the class file by the compiler
* but need not be retained by the VM at run time. This is the default
* behavior.
*/
// 字节码时期, 编译之后
CLASS,
/**
* Annotations are to be recorded in the class file by the compiler and
* retained by the VM at run time, so they may be read reflectively.
*
* @see java.lang.reflect.AnnotatedElement
*/
// 运行时期, 也就是一直保留, 通常也都用这个
RUNTIME
}

@Documented是否生成文档的标注, 也就是生成接口文档是, 是否生成注解文档

注解说完了, 下面需要到对应的controller的方法中取添加注解, 配置该方法允许的权限

在方法上配置权限

@RestController
public class HelloController { @RequestMapping(value = "/admin", produces = MediaType.APPLICATION_JSON_UTF8_VALUE, method = RequestMethod.GET)
// 配置注解权限, 允许身份为admin, 或者说允许权限为admin的人访问
@Access(authorities = {"admin"})
public String hello() {
return "Hello, admin";
}
}

编写权限拦截逻辑

// 自定义一个权限拦截器, 继承HandlerInterceptorAdapter类
public class AuthenticationInterceptor extends HandlerInterceptorAdapter { // 在调用方法之前执行拦截
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
// 将handler强转为HandlerMethod, 前面已经证实这个handler就是HandlerMethod
HandlerMethod handlerMethod = (HandlerMethod) handler;
// 从方法处理器中获取出要调用的方法
Method method = handlerMethod.getMethod();
// 获取出方法上的Access注解
Access access = method.getAnnotation(Access.class);
if (access == null) {
// 如果注解为null, 说明不需要拦截, 直接放过
return true;
}
if (access.authorities().length > 0) {
// 如果权限配置不为空, 则取出配置值
String[] authorities = access.authorities();
Set<String> authSet = new HashSet<>();
for (String authority : authorities) {
// 将权限加入一个set集合中
authSet.add(authority);
}
// 这里我为了方便是直接参数传入权限, 在实际操作中应该是从参数中获取用户Id
// 到数据库权限表中查询用户拥有的权限集合, 与set集合中的权限进行对比完成权限校验
String role = request.getParameter("role");
if (StringUtils.isNotBlank(role)) {
if (authSet.contains(role)) {
// 校验通过返回true, 否则拦截请求
return true;
}
}
}
// 拦截之后应该返回公共结果, 这里没做处理
return false;
} }

拦截器详解源码地址:https://www.cnblogs.com/fangjian0423/p/springMVC-interceptor.html

Springboot中SpringMvc拦截器配置与应用(实战)的更多相关文章

  1. springmvc以及springboot中的拦截器配置

    拦截器两种实现   如果不同的controller中都需要拦截器,不能使用相同的拦截器,因为拦截器不能跨controller,这个时候只能为不同的controller配置不同的拦截器,每一个拦截器只能 ...

  2. 在springboot中使用拦截器

    在springMVC中可以实现拦截器,是通过实现HandlerInterceptor接口,然后在springmvc-web.xml中配置就可以使用拦截器了.在springboot中拦截器也是一样的思想 ...

  3. springboot中使用拦截器

    5.1 回顾SpringMVC使用拦截器步骤 自定义拦截器类,实现HandlerInterceptor接口 注册拦截器类 5.2 Spring Boot使用拦截器步骤 5.2.1        按照S ...

  4. SpringBoot中SpringMVC的自动配置以及扩展

    一.问题引入 我们在SSM中使用SpringMVC的时候,需要由我们自己写SpringMVC的配置文件,需要用到什么就要自己配什么,配置起来也特别的麻烦.我们使用SpringBoot的时候没有进行配置 ...

  5. springboot中使用拦截器、监听器、过滤器

     拦截器.过滤器.监听器在web项目中很常见,这里对springboot中怎么去使用做一个总结. 1. 拦截器(Interceptor)   我们需要对一个类实现HandlerInterceptor接 ...

  6. SpringMVC拦截器配置

    1.首先在springmvc.xml中添加配置 <mvc:interceptors> <mvc:interceptor> <mvc:mapping path=" ...

  7. 解决 Springboot中Interceptor拦截器中依赖注入失败

    问题: 在Springboot拦截器Interceptor中使用@Resource依赖注入时,发现运行的时候被注解的对象居然是null,没被注入进去 原配置为: @Configurationpubli ...

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

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

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

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

随机推荐

  1. [Maven]How do I tell Maven to use the latest version of a dependency?

    Link: http://stackoverflow.com/questions/30571/how-do-i-tell-maven-to-use-the-latest-version-of-a-de ...

  2. 简单的Web日志分析脚本

    前言 长话短说,事情的起因是这样的,由于工作原因需要分析网站日志,服务器是windows,iis日志,在网上找了找,github找了找,居然没找到,看来只有自己动手丰衣足食. 那么分析方法我大致可分为 ...

  3. JS电话、手机号码验证

    function isTelephone(inpurStr) {            var partten = /^0(([1,2]\d)|([3-9]\d{2}))-\d{7,8}$/;     ...

  4. 原 tomcat的server.xml配置文件中三个端口的作用

    以Tomcat7.0为例, 在安装目录下. conf/server.xml 中可以配置三个端口号, 如果使用多个tomcat 是需要配置这三个. 该Connector 用于监听请求. protocol ...

  5. Lonsdor K518ISE Key Programmer Review

    Lonsdor K518ISE key programmer is the latest version of Lonsdor, with wider vehicle coverage in key ...

  6. SSMS 远程连接SERVER 设置 - Unable to connect to SQL Server instance remotely

    问题描述: 新装了一台SERVER,在SERVER本地打开SSMS链接sever,一且正常.但是用我自己local去链接的时候出现以下错误. A network-related or instance ...

  7. 20篇关于商品管理系统和uml技术的相关文献

    1.基于UML技术的商品管理系统设计与实现 2.UML技术在行业资源平台系统建模中的应用 3.基于JSP的商品信息管理系统设计与开发 4.基于UML技术的客户关系管理系统实现 5.商品管理系统 6.基 ...

  8. Tree UVA - 548(二叉树递归遍历)

    题目链接:https://vjudge.net/problem/UVA-548 题目大意:给一颗点带权(权值各不相同,都是小于10000的正整数)的二叉树的中序遍历和后序遍历,找一个叶子结点使得它到根 ...

  9. Kudu 常见的几个应用场景

    不多说,直接上干货! Kudu 常见的几个应用场景 实时更新的应用.刚刚到达的数据就马上要被终端用户使用访问到. 时间序列相关的应用,需要同时支持: 根据海量历史数据查询. 必须非常快地返回关于单个实 ...

  10. DEDE文章列表加上序号效果

    在文章列表上面加上序号列表的形式,使得文章列表表现得没那么单调,更加丰富一点. {dede:arclist orderby=pubdate type='commend.' titlelen='26' ...