interceptor - 图1

应用场景

1、登录验证

以后台管理系统为例,大部分接口都应该要求做登录验证。如果每个方法里都加上登录状态检查,那未免太麻烦了,而且也不利于维护。应当把登录验证这个动作封装起来,以便复用。

2、数据统计 / 监控

比如使用 redis 统计指定页面的访问量等

如何使用拦截器?

以登录拦截器为例

控制器

先写一个测试的方法:

  1. package com.example.boot.controller;
  2. import lombok.extern.slf4j.Slf4j;
  3. import org.springframework.web.bind.annotation.RequestMapping;
  4. import org.springframework.web.bind.annotation.RestController;
  5. import java.util.HashMap;
  6. import java.util.Map;
  7. @RestController
  8. @Slf4j
  9. public class TestController {
  10. @RequestMapping("/test/index")
  11. public Map index() {
  12. Map<String, Object> map = new HashMap<>();
  13. map.put("code", 1);
  14. map.put("msg", "ok");
  15. map.put("data", null);
  16. return map;
  17. }
  18. }

image.png
ok,能正常访问测试接口了。

1、编写拦截器类

实现 HandlerInterceptor 接口,并使用 @Component 注解自动注入到容器中。

LoginInterceptor

  1. package com.example.boot.interceptor;
  2. import lombok.extern.slf4j.Slf4j;
  3. import org.springframework.stereotype.Component;
  4. import org.springframework.web.servlet.HandlerInterceptor;
  5. import org.springframework.web.servlet.ModelAndView;
  6. import javax.servlet.http.Cookie;
  7. import javax.servlet.http.HttpServletRequest;
  8. import javax.servlet.http.HttpServletResponse;
  9. import javax.servlet.http.HttpSession;
  10. import java.util.Enumeration;
  11. import java.util.Map;
  12. @Component
  13. @Slf4j
  14. public class LoginInterceptor implements HandlerInterceptor {
  15. @Override
  16. public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
  17. log.info("LoginInterceptor -> preHandle 执行了");
  18. // HttpSession session = request.getSession();
  19. // Map<String, String[]> parameterMap = request.getParameterMap();
  20. // Enumeration<String> parameterNames = request.getParameterNames();
  21. // url
  22. String scheme = request.getScheme();
  23. String serverName = request.getServerName();
  24. int serverPort = request.getServerPort();
  25. String requestURI = request.getRequestURI();
  26. StringBuffer requestURL = request.getRequestURL();
  27. log.info("LoginInterceptor -> preHandle -> scheme: {}", scheme);
  28. log.info("LoginInterceptor -> preHandle -> serverName: {}", serverName);
  29. log.info("LoginInterceptor -> preHandle -> serverPort: {}", serverPort);
  30. log.info("LoginInterceptor -> preHandle -> requestURI: {}", requestURI);
  31. log.info("LoginInterceptor -> preHandle -> requestURL: {}", requestURL);
  32. // header
  33. String userAgentHeader = request.getHeader("User-Agent");
  34. log.info("LoginInterceptor -> preHandle -> userAgentHeader: {}", userAgentHeader);
  35. Enumeration<String> headerNames = request.getHeaderNames();
  36. while (headerNames.hasMoreElements()) {
  37. String name = headerNames.nextElement();
  38. //通过请求头的名称获取请求头的值
  39. String value = request.getHeader(name);
  40. log.info("LoginInterceptor -> preHandle -> userAgentHeaders: {} -> {}", name, value);
  41. }
  42. // cookie
  43. Cookie[] cookies = request.getCookies();
  44. for (Cookie cookie : cookies) {
  45. log.info("LoginInterceptor -> preHandle -> cookies: {} -> {}", cookie.getName(), cookie.getValue());
  46. }
  47. return true;
  48. }
  49. @Override
  50. public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
  51. log.info("LoginInterceptor -> postHandle 执行了");
  52. }
  53. @Override
  54. public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
  55. log.info("LoginInterceptor -> afterCompletion 执行了");
  56. }
  57. }

2、将拦截器注册到容器中,指定拦截规则

新建一个配置类,使用 @Autowired 注解将拦截器自动装配到配置类中。

实现 WebMvcConfigurer 接口的 addInterceptors 方法。

MyWebMvcConfig

  1. package com.example.boot.config;
  2. import com.example.boot.interceptor.LoginInterceptor;
  3. import org.springframework.beans.factory.annotation.Autowired;
  4. import org.springframework.context.annotation.Configuration;
  5. import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
  6. import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
  7. @Configuration
  8. public class MyWebMvcConfig implements WebMvcConfigurer {
  9. @Autowired
  10. LoginInterceptor loginInterceptor;
  11. @Override
  12. public void addInterceptors(InterceptorRegistry registry) {
  13. registry.addInterceptor(loginInterceptor)
  14. .addPathPatterns("/**")
  15. .excludePathPatterns(
  16. "/css/**",
  17. "/js/**",
  18. "/images/**"
  19. );
  20. }
  21. }

拦截所有请求
addPathPatterns -> /**

放行下列请求
excludePathPatterns -> “”,””

3、测试效果

image.png
image.png

应该注意哪些东西?

1、公共规则抽取

如果拦截器数量较多,应当把公共规则抽象出来。

  1. public void addInterceptors(InterceptorRegistry registry) {
  2. // 公共拦截规则
  3. String[] PublicAddPathPatterns = {
  4. "/**"
  5. };
  6. // 公共放行路径规则
  7. String[] PublicExcludePathPatterns = {
  8. "/static/**",
  9. "/css/**",
  10. "/js/**",
  11. "/images/**"
  12. };
  13. registry.addInterceptor(loginInterceptor)
  14. .addPathPatterns(PublicAddPathPatterns)
  15. .excludePathPatterns(PublicExcludePathPatterns);
  16. registry.addInterceptor(login2Interceptor)
  17. .addPathPatterns(PublicAddPathPatterns)
  18. .excludePathPatterns(PublicExcludePathPatterns);
  19. }

2、不要使用 new 的方式手动创建拦截器实例

如果拦截器内使用了 @Autowired 注解自动装配,那么 new 出来的拦截器中,@Autowired 注解将会失效。

推荐在配置类中,使用 @Autowired 注解自动装配拦截器,而不是 new 的方式。

Reference

1、Springboot过滤器和拦截器详解及使用场景:https://zhuanlan.zhihu.com/p/340397290