1 SpringMVC中的拦截器

1.1 拦截器的作用

Spring MVC 的处理器拦截器类似于 Servlet 开发中的过滤器 Filter,用于对处理器进行预处理和后处理。
用户可以自己定义一些拦截器来实现特定的功能。
谈到拦截器,还要向大家提一个词——拦截器链(Interceptor Chain)。拦截器链就是将拦截器按一定的顺序联结成一条链。在访问被拦截的方法或字段时,拦截器链中的拦截器就会按其之前定义的顺序被调用。
说到这里,可能大家脑海中有了一个疑问,这不是我们之前学的过滤器吗?是的它和过滤器是有几分相似,但是也有区别,接下来我们就来说说他们的区别:
过滤器是 servlet 规范中的一部分,任何 java web 工程都可以使用。
拦截器是 SpringMVC 框架自己的,只有使用了 SpringMVC 框架的工程才能用。
过滤器在 url-pattern 中配置了/之后,可以对所有要访问的资源拦截。
拦截器它是只会拦截访问的控制器(controller)方法,如果访问的是 jsp,html,css,image 或者 js 是不会进行拦截的。
它也是 AOP 思想的具体应用。
我们要想自定义拦截器, 要求必须实现:HandlerInterceptor接口,或者继承Spring为我们提供了org.springframework.web.servlet.handler.HandlerInterceptorAdapter这个适配器
过滤器能做拦截器所有能做的事,但是过滤器能做的事,拦截器可能就处理不了
*图片.png

2 自定义拦截器

2.1 编写一个类实现HandlerInterceptor接口

重写接口中的方法,HandlerInterceptor接口中给出了默认的方法实现,如果我们不想写自己的实现,可以使用默认的实现,我们也可以编写自己的实现,
接口中提供了三个方法
(1) preHandle:这个是用来预处理的,会在controller控制器中的方法执行前执行,
(2) postHandle:这个方法是用来后置处理的,会在controller控制器中的方法执行完后执行
(3) afterCompletion:这个方法用来做最终的处理,就是controller控制器,jsp等页面都执行完了再执行

IDEA重写方法小技巧:教程中因为没改IDEA的输入配置,用的就是IDEA的默认输入方式,包括快捷键啥的,而我改为了eclipse模式,所以教程中的有些快捷键并不好使(比如重写方法IDEA快捷键CTRL + O),但是别忘记了强大的alt + insert,这个快捷键,就是为快速生成提供的,可以提供很多快速生成

  1. public class CustomerInterceptor implements HandlerInterceptor {
  2. private static final Logger logger = LoggerFactory.getLogger(CustomerInterceptor.class);
  3. /**
  4. *预处理,controller方法执行前
  5. *return true,放行,执行下一个拦截器,如果没有执行controller中的方法
  6. * return false,不放行,
  7. */
  8. @Override
  9. public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
  10. if (logger.isInfoEnabled()) {
  11. logger.info("自定义拦截器中的预处理方法执行了,handler:{}",handler.toString());
  12. }
  13. return true;
  14. }
  15. }

2.2配置拦截器

  1. <!--配置springMVC拦截器-->
  2. <mvc:interceptors>
  3. <!--配置具体的哪一个拦截器-->
  4. <mvc:interceptor>
  5. <!--配置要拦截的具体方法-->
  6. <mvc:mapping path="/interceptor/*"/>
  7. <!--配置拦截器不拦截的方法,一般mappingexclude-mapping配置一个就好
  8. <mvc:exclude-mapping path=""/>-->
  9. <!--配置编写的拦截器-->
  10. <bean class="com.bozhao.interceptor.CustomerInterceptor"/>
  11. </mvc:interceptor>
  12. </mvc:interceptors>

2.3运行结果

图片.png

3 使用拦截器方式实现token鉴权

如果我们每个方法都去写一段代码,冗余度太高,不利于维护,那如何做使我们的代码看起来更清爽呢?我们可以将这段代码放入拦截器去实现,这也是AOP思想的实现

3.1 添加拦截器

Spring为我们提供了org.springframework.web.servlet.handler.HandlerInterceptorAdapter这个适配器,继承此类,可以非常方便的实现自己的拦截器。他有三个方法:分别实现预处理、后处理(调用了Service并返回ModelAndView,但未进行页面渲染)、返回处理(已经渲染了页面)在preHandle中,可以进行编码、安全控制等处理;在postHandle中,有机会修改ModelAndView;在afterCompletion中,可以根据ex是否为null判断是否发生了异常,进行日志记录。
(1)创建拦截器类。

  1. package com.tensquare.user.interceptor;
  2. import io.jsonwebtoken.Claims;
  3. import org.apache.commons.lang3.StringUtils;
  4. import org.slf4j.Logger;
  5. import org.slf4j.LoggerFactory;
  6. import org.springframework.beans.factory.annotation.Autowired;
  7. import org.springframework.stereotype.Component;
  8. import org.springframework.web.servlet.HandlerInterceptor;
  9. import util.JwtUtil;
  10. import javax.servlet.http.HttpServletRequest;
  11. import javax.servlet.http.HttpServletResponse;
  12. /**
  13. * @author: Luck-zb
  14. * description:Jwt拦截器
  15. * Date:2021/1/31 - 12:57
  16. */
  17. @Component // 交给spring容器,相当于xml配置方式配置拦截器
  18. public class JwtInterceptor implements HandlerInterceptor {
  19. private static Logger logger = LoggerFactory.getLogger(JwtInterceptor.class);
  20. @Autowired
  21. private JwtUtil jwtUtil;
  22. public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
  23. // 获取到token的请求头
  24. String header = request.getHeader("Authorization");
  25. if (StringUtils.isNotBlank(header)) {
  26. if (header.startsWith("Bearer ")) {
  27. // 得到当前的令牌token
  28. String token = header.substring(7);
  29. if (logger.isInfoEnabled()) {
  30. logger.info("当前令牌token: {}", token);
  31. }
  32. // 解析token
  33. try {
  34. Claims claims = jwtUtil.parseJWT(token);
  35. // 得到当前用户的角色
  36. String roles = (String) claims.get("roles");
  37. if (StringUtils.isNotBlank(roles)) {
  38. // 把不同角色的用户解析后的token request域对象中
  39. if("user".equals(roles)) {
  40. request.setAttribute("user_claims", claims);
  41. }
  42. if ("admin".equals(roles)) {
  43. request.setAttribute("admin_claims", claims);
  44. }
  45. }
  46. } catch (Exception e) {
  47. e.printStackTrace();
  48. }
  49. }
  50. }
  51. return true;
  52. }
  53. }

(2)配置拦截器类

  1. package com.tensquare.user.config;
  2. import com.tensquare.user.interceptor.JwtInterceptor;
  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.WebMvcConfigurationSupport;
  7. /**
  8. * @author: Luck-zb
  9. * description:springMVC配置类,WebMvcConfigurationSupport表示所有springMVC的配置文件,都可以选择此类 * 进行配置
  10. * Date:2021/1/31 - 12:59
  11. */
  12. @Configuration
  13. public class SpringMvcConfig extends WebMvcConfigurationSupport{
  14. @Autowired
  15. private JwtInterceptor jwtInterceptor;
  16. /**
  17. * Override this method to add Spring MVC interceptors for
  18. * pre- and post-processing of controller invocation.
  19. * @see InterceptorRegistry
  20. */
  21. protected void addInterceptors(InterceptorRegistry registry) {
  22. // 指定拦截器对象和该拦截器要拦截的路径
  23. registry.addInterceptor(jwtInterceptor)
  24. .addPathPatterns("/**")
  25. .excludePathPatterns("/**/login/**"); // 根据请求url放行登录请求
  26. }
  27. }

3.2 拦截器验证token

重写preHandle()方法

package com.tensquare.user.interceptor;

import io.jsonwebtoken.Claims;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import util.JwtUtil;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
 * @author: Luck-zb
 * description:Jwt拦截器
 * Date:2021/1/31 - 12:57
 */
@Component
public class JwtInterceptor implements HandlerInterceptor {

    private static Logger logger = LoggerFactory.getLogger(JwtInterceptor.class);

    @Autowired
    private JwtUtil jwtUtil;

    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {

        // 获取到token的请求头
        String header = request.getHeader("Authorization");
        if (StringUtils.isNotBlank(header)) {
            if (header.startsWith("Bearer ")) {
                // 得到当前的令牌token
                String token = header.substring(7);
                if (logger.isInfoEnabled()) {
                    logger.info("当前令牌token: {}", token);
                }
                // 解析token
                try {
                    Claims claims = jwtUtil.parseJWT(token);
                    // 得到当前用户的角色
                    String roles = (String) claims.get("roles");
                    if (StringUtils.isNotBlank(roles)) {
                        // 把不同角色的用户解析后的token request域对象中
                        if("user".equals(roles)) {
                            request.setAttribute("user_claims", claims);
                        }
                        if ("admin".equals(roles)) {
                            request.setAttribute("admin_claims", claims);
                        }
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }

        return true;
    }
}