JWT令牌和Filter过滤器以及Interceptor拦截器 总结

91 阅读6分钟

业务方法

  1. 参数校验
  2. 业务处理
  3. 数据封装

参数校验的方案有

1.Cookie:客户端会话技术

缺点:APP不能用、不能跨域

2.Session:服务端会话技术,底层基于Cookie

缺点:分布式环境Session不能共享、会损耗服务器的内存

3. 主流JWT令牌

  • 第一部分:Header(头), 记录令牌类型、签名算法等。 例如:{"alg":"HS256","type":"JWT"}

  • 第二部分:Payload(有效载荷),携带一些非敏感自定义信息、默认信息等。 例如:{"id":"1","username":"Tom"}

  • 第三部分:Signature(签名),防止Token被篡改、确保安全性。将header、payload,并加入指定秘钥,通过指定签名算法计算而来。

    • 其实在生成JWT令牌时,会对JSON格式的数据进行一次编码:进行base64编码
  • 自己写的方法代码

//Map<String,Object> claims=new HashMap<>();
  //  claims.put("username","wusong");
//生成令牌方法  key="179d63f1-32a7-4be3-821b-766cd0e908a4" 秘钥自己设置(非常重要,不要暴露)
public String getTokenJwt(Map<String,Object> claims ,String key){
    String JWT = Jwts.builder()
     .addClaims(claims)
     .setExpiration(new Date(System.currentTimeMillis() + 1000*60*60))
        .signWith(SignatureAlgorithm.HS256, key)
           .compact();
   return JWT;
}
//解析令牌获取信息方法 
public Map<String,Object>   parserToken( String JWT,String key){
    Claims claims= Jwts.parser()
       .setSigningKey(key)
          .parseClaimsJws(JWT).getBody();
   return claims
}

当然也可以引入依赖

<dependency>
    <groupId>io.jsonwebtoken</groupId>
    <artifactId>jjwt</artifactId>
    <version>0.9.1</version>
</dependency>

引入工具类

public class JwtUtils {

    private static String signKey = "SVRIRUlNQQ==";
    private static Long expire = 24 * 60 * 60 * 1000L;

    /**
     * 生成JWT令牌
     * @return
     */
    public static String generateJwt(Map<String,Object> claims){
        String jwt = Jwts.builder()
                .addClaims(claims)
                .signWith(SignatureAlgorithm.HS256, signKey)
                .setExpiration(new Date(System.currentTimeMillis() + expire))
                .compact();
        return jwt;
    }

    /**
     * 解析JWT令牌
     * @param jwt JWT令牌
     * @return JWT第二部分负载 payload 中存储的内容
     */
    public static Claims parseJWT(String jwt){
        Claims claims = Jwts.parser()
                .setSigningKey(signKey)
                .parseClaimsJws(jwt)
                .getBody();
        return claims;
    }
}

Filter过滤器

  • Filter是 JavaWeb三大组件(Servlet、Filter、Listener)之一。

  • 过滤器可以把对资源的请求拦截下来,从而实现一些特殊的功能

    • 使用了过滤器之后,要想访问web服务器上的资源,必须先经过滤器,过滤器处理完毕之后,才可以访问对应的资源。

过滤器的基本使用操作:

  • 第1步,定义过滤器 :1.定义一个类,实现 Filter 接口,并重写其所有方法。
  • 第2步,配置过滤器:Filter类上加 @WebFilter 注解,配置拦截资源的路径。 引导类上加 @ServletComponentScan 开启Servlet组件支持。

  • 1.init方法:过滤器的初始化方法。在web服务器启动的时候会自动的创建Filter过滤器对象,在创建过滤器对象的时候会自动调用init初始化方法,这个方法只会被调用一次。
    1. doFilter方法:这个方法是在每一次拦截到请求之后都会被调用,所以这个方法是会被调用多次的,每拦截到一次请求就会调用一次doFilter()方法。
    1. destroy方法: 是销毁的方法。当我们关闭服务器的时候,它会自动的调用销毁方法destroy,而这个销毁方法也只会被调用一次

执行流程:

过滤前的代码 ----> 放行(目标资源)---> 过滤后的代码

代码实现

@WebFilter(urlPatterns = "/*")
//配置过滤器要拦截的请求路径( /* 表示拦截浏览器的所有请求 )
//是数组可以添加多个路径
public class DemoFilter implements Filter {
    public void init(FilterConfig filterConfig) throws ServletException {
        System.out.println("init ...");
    }

    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain chain) throws IOException, ServletException {
        System.out.println("拦截到了请求...");
        chain.doFilter(request, response);`
    }

    public void destroy() {
        System.out.println("destroy ... ");
    }
}
@ServletComponentScan //开启对Servlet组件的支持
@SpringBootApplication
public class TliasManagementApplication {
    public static void main(String[] args) {
        SpringApplication.run(TliasManagementApplication.class, args);
    }
}

注意事项: 在过滤器Filter中,如果不执行放行操作,将无法访问后面的资源。

放行操作:chain.doFilter(request, response);

登录例子 实现登录校验

@WebFilter(urlPatterns = "/*")是数组可以添加多个路径
public class AuthorizedFilter implements Filter {
    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        HttpServletRequest servletRequest1= (HttpServletRequest) servletRequest;

        HttpServletResponse servletResponse1= (HttpServletResponse) servletResponse;
        String requestURI = servletRequest1.getRequestURI();
        //校验路径是登录则通过,进入路径内部进行密码验证
        if (requestURI.contains("/login")){
            filterChain.doFilter(servletRequest1,servletResponse);
            return;
        }
        //当路径不是登录则对令牌进行验证
        String token = servletRequest1.getHeader("token");
        if (StrUtil.isBlank(token)){
            servletResponse1.setStatus(HttpStatus.HTTP_UNAUTHORIZED);
            return;
        }
        try {
            Claims claims= (Claims) JwtUtils.parseJWT(token);
            Object id=claims.getClaim("id");
            System.out.println(id);
            filterChain.doFilter(servletRequest1,servletResponse1);
        } catch (Exception e) {
        servletResponse1.setStatus(HttpStatus.HTTP_UNAUTHORIZED);
        }

    }
}

拦截的路径

image.png 滤器链
指的是在一个web应用程序当中,可以配置多个过滤器,多个过滤器就形成了一个过滤器链。

链上的过滤器在执行的时候会一个一个的执行,会先执行第一个Filter,放行之后再来执行第二个Filter,如果执行到了最后一个过滤器放行之后,才会访问对应的web资源。

过滤器链上过滤器的执行顺序:注解配置的Filter,优先级是按照过滤器类名(字符串)的自然排序。 比如:

  • AbcFilter
  • DemoFilter

这两个过滤器来说,AbcFilter 会先执行,DemoFilter会后执行。

Interceptor拦截器

是Spring的组件,要实现HandlerInterceptor接口 实现步骤:

创建一个类,实现HandlerInterceptor接口,重写抽象方法

将拦截器交给Spring管理,类上加@Component

在写一个配置类,实现 WebMvcConfigurer接口,

并且在类上加注解@Configuration //表示当前是一个spring的配置类

重写 addInterceptors方法,在内部注册拦截器

执行流程:

preHandle 方法中的 ---> 放行(目标资源)---> postHandle方法 ---> afterCompletion方法

代码实现

@Component
public class DemoInterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        System.out.println("在handler执行之前执行");
        //ture 放行 false 拦截
        return true;
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        System.out.println("在handler只后执行,渲染视图之前执行");
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        System.out.println("在渲染视图之后执行,作一些释放资源的动作");
    }
}
@Configuration //表示当前是一个spring的配置类
public class WebConfig implements WebMvcConfigurer {
    @Autowired
    private DemoInterceptor demoInterceptor;
    @Override
  @Override public void addInterceptors(InterceptorRegistry registry) {
  //注册自定义拦截器对象 
  registry.addInterceptor(demoInterceptor) 
  .addPathPatterns("/**")//设置拦截器拦截的请求路径( /** 表示拦截所有请求) 
  .excludePathPatterns("/login");//设置不拦截的请求路径 } }
    }
    }

登录例子 实现登录校验

@Component
public class AuthorizeInterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
//校验路径是登录则通过,进入路径内部进行密码验证,可以不写,在配置文件可以调过路径
//        String requestURI = request.getRequestURI();
//        if (requestURI.contains("/login")) {
//            return true;
//        }
 //当路径不是登录则对令牌进行验证
        String token = request.getHeader("token");
        if (StrUtil.isBlank(token)) {
            response.setStatus(HttpStatus.HTTP_UNAUTHORIZED);
            return false;
        }

        try {
            Claims claims = JwtUtils.parseJWT(token);
            Object id = claims.get("id");
            System.out.println(id);
           return true;
        } catch (Exception e) {
            response.setStatus(HttpStatus.HTTP_UNAUTHORIZED);
            return false;
        }
    }}
@Configuration
public class WebConfig implements WebMvcConfigurer {
    @Autowired
    private AuthorizeInterceptor authorizeInterceptor;

    @Override
   public void addInterceptors(InterceptorRegistry registry){
        registry.addInterceptor(authorizeInterceptor)
                .excludePathPatterns("/login")
                .addPathPatterns("/**");
    }
    }

阻拦路径 image.png

目前请求的执行流程

请求 ---> Nginx ---> Tomcat ----> Filter ---> DispatcherServlet ----> Interceptor ---> Controller中的Handler

过滤器和拦截器之间的区别主要是两点:

-接口规范不同:过滤器需要实现Filter接口,而拦截器需要实现HandlerInterceptor接口。

-拦截范围不同:过滤器Filter会拦截所有的资源,而Interceptor只会拦截Spring环境中的资源.