SpringBoot整合JWT

打印 上一主题 下一主题

主题 1864|帖子 1864|积分 5592

目录

1、JWT简介
1.1、Session+Cookie认证
1.2、Token认证
1.3、对比
2、JWT构成
头部(Header)
载荷(Payload)
签名(Signature)
3、实行流程



1、JWT简介

JWT是JSON Web Token的缩写,是一个开放标准(RFC 7519),它定义了一种紧凑和自包含的方式,用于作为JSON对象在各方之间安全地传输信息。JWT可以用于身份验证和授权,由于它是数字签名的。
github: https://github.com/jwtk/jjwt
1.1、Session+Cookie认证

http 协议本身是无状态的协议,就意味着当有用户向系统使用账户名称和暗码进行用户认证之后,下一次请求还要再一次用户认证才行。由于我们不能通过 http 协议知道是哪个用户发出的请求,以是假如要知道是哪个用户发出的请求,那就需要在服务器保存一份用户信息 ,这就需session。Session认证是一种基于服务器端的认证方式,它是通过在服务器端保存用户会话信息来实现的。当用户第一次访问服务器时,服务器会天生一个Session ID并将其存储在Cookie中。用户在后续的请求中将会带上这个Cookie,服务器可以通过这个Cookie来辨认用户并获取用户的会话信息。
1.2、Token认证

token认证方式跟 session 的方式流程差不多,不同的地方在于保存的是一个 token 值到 redis,token 一般是一串随机的字符(比如UUID),value 一般是用户ID,而且设置一个逾期时间。每次请求服务的时候带上 token 在请求头,后端吸收到token 则根据 token 查一下 redis 是否存在,假如存在则表现用户已认证,假如 token 不存在则跳到登录界面让用户重新登录,登录成功后返回一个 token 值给客户端。
1.3、对比



  • 在传统的用户登录认证中,由于http是无状态的,以是都是接纳session方式。用户登录成功,服务端会保证一个session,当然会给客户端一个sessionId,客户端会把sessionId保存在cookie中,每次请求都会携带这个sessionId。cookie+session这种模式通常是保存在内存中,而且服务从单服务到多服务碰面临的session共享题目,随着用户量的增多,开销就会越大。而JWT不是这样的,只需要服务端天生token,客户端保存这个token,每次请求携带这个token,服务端认证分析就可。
  • JWT方式校验方式更加简朴便捷化,无需通过redis缓存,而是直接根据token取出保存的用户信息,以及对token可用性校验,单点登录,验证token更为简朴
2、JWT构成

JWT 由三部门构成:头部(Header)、**载荷(Payload)**和 签名(Signature),从官方的图解也可以看到如下:(JWT官网:JSON Web Tokens - jwt.io)

头部(Header)

头部(Header): JWT头部通常由两部门构成,第一部门是声明类型,比方JWT,第二部门是声明所使用的算法,比方HMAC SHA256或者RSA等。 末了使用 Base64 URL 算法将上述 JSON 对象转换为字符串保存 。
  1. {
  2.     "alg": "HS256", // 签名算法
  3.     "typ": "JWT" // 令牌类型
  4. }
复制代码
载荷(Payload)

携带一些用户信息 和默认字段; 默认情况下 JWT 是未加密的,任何人都可以解读其内容,因此一些敏感信息不要存放于此,以防信息泄露。 JSON 对象也使用 Base64 URL 算法转换为字符串后保存,可以反编码。
  1. /**
  2. ss (issuer):签发人/发行人
  3. sub (subject):主题
  4. aud (audience):用户
  5. exp (expiration time):过期时间
  6. nbf (Not Before):生效时间,在此之前是无效的
  7. iat (Issued At):签发时间
  8. jti (JWT ID):用于标识该 JWT
  9. **/
  10. // 自定义
  11. {
  12.     //默认字段
  13.     "sub":"主题123",
  14.     //自定义字段
  15.     "name":"gcxy",
  16.     "isAdmin":"true",
  17.     "loginTime":"2023-8-22 10:00:03"
  18. }
复制代码
签名(Signature)

防止Token被窜改、确保安全性 ,签名过程:
1.将头部和载荷按照顺序拼接成一个字符串;
2.使用私钥对拼接后的字符串进行加密,得到一个签名;
3.将签名添加到头部中。
3、实行流程

jwt实行流程大抵如下:


代码实例:
导入依赖:
  1. <!-- JWT依赖 -->
  2. <dependency>
  3.     <groupId>com.auth0</groupId>
  4.     <artifactId>java-jwt</artifactId>
  5.     <version>3.4.0</version>
  6. </dependency>
复制代码
封装JWT工具类
  1. package com.gcxy.demo3.utils;
  2. import com.auth0.jwt.JWT;
  3. import com.auth0.jwt.JWTCreator;
  4. import com.auth0.jwt.algorithms.Algorithm;
  5. import com.auth0.jwt.interfaces.Claim;
  6. import com.auth0.jwt.interfaces.DecodedJWT;
  7. import org.springframework.stereotype.Component;
  8. import java.util.Calendar;
  9. import java.util.Date;
  10. import java.util.Map;
  11. @Component
  12. public class JwtUtil {
  13.     private static String secret = "1234";
  14.     /**
  15.      * 生成token
  16.      * @param subject
  17.      * @return
  18.      */
  19.     public static String createToken(Map<String, String> subject){
  20.         // 过期时间
  21.         Date date = new Date(new Date().getTime() + 3600 * 1000);
  22.         JWTCreator.Builder builder = JWT.create();
  23.         subject.forEach((k, v) -> {
  24.             builder.withClaim(k, v);
  25.         });
  26.         // 设置过期时间
  27.         Calendar calendar = Calendar.getInstance();
  28.         calendar.add(Calendar.DATE, 1); // 设置默认过期时间
  29.         builder.withExpiresAt(calendar.getTime());  // 指定过期时间
  30.         return builder.sign(Algorithm.HMAC256(secret));
  31.     }
  32.     /**
  33.      * 校验token
  34.      * @param token
  35.      * @return
  36.      */
  37.     public static DecodedJWT verifyToken(String token) {
  38.         return JWT.require(Algorithm.HMAC256(secret)).build().verify(token);
  39.     }
  40.     /**
  41.      * 获取token信息
  42.      * @param token
  43.      * @return
  44.      */
  45.     public static Map<String, Claim> getTokenInfo(String token) {
  46.         return JWT.require(Algorithm.HMAC256(secret)).build().verify(token).getClaims();
  47.     }
  48. }
复制代码
创建拦截 器:
  1. package com.gcxy.demo3.interceptor;
  2. import com.auth0.jwt.interfaces.DecodedJWT;
  3. import com.fasterxml.jackson.databind.ObjectMapper;
  4. import com.gcxy.demo3.utils.JwtUtil;
  5. import org.springframework.stereotype.Component;
  6. import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
  7. import javax.annotation.Resource;
  8. import javax.servlet.http.HttpServletRequest;
  9. import javax.servlet.http.HttpServletResponse;
  10. import java.util.HashMap;
  11. @Component
  12. public class TokenInterceptor extends HandlerInterceptorAdapter {
  13.     @Resource
  14.     private JwtUtil jwtConfig;
  15.     @Override
  16.     public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
  17.         HashMap<String, String> map = new HashMap<>();
  18.         // Token 验证
  19.         String token = request.getHeader("Authorization");
  20.         try {
  21.             // 校验成功放行请求
  22.             DecodedJWT verifyToken = jwtConfig.verifyToken(token);
  23.             return true;
  24.         } catch (Exception e) {
  25.             map.put("msg", "token验证失败: " + e);
  26.         }
  27.        // 校验失败返回失败信息
  28.         String json = new ObjectMapper().writeValueAsString(map);
  29.         response.setContentType("application/json;charset=UTF-8");
  30.         response.getWriter().write(json);
  31.         return false;
  32.     }
  33.     @Override
  34.     public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
  35.     }
  36.     @Override
  37.     public void afterConcurrentHandlingStarted(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
  38.     }
  39. }
复制代码
注册拦截器:
  1. package com.gcxy.demo3.config;
  2. import com.gcxy.demo3.interceptor.TokenInterceptor;
  3. import org.springframework.context.annotation.Configuration;
  4. import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
  5. import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
  6. @Configuration
  7. public class InterceptorConfig implements WebMvcConfigurer {
  8.     @Override
  9.     public void addInterceptors(InterceptorRegistry registry) {
  10.         // 拦截所有请求,通过判断是否有 @LoginRequired 注解 决定是否需要登录
  11.         registry.addInterceptor(new TokenInterceptor())
  12.                 .addPathPatterns("/**")
  13.                 .excludePathPatterns("/login")
  14.         ;
  15.     }
  16. }
复制代码
创建测试controller
  1. package com.gcxy.demo3.controller;
  2. import com.auth0.jwt.interfaces.DecodedJWT;
  3. import com.gcxy.demo3.utils.JwtUtil;
  4. import org.springframework.web.bind.annotation.GetMapping;
  5. import org.springframework.web.bind.annotation.PostMapping;
  6. import org.springframework.web.bind.annotation.RestController;
  7. import javax.servlet.http.HttpServletRequest;
  8. import java.util.HashMap;
  9. import java.util.Map;
  10. @RestController
  11. public class UserController {
  12.     @PostMapping("/login")
  13.     public Map<String, Object> login(String username, String password) {
  14.         Map<String, Object> map = new HashMap<>();
  15.         try{
  16.             // 生成token
  17.             Map<String, String> payload = new HashMap<>();
  18.             payload.put("username", "zhangsan");
  19.             payload.put("password", "123456");
  20.             String token = JwtUtil.createToken(payload);
  21.             map.put("code", 200);
  22.             map.put("msg", "登录成功");
  23.             map.put("token", token);
  24.         }catch (Exception e){
  25.             System.out.println(e.getMessage());
  26.             map.put("code", 500);
  27.             map.put("msg", "登录失败");
  28.         }
  29.       return map;
  30.     }
  31.     @GetMapping("/getUserInfo")
  32.     public Map<String, Object> getUserInfo(HttpServletRequest request) {
  33.         Map<String, Object> map = new HashMap<>();
  34.         String token = request.getHeader("Authorization");
  35.         DecodedJWT tokenInfo = JwtUtil.verifyToken(token);
  36.         String username = tokenInfo.getClaim("username").asString();
  37.         String password = tokenInfo.getClaim("password").asString();
  38.         System.out.println("username = " + username);
  39.         System.out.println("password = " + password);
  40.         map.put("code", 200);
  41.         map.put("msg", "请求成功");
  42.         return map;
  43.     }
  44. }
复制代码
测试结果:




练习拓展:
结合Redis实现token验证

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

莱莱

论坛元老
这个人很懒什么都没写!
快速回复 返回顶部 返回列表