拦截器Interceptor,是SpringMVC中的核心内容,利用spring的AOP(Aspect Oriented Programming, 面向切面编程)特性,可以很方便的对用户的业务代码进行横向抽取,根据具体业务需求对应用功能进行增强。
在SpringBoot中使用Interceptor,同时采用全注解开发,涉及到以下接口和类:
- HandlerInterceptor:处理器拦截器,handler就是处理器,在springboot web开发中,由控制器来处理web请求,因此handler具体指控制器
- 使用全注解开发,通过@Configuration注解,让一个java对象主任到IOC容器,并作为配置对象,这里的JavaConfig类相当于一个xml配置文件;
- 在以前的xml配置中
(1)通过引入一些标签进行配置,在JavaConfig中,通过继承一个类或者实现一个接口来实现配置,这里所继承的类、所实现的接口就相当于引入的标签;
(2)通过设置所引入标签的属性和值,可以实现个性化配置,在JavaConfig中通过覆盖类或者接口的方法来实现个性化配置。
1. 整体流程
实现一个拦截器,我们只需要做如下两步即可。
- 定义自己的拦截器:
实现HandlerInterceptor接口的方法,来自定义拦截器 - 注册拦截器:
JavaConfig配置类实现WebMvcConfigurer接口的addInterceptor方法来注册拦截器、
知道步骤以后我们就可以来实现一个简单的拦截器了。
2. 简单案例
2.1 拦截器的定义
public class LoginInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("执行了拦截器的preHandle方法");
return true;
}
}
2.2 注册拦截器
创建一个配置类实现WebMvcConfigurer
接口,然后在addInterceptors
方法中注册拦截器即可。
需要注意的是记得添加@Configuration
注解,将配置类交给spring管理。
在配置的时候我们可以放行不需要拦截的接口,同时也可以指定拦截的接口;通过addPathPatterns
和excludePathPatterns
两个方法实现。
@Configuration
public class MyConfig implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
LoginInterceptor loginInterceptor = new LoginInterceptor();
String[] path = {"/user/**"}; // 如果拦截全部可以设置为 /**
String[] excludePath = {"/user/login"}; // 不需要拦截的接口路径
registry.addInterceptor(loginInterceptor).addPathPatterns(path).excludePathPatterns(excludePath);
}
}
2.3 控制器
拦截器注册完成后,我们就可以编写接口来进行测试了。
@RestController
public class IndexController {
@RequestMapping("/user/account")
public String userAccount() {
return "/user/account";
}
@RequestMapping("/user/login")
public String userLogin() {
return "/user/login";
}
}
分别访问两个接口,可以发现访问/user/account
接口的时候被成功拦截了,至此我们就实现了一个简单的拦截器功能了。
3. 处理指定接口
我们也可以在拦截器里面进行一些业务逻辑的处理,比如放行部分接口、捕捉指定接口等。
public class LoginInterceptor implements HandlerInterceptor {
// 处理指定接口
private static final String TOKEN_LOGIN = "/user/token";
private static final String USER_NO_URL = "/user/no"; // 拦截不存在的接口路径
// 不要要校验的接口
private static final List<String> NO_NEED_LOGIN = new ArrayList<>(Arrays.asList("/user/health", "/logout"));
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
// 获取访问的接口名
String uri = request.getRequestURI();
String contentPath = request.getContextPath();
if (uri.startsWith(contentPath + "/")) {
// 去掉contextPath
uri = uri.replace(contentPath + "/", "/");
}
if (NO_NEED_LOGIN.contains(uri)) {
// log.info("资源{}不需要登录,直接访问", uri);
System.out.println("这个接口不需要验证,直接放行.");
return true;
}
if (uri.equals(TOKEN_LOGIN)) {
response.setStatus(200);
response.setCharacterEncoding("utf-8");
response.getWriter().println("处理指定接口,并返回数据信息."); // 可以返回相关json处理
return false;
}
if (uri.equals(USER_NO_URL)) {
response.setStatus(500);
response.setCharacterEncoding("utf-8");
response.getWriter().println("拦截的是未定义的接口."); // 可以返回相关json处理
return false;
}
// 可以对每个接口进行身份校验 或者 编写相关业务逻辑
System.out.println("执行了拦截器的preHandle方法");
return true;
}
}
在拦截器里面我们也可以拦截用户信息,然后在之后的接口操作中使用,这部分的内容可以查看另外一篇文章。