当前位置:   article > 正文

JWT令牌、过滤器Filter、拦截器Interceptor

JWT令牌、过滤器Filter、拦截器Interceptor

目录

JWT令牌

简介

JWT生成

解析JWT 

登陆后下发令牌 

过滤器(Filter)

Filter快速入门 

Filter拦截路径

过滤器链

登录校验Filter-流程

拦截器(Interceptor)

Interceptor 快速入门

拦截路径 

登录校验流程 



JWT令牌

简介

全称:JSON Web Token(https://iwt.io/)
定义了一种简洁的、自包含的格式,用于在通信双方以json数据格式安全的传输信息。由于数字签名的存在,这些信息是可靠的。

组成:
第一部分:Header(头),记录令牌类型、签名算法等。例如:{"alg":"HS256","type":"JWT"}
第二部分:Payload(有效载荷),携带一些自定义信息、默认信息等。例如 {"id":"1","username":"Tom"}
第三部分:Signature(签名),防止Token被篡改、确保安全性。将header、pavload,并加入指定秘钥,通过指定签名算法计算而来。

场景:登录认证,
登录成功后,生成令牌
后续每个请求,都要携带JWT令牌,系统在每次请求处理之前,先校验令牌,通过后,再处理 

JWT生成

在pom.xml 导入依赖

  1. <dependency>
  2. <groupId>io.jsonwebtoken</groupId>
  3. <artifactId>jjwt</artifactId>
  4. <version>0.9.1</version>
  5. </dependency>

测试 

  1. //生成JWT
  2. @Test
  3. public void testGenJwt(){
  4. Map<String,Object>claims=new HashMap<>();
  5. claims.put("id",1);
  6. claims.put("name","Tom");
  7. String jwt = Jwts.builder()
  8. .setClaims(claims)//自定义内容
  9. .signWith(SignatureAlgorithm.HS256, "itking")//签名算法
  10. .setExpiration(new Date(System.currentTimeMillis() + 3600 * 1000))//设置有效期为一个小时
  11. .compact();
  12. System.out.println(jwt);
  13. }

 返回生成的JWT令牌

解析JWT 

  1. @Test
  2. public void testParseJwt(){
  3. Claims claims = Jwts.parser()
  4. .setSigningKey("itking")//指定签名密钥
  5. .parseClaimsJws("eyJhbGciOiJIUzI1NiJ9.eyJuYW1lIjoiVG9tIiwiaWQiOjEsImV4cCI6MTcxODM2ODM3N30.iRwBkPs6CGuUrZpz1YBjh0QubkpEnY6ArxddkVlMclI")//解析令牌
  6. .getBody();
  7. System.out.println(claims);
  8. }

 

JWT校验时使用的签名秘钥,必须和生成IWT令牌时使用的秘钥是配套的

如果JWT令牌解析校验时报错,则说明JWT令牌被篡改或失效了,令牌非法 

登陆后下发令牌 

令牌生成:登录成功后,生成JWT令牌,并返回给前端,
令牌校验:在请求到达服务端后,对令牌进行统一拦截、校验 

需要附加一个工具类JwtUtils类

  1. package com.example.Utils;
  2. import io.jsonwebtoken.Claims;
  3. import io.jsonwebtoken.Jwts;
  4. import io.jsonwebtoken.SignatureAlgorithm;
  5. import java.util.Date;
  6. import java.util.Map;
  7. public class JwtUtils {
  8. private static String signKey = "itking";
  9. private static Long expire = 43200000L;
  10. /**
  11. * 生成JWT令牌
  12. * @param claims JWT第二部分负载 payload 中存储的内容
  13. * @return
  14. */
  15. public static String generateJwt(Map<String, Object> claims){
  16. String jwt = Jwts.builder()
  17. .addClaims(claims)
  18. .signWith(SignatureAlgorithm.HS256, signKey)
  19. .setExpiration(new Date(System.currentTimeMillis() + expire))
  20. .compact();
  21. return jwt;
  22. }
  23. /**
  24. * 解析JWT令牌
  25. * @param jwt JWT令牌
  26. * @return JWT第二部分负载 payload 中存储的内容
  27. */
  28. public static Claims parseJWT(String jwt){
  29. Claims claims = Jwts.parser()
  30. .setSigningKey(signKey)
  31. .parseClaimsJws(jwt)
  32. .getBody();
  33. return claims;
  34. }
  35. }

再更改一下LoginController

  1. package com.example.Controller;
  2. import com.example.Pojo.Emp;
  3. import com.example.Pojo.Result;
  4. import com.example.Service.EmpService;
  5. import com.example.Utils.JwtUtils;
  6. import org.springframework.beans.factory.annotation.Autowired;
  7. import org.springframework.web.bind.annotation.PostMapping;
  8. import org.springframework.web.bind.annotation.RequestBody;
  9. import org.springframework.web.bind.annotation.RestController;
  10. import java.util.HashMap;
  11. import java.util.Map;
  12. @RestController
  13. public class LoginController {
  14. @Autowired
  15. private EmpService empService;
  16. @PostMapping("/login")
  17. public Result login(@RequestBody Emp emp){
  18. Emp e= empService.login(emp);
  19. //登陆成功,生成令牌,下发令牌
  20. if(e!=null){
  21. Map<String, Object> claims=new HashMap<>();
  22. claims.put("id",e.getId());
  23. claims.put("name",e.getName());
  24. claims.put("username",e.getUsername());
  25. String jwt = JwtUtils.generateJwt(claims);
  26. return Result.success(jwt);
  27. }
  28. //登陆失败,返回错误信息
  29. return Result.error("用户名或密码错误");
  30. }
  31. }

 测试

过滤器(Filter)

概念:Filter 过滤器,是JavaWeb 三大组件(Servlet、Filter、Listener)之一

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

过滤器一般完成一些通用的操作,比如:登录校验、统一编码处理、敏感字符处理等

Filter快速入门 

定义Filter:定义一个类,实现 Filter接口,并重写其所有方法。
配置Filter: filter类上加 @Webfilter 注解,配置拦截资源的路径。引导类上加 @ServletComponentScan 开启Servlet组件支持 

初始化与销毁已经默认实现了

DemoFilter类 

  1. package com.example.Filter;
  2. import jakarta.servlet.*;
  3. import jakarta.servlet.annotation.WebFilter;
  4. import java.io.IOException;
  5. @WebFilter(urlPatterns = "/*")
  6. public class DemoFilter implements Filter {
  7. //初始化方法,只调用一次
  8. @Override
  9. public void init(FilterConfig filterConfig) throws ServletException {
  10. System.out.println("init初始化方法执行了");
  11. }
  12. //拦截到请求之后调用,会调用多次
  13. @Override
  14. public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
  15. System.out.println("拦截到请求");
  16. //放行
  17. filterChain.doFilter(servletRequest,servletResponse);
  18. }
  19. //销毁方法,只调用一次
  20. @Override
  21. public void destroy() {
  22. System.out.println("destroy销毁方法执行了");
  23. }
  24. }

 还要在启动类加上@ServletComponentScan

  1. package com.example;
  2. import org.springframework.boot.SpringApplication;
  3. import org.springframework.boot.autoconfigure.SpringBootApplication;
  4. import org.springframework.boot.web.servlet.ServletComponentScan;
  5. @ServletComponentScan//开启了对servlet组件的支持
  6. @SpringBootApplication
  7. public class TliasWebManagementApplication {
  8. public static void main(String[] args) {
  9. SpringApplication.run(TliasWebManagementApplication.class, args);
  10. }
  11. }

放行后访问对应资源,资源访问完成后,还会回到Filter中吗?

如果回到Filter中,是重新执行还是执行放行后的逻辑呢?

执行放行后逻辑

Filter拦截路径

Filter 可以根据需求,配置不同的拦截资源路径

拦截路径urlPatterns值                                含义
拦截具体路径        /login只有访问 /login 路径时,才会被拦截
目录拦截        /emps/*访问/emps下的所有资源,都会被拦截
拦截所有路径           /*访问所有资源,都会被拦截

过滤器链

介绍:一个web应用中,可以配置多个过滤器,这多个过滤器就形成了一个过滤器链

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

所有的请求,拦截到了之后,都需要校验令牌吗?

有一个例外,登录请求

拦截到请求后,什么情况下才可以放行,执行业务操作? 

有令牌,且令牌校验通过(合法);否则都返回未登录错误结果

登录校验Filter-流程

  1. 获取请求url。
  2. 判断请求url中是否包含login,如果包含,说明是登录操作,放行
  3. 获取请求头中的令牌(token)
  4. 判断令牌是否存在,如果不存在,返回错误结果(未登录)
  5. 解析token,如果解析失败,返回错误结果(未登录)
  6. 放行。 
  1. package com.example.Filter;
  2. import com.alibaba.fastjson.JSONObject;
  3. import com.example.Pojo.Result;
  4. import com.example.Utils.JwtUtils;
  5. import jakarta.servlet.*;
  6. import jakarta.servlet.annotation.WebFilter;
  7. import jakarta.servlet.http.HttpServletRequest;
  8. import jakarta.servlet.http.HttpServletResponse;
  9. import lombok.extern.slf4j.Slf4j;
  10. import org.springframework.util.StringUtils;
  11. import java.io.IOException;
  12. @Slf4j
  13. @WebFilter(urlPatterns = "/*")
  14. public class LoginFilter implements Filter {
  15. @Override
  16. public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
  17. HttpServletRequest req= (HttpServletRequest) servletRequest;
  18. HttpServletResponse resp= (HttpServletResponse) servletResponse;
  19. //1获取请求url。
  20. String url=req.getRequestURI().toString();
  21. log.info("请求的url:{}",url);
  22. //2判断请求url中是否包含login,如果包含,说明是登录操作,放行
  23. if(url.contains("login")){
  24. log.info("登陆操作,放行");
  25. filterChain.doFilter(servletRequest,servletResponse);
  26. return;
  27. }
  28. //3获取请求头中的令牌(token)
  29. String jwt=req.getHeader("token");
  30. //4判断令牌是否存在,如果不存在,返回错误结果(未登录)
  31. if(!StringUtils.hasLength(jwt)){
  32. log.info("请求头token为空,返回未登陆信息");
  33. Result error = Result.error("NOT_LOGIN");
  34. String notLogin = JSONObject.toJSONString(error);
  35. resp.getWriter().write(notLogin);
  36. return;
  37. }
  38. //5解析token,如果解析失败,返回错误结果(未登录)
  39. try {
  40. JwtUtils.parseJWT(jwt);
  41. } catch (Exception e) {
  42. e.printStackTrace();
  43. log.info("解析令牌失败,返回未登录信息");
  44. Result error = Result.error("NOT_LOGIN");
  45. String notLogin = JSONObject.toJSONString(error);
  46. resp.getWriter().write(notLogin);
  47. return;
  48. }
  49. //6放行
  50. log.info("令牌合法,放行");
  51. filterChain.doFilter(servletRequest,servletResponse);
  52. }
  53. }

拦截器(Interceptor)

概念:是一种动态拦截方法调用的机制,类似于过滤器。Spring框架中提供的,用来动态拦截控制器方法的执行

作用:拦截请求,在指定的方法调用前后,根据业务需要执行预先设定的代码。

Interceptor 快速入门

1.定义拦截器,实现HandlerInterceptor接口,并重写其所有方法
2.注册拦截器 

先创建一个包+类

 

然后实现这个接口

再按ctrl+o重写里面的方法

  1. package com.example.Interceptor;
  2. import jakarta.servlet.http.HttpServletRequest;
  3. import jakarta.servlet.http.HttpServletResponse;
  4. import org.springframework.stereotype.Component;
  5. import org.springframework.web.servlet.HandlerInterceptor;
  6. import org.springframework.web.servlet.ModelAndView;
  7. @Component
  8. public class LoginCheckInterceptor implements HandlerInterceptor {
  9. @Override//目标资源方法运行前运行,返回true:放行,放回false,不放行
  10. public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
  11. System.out.println("preHandle运行了");
  12. return true;
  13. }
  14. @Override//标资源方法运行后运行
  15. public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
  16. System.out.println("postHandle运行了");
  17. }
  18. @Override//视图渲染完毕后运行,最后运行
  19. public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
  20. System.out.println("afterCompletion运行了");
  21. }
  22. }

 再写个配置类

  1. package com.example.Config;
  2. import com.example.Interceptor.LoginCheckInterceptor;
  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 WebConfig implements WebMvcConfigurer {
  9. @Autowired
  10. private LoginCheckInterceptor loginCheckInterceptor;
  11. @Override
  12. public void addInterceptors(InterceptorRegistry registry) {
  13. registry.addInterceptor(loginCheckInterceptor).addPathPatterns("/**");
  14. }
  15. }

拦截路径 

拦截路径含义举例
/*一级路径能匹配/depts,/emps,/login,不能匹配/depts/1
/**任意级路径能匹配/depts,/depts/1,/depts/1/2
/depts/*/depts下的一级路径能匹配/depts/1,不能匹配/depts/1/2,/depts
/depts/**/depts下的任意级路径能匹配/depts,/depts/1,/depts/1/2,不能匹配/emps/1

 Filter 与Interceptor

接口规范不同:过滤器需要实现Filter接口,而拦截器需要实现HandlerInterceptor接口。
拦截范围不同:过滤器Filter会拦截所有的资源,而Interceptor只会拦截Spring环境中的资源。

登录校验流程 

这个与Filter差不多

  1. package com.example.Interceptor;
  2. import com.alibaba.fastjson.JSONObject;
  3. import com.example.Pojo.Result;
  4. import com.example.Utils.JwtUtils;
  5. import jakarta.servlet.http.HttpServletRequest;
  6. import jakarta.servlet.http.HttpServletResponse;
  7. import lombok.extern.slf4j.Slf4j;
  8. import org.springframework.stereotype.Component;
  9. import org.springframework.util.StringUtils;
  10. import org.springframework.web.servlet.HandlerInterceptor;
  11. import org.springframework.web.servlet.ModelAndView;
  12. @Slf4j
  13. @Component
  14. public class LoginCheckInterceptor implements HandlerInterceptor {
  15. @Override//目标资源方法运行前运行,返回true:放行,放回false,不放行
  16. public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
  17. String url=request.getRequestURI().toString();
  18. log.info("请求的url:{}",url);
  19. //2判断请求url中是否包含login,如果包含,说明是登录操作,放行
  20. if(url.contains("login")){
  21. log.info("登陆操作,放行");
  22. return true;
  23. }
  24. //3获取请求头中的令牌(token)
  25. String jwt=request.getHeader("token");
  26. //4判断令牌是否存在,如果不存在,返回错误结果(未登录)
  27. if(!StringUtils.hasLength(jwt)){
  28. log.info("请求头token为空,返回未登陆信息");
  29. Result error = Result.error("NOT_LOGIN");
  30. String notLogin = JSONObject.toJSONString(error);
  31. response.getWriter().write(notLogin);
  32. return false;
  33. }
  34. //5解析token,如果解析失败,返回错误结果(未登录)
  35. try {
  36. JwtUtils.parseJWT(jwt);
  37. } catch (Exception e) {
  38. e.printStackTrace();
  39. log.info("解析令牌失败,返回未登录信息");
  40. Result error = Result.error("NOT_LOGIN");
  41. String notLogin = JSONObject.toJSONString(error);
  42. response.getWriter().write(notLogin);
  43. return false;
  44. }
  45. //6放行
  46. log.info("令牌合法,放行");
  47. return true;
  48. }
  49. @Override//标资源方法运行后运行
  50. public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
  51. System.out.println("postHandle运行了");
  52. }
  53. @Override//视图渲染完毕后运行,最后运行
  54. public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
  55. System.out.println("afterCompletion运行了");
  56. }
  57. }

 努力遇见更好的自己!!!

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/小丑西瓜9/article/detail/729480
推荐阅读
相关标签
  

闽ICP备14008679号