当前位置:   article > 正文

springboot登录验证jwt令牌技术_springboot项目中实现jwt令牌颁发和校验的流程?

springboot项目中实现jwt令牌颁发和校验的流程?

首先,我们要清楚我们使用的前后端交互协议是HTTP协议,而HTTP协议是无状态协议,所谓无状态,指的是每一次请求都是独立的,下一次请求并不会携带上一次请求的数据。而浏览器与服务器之间进行交互,基于HTTP协议也就意味着现在我们通过浏览器来访问了登陆这个接口,实现了登陆的操作,接下来我们在执行其他业务操作时,服务器也并不知道这个员工到底登陆了没有。因为HTTP协议是无状态的,两次请求之间是独立的,所以是无法判断这个员工到底登陆了没有。
 

这时候就需要会话跟踪了。

会话跟踪:一种维护浏览器状态的方法,服务器需要识别多次请求是否来自于同一浏览器,以便在同一次会话的多次请求间共享数据。

会话跟踪技术有两种:

  1. Cookie(客户端会话跟踪技术)

    • 数据存储在客户端浏览器当中

  2. Session(服务端会话跟踪技术)

    • 数据存储在储在服务端

  3. 令牌技术

方案一:Cookie

cookie 是客户端会话跟踪技术,它是将共享的数据存储在客户端浏览器的。

  • 服务器会 自动 的将 cookie 响应给浏览器。

  • 浏览器接收到响应回来的数据之后,会 自动 的将 cookie 存储在浏览器本地。

  • 在后续的请求当中,浏览器会 自动 的将 cookie 携带到服务器端。

为什么这一切都是自动的?

是因为 cookie 它是 HTP 协议当中所支持的技术,而各大浏览器厂商都支持了这一标准。在 HTTP 协议官方给我们提供了一个响应头和请求头:

  • 响应头 Set-Cookie :设置Cookie数据的

  • 请求头 Cookie:携带Cookie数据的

优点:HTTP协议中支持的技术

缺点:移动APP中无法使用cookie

            不安全,在本地存储,而且用户也可以手动禁止

           cookie不能跨域

方案二  Session会话

Session,它是服务器端会话跟踪技术,所以它是将共享数据存储在服务器端的。

浏览器第一次请求session,会话对象是不存在的,这个时候服务器会自动创建一个会话对象,每一个对象都会有一个ID。然后服务器端给浏览器响应数据的时候,会将session的ID通过cookie响应给浏览器。

接下来,在后续的每一次请求当中,都会将 Cookie 的数据获取出来,并且携带到服务端。接下来服务器拿到JSESSIONID这个 Cookie 的值,也就是 Session 的ID。拿到 ID 之后,就会从众多的 Session 当中来找到当前请求对应的会话对象Session 。

这样我们就可以通过 Session 会话对象在同一次会话的多次请求之间来共享数据了。

 方案三 jwt令牌

令牌其实就是给用户一个身份标识,由头部,荷载(搭载需要的业务数据),签名三部分组成。

通过令牌跟踪技术跟踪会话,在请求登录接口时,如果登录成功,我们可以生成一个令牌,这个令牌就是用户的合法身份凭证,我们响应数据的时候,就可以把令牌响应给前端。

而令牌可以把这个身份凭证存储在自己指定的地方,响应体/头都可以,接下来每次请求中都要将令牌携带到服务端,进行检验令牌的有效性。这样就可以共享数据了。

 

优缺点

  • 优点:

    • 支持PC端、移动端

    • 解决集群环境下的认证问题

    • 减轻服务器的存储压力(无需在服务器端存储)

  • 缺点:需要自己实现(包括令牌的生成、令牌的传递、令牌的校验)

 生成和校验

 首先需要在pom中引用依赖:

  1. <!-- JWT依赖-->
  2. <dependency>
  3. <groupId>io.jsonwebtoken</groupId>
  4. <artifactId>jjwt</artifactId>
  5. <version>0.9.1</version>
  6. </dependency>

生成jwt令牌

  1. @Test
  2. public void genJwt(){
  3. Map<String,Object> claims = new HashMap<>();
  4. claims.put("id",1);
  5. claims.put("username","Tom");
  6. String jwt = Jwts.builder()
  7. .setClaims(claims) //自定义内容(载荷)
  8. .signWith(SignatureAlgorithm.HS256, "itheima") //签名算法
  9. .setExpiration(new Date(System.currentTimeMillis() + 24*3600*1000)) //有效期
  10. .compact();
  11. System.out.println(jwt);
  12. }

解析令牌

  1. @Test
  2. public void parseJwt(){
  3. Claims claims = Jwts.parser()
  4. .setSigningKey("itheima")//指定签名密钥(必须保证和生成令牌时使用相同的签名密钥)
  5. .parseClaimsJws("eyJhbGciOiJIUzI1NiJ9.eyJpZCI6MSwiZXhwIjoxNjcyNzI5NzMwfQ.fHi0Ub8npbyt71UqLXDdLyipptLgxBUg_mSuGJtXtBk")
  6. .getBody();
  7. System.out.println(claims);
  8. }

而我们可以直接在工具类中使用

  1. public class JwtUtils {
  2. private static String signKey = "itheima";//签名密钥
  3. private static Long expire = 43200000L; //有效时间
  4. /**
  5. * 生成JWT令牌
  6. * @param claims JWT第二部分负载 payload 中存储的内容
  7. * @return
  8. */
  9. public static String generateJwt(Map<String, Object> claims){
  10. String jwt = Jwts.builder()
  11. .addClaims(claims)//自定义信息(有效载荷)
  12. .signWith(SignatureAlgorithm.HS256, signKey)//签名算法(头部)
  13. .setExpiration(new Date(System.currentTimeMillis() + expire))//过期时间
  14. .compact();
  15. return jwt;
  16. }
  17. /**
  18. * 解析JWT令牌
  19. * @param jwt JWT令牌
  20. * @return JWT第二部分负载 payload 中存储的内容
  21. */
  22. public static Claims parseJWT(String jwt){
  23. Claims claims = Jwts.parser()
  24. .setSigningKey(signKey)//指定签名密钥
  25. .parseClaimsJws(jwt)//指定令牌Token
  26. .getBody();
  27. return claims;
  28. }
  29. }

而我们使用了令牌之后,我们会发现,要想检验令牌的有效性,必须在每个接口都要进行校验,太复杂麻烦了,所以我们只需要拦截后,统一来进行校验,这里有两种方法:过滤器Filter和拦截器Interceptor。

 过滤器Filter

 就是使用过滤器来进行统一的令牌校验,只有通过了校验,才会放行,让它继续调用接口方法。

 快速入门

  • 第1步,定义过滤器 :1.定义一个类,实现 Filter 接口,并重写其所有方法。

  • 第2步,配置过滤器:Filter类上加 @WebFilter 注解,配置拦截资源的路径。引导类上加 @ServletComponentScan 开启Servlet组件支持。

  1. @WebFilter(urlPatterns = "/*") //配置过滤器要拦截的请求路径( /* 表示拦截浏览器的所有请求 )
  2. public class DemoFilter implements Filter {
  3. @Override //初始化方法, 只调用一次
  4. public void init(FilterConfig filterConfig) throws ServletException {
  5. System.out.println("init 初始化方法执行了");
  6. }
  7. @Override //拦截到请求之后调用, 调用多次
  8. public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
  9. System.out.println("Demo 拦截到了请求...放行前逻辑");
  10. //放行
  11. chain.doFilter(request,response);
  12. }
  13. @Override //销毁方法, 只调用一次
  14. public void destroy() {
  15. System.out.println("destroy 销毁方法执行了");
  16. }
  17. }

而其中主要就是doFilter()方法,登录校验也主要在这个方法中。

  1. @Slf4j
  2. @WebFilter(urlPatterns = "/*") //拦截所有请求
  3. public class LoginCheckFilter implements Filter {
  4. @Override
  5. public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain chain) throws IOException, ServletException {
  6. //前置:强制转换为http协议的请求对象、响应对象 (转换原因:要使用子类中特有方法)
  7. HttpServletRequest request = (HttpServletRequest) servletRequest;
  8. HttpServletResponse response = (HttpServletResponse) servletResponse;
  9. //1.获取请求url
  10. String url = request.getRequestURL().toString();
  11. log.info("请求路径:{}", url); //请求路径:http://localhost:8080/login
  12. //2.判断请求url中是否包含login,如果包含,说明是登录操作,放行
  13. if(url.contains("/login")){
  14. chain.doFilter(request, response);//放行请求
  15. return;//结束当前方法的执行
  16. }
  17. //3.获取请求头中的令牌(token)
  18. String token = request.getHeader("token");
  19. log.info("从请求头中获取的令牌:{}",token);
  20. //4.判断令牌是否存在,如果不存在,返回错误结果(未登录)
  21. if(!StringUtils.hasLength(token)){
  22. log.info("Token不存在");
  23. Result responseResult = Result.error("NOT_LOGIN");
  24. //把Result对象转换为JSON格式字符串 (fastjson是阿里巴巴提供的用于实现对象和json的转换工具类)
  25. String json = JSONObject.toJSONString(responseResult);
  26. response.setContentType("application/json;charset=utf-8");
  27. //响应
  28. response.getWriter().write(json);
  29. return;
  30. }
  31. //5.解析token,如果解析失败,返回错误结果(未登录)
  32. try {
  33. JwtUtils.parseJWT(token);
  34. }catch (Exception e){
  35. log.info("令牌解析失败!");
  36. Result responseResult = Result.error("NOT_LOGIN");
  37. //把Result对象转换为JSON格式字符串 (fastjson是阿里巴巴提供的用于实现对象和json的转换工具类)
  38. String json = JSONObject.toJSONString(responseResult);
  39. response.setContentType("application/json;charset=utf-8");
  40. //响应
  41. response.getWriter().write(json);
  42. return;
  43. }
  44. //6.放行
  45. chain.doFilter(request, response);
  46. }
  47. }

其中我们用到了将对象转化成json字符串的工具类fastjson,需要我们引入依赖

  1. <dependency>
  2. <groupId>com.alibaba</groupId>
  3. <artifactId>fastjson</artifactId>
  4. <version>1.2.76</version>
  5. </dependency>

拦截器Interceptor

  • 是一种动态拦截方法调用的机制,类似于过滤器。

  • 拦截器是Spring框架中提供的,用来动态拦截控制器方法的执行。

拦截器的作用:

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

快速入门

下面我们通过快速入门程序,来学习下拦截器的基本使用。拦截器的使用步骤和过滤器类似,也分为两步:

  1. 定义拦截器

  2. 注册配置拦截器

  1. //自定义拦截器
  2. @Component
  3. public class LoginCheckInterceptor implements HandlerInterceptor {
  4. //目标资源方法执行前执行。 返回true:放行 返回false:不放行
  5. @Override
  6. public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
  7. System.out.println("preHandle .... ");
  8. return true; //true表示放行
  9. }
  10. //目标资源方法执行后执行
  11. @Override
  12. public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
  13. System.out.println("postHandle ... ");
  14. }
  15. //视图渲染完毕后执行,最后执行
  16. @Override
  17. public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
  18. System.out.println("afterCompletion .... ");
  19. }
  20. }
  21. //注册配置拦截器
  22. @Configuration
  23. public class WebConfig implements WebMvcConfigurer {
  24. //自定义的拦截器对象
  25. @Autowired
  26. private LoginCheckInterceptor loginCheckInterceptor;
  27. @Override
  28. public void addInterceptors(InterceptorRegistry registry) {
  29. //注册自定义拦截器对象
  30. registry.addInterceptor(loginCheckInterceptor).addPathPatterns("/**");//设置拦截器拦截的请求路径( /** 表示拦截所有请求)
  31. }
  32. }

 其中:

preHandle方法:目标资源方法执行前执行。 返回true:放行 返回false:不放行

postHandle方法:目标资源方法执行后执行

afterCompletion方法:视图渲染完毕后执行,最后执行

拦截器/**必须两个** 不然只会拦截/emp 或者/dept 无法拦截/emp/1的多级路径。

最后,两种拦截方法都可以实现最终的验证,到底使用那种啦?

 

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

闽ICP备14008679号