JWT令牌详细解析

JWT令牌详细解析主要介绍了 SpringBoot 集成 JWT 令牌详细说明 JWT 方式校验方式更加简单便捷化 无需通过 redis 缓存 而是直接根据 token 取出保存的用户信息 以及对 token 可用性校验 单点登录 验证 token 更为简单

大家好,欢迎来到IT知识分享网。


前言

主要介绍了SpringBoot集成JWT令牌详细说明,JWT方式校验方式更加简单便捷化,无需通过redis缓存,而是直接根据token取出保存的用户信息,以及对token可用性校验,单点登录,验证token更为简单。


一、JWT是什么?

一句话解释就是:用于对应用程序上的用户进行身份验证的标记
JWT简称JSON、Web、Token,也就是通过JSON形式作为web与应用中的令牌,用于在各方之间安全地将信息作为JSON对象传输。在数据传输过程中还可以完成数据加密、签名等相关处理。
JSON、Web、Token是一个开放标准,它定义了一种紧凑的、自包含的方式,用于在各方之间以JSON对象安全的传输信息。此信息可以验证和信任,因为它是数字签名的,JWT可以使用秘密或使用RSA或ECDSA的公钥/私钥对进行签名。
官网:http://jwt.io/introduction/


二、JWT与传统Cookie+Session的对比

  1. 在传统的用户登录认证中,因为http是无状态的,所以都是采用session方式。用户登录成功,服务端会保证一个session,当然会给客户端一个sessionId,客户端会把sessionId保存在cookie中,每次请求都会携带这个sessionId。

传统Cookie+ Session认证

在这里插入图片描述

  1. cookie+session这种模式通常是保存在内存中,而且服务从单服务到多服务会面临的session共享问题,随着用户量的增多,开销就会越大。而JWT不是这样的,只需要服务端生成token,客户端保存这个token,每次请求携带这个token,服务端认证解析就可。
  2. JWT方式校验方式更加简单便捷化,无需通过redis缓存,而是直接根据token取出保存的用户信息,以及对token可用性校验,单点登录,验证token更为简单。

在这里插入图片描述

三、JWT

1. JWT的功能

JWT主要的功能有授权信息交互两种
授权: 用户的授权,一旦用户登录后获得了token,后续一些需要用户权限的请求就不会拒绝请求。
信息交互: JSON Web Token是在各方之间安全传输信息的好方法。因为可以对JWT进行签名(例如:使用公钥/私钥对),所以可以确保发送方信息,由于签名是使用标头和有效负载计算的,因此可以验证内容是否遭到篡改,保证信息的完整性。

2. JWT的结构

  • header: 标头通常由两部分组成即令牌的类型(JWT)和使用的算法类型。【通常使用Base64编码组成JWT结构的第一部分】
{ 
      "alg":"HS256", "typ":"JWT" } 
  • payload: 有效负载,存储用户的相关的信息和其他数据的声明。【通常使用Base64编码组成JWT结构的第二部分】
{ 
      "username":"admin", "password":"" } 
  • singnature: 签名,与前面两个部分生成JWT,该签名不能让其他人知道,是保证token不被串改,保证信息的安全。【通常使用Base64编码组成JWT结构的第二部分】
header+"."+payload+"."+singnature ---> 组成唯一的token 

3. JWT的使用

  1. 导入JWT依赖
<dependency> <groupId>com.auth0</groupId> <artifactId>java-jwt</artifactId> <version>3.10.3</version> </dependency> 
  1. 生成token
@PostMapping("/login") @ApiOperation(value = "员工登录") public Result<EmployeeLoginVO> login(@RequestBody EmployeeLoginDTO employeeLoginDTO) { 
      log.info("员工登录:{}", employeeLoginDTO); Employee employee = employeeService.login(employeeLoginDTO); //登录成功后,生成jwt令牌 Map<String, Object> claims = new HashMap<>(); claims.put(JwtClaimsConstant.EMP_ID, employee.getId()); String token = JwtUtil.createJWT( jwtProperties.getAdminSecretKey(), jwtProperties.getAdminTtl(), claims); EmployeeLoginVO employeeLoginVO = EmployeeLoginVO.builder() .id(employee.getId()) .userName(employee.getUsername()) .name(employee.getName()) .token(token) .build(); return Result.success(employeeLoginVO); } 
  1. 配置拦截器:在WebMvcConfiguration中重写拦截器接口addInterceptors,每次访问/admin目录下的文件都会经过此拦截器进行jwt验证。
@Configuration @Slf4j public class WebMvcConfiguration extends WebMvcConfigurationSupport { 
      @Autowired private JwtTokenAdminInterceptor jwtTokenAdminInterceptor; / * 注册自定义拦截器 * * @param registry */ protected void addInterceptors(InterceptorRegistry registry) { 
      log.info("开始注册自定义拦截器..."); registry.addInterceptor(jwtTokenAdminInterceptor) .addPathPatterns("/admin/") .excludePathPatterns("/admin/employee/login"); } 
  1. 验证解析token:创建拦截器的类JwtInterceptor,用于配置WebMvcConfigurer,将自己的自定义拦截器装配到容器中;jwt拦截器文件JwtInterceptor,重写preHandle方法,校验通过拦截器放行,不通过返回错误的状态码。
@Component @Slf4j public class JwtTokenAdminInterceptor implements HandlerInterceptor { 
      @Autowired private JwtProperties jwtProperties; / * 校验jwt * @param request * @param response * @param handler * @return * @throws Exception */ @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { 
      //判断当前拦截到的是Controller的方法还是其他资源 if (!(handler instanceof HandlerMethod)) { 
      //当前拦截到的不是动态方法,直接放行 return true; } //1、从请求头中获取令牌 String token = request.getHeader(jwtProperties.getAdminTokenName()); //2、校验令牌 try { 
      log.info("jwt校验:{}", token); Claims claims = JwtUtil.parseJWT(jwtProperties.getAdminSecretKey(), token); Long empId = Long.valueOf(claims.get(JwtClaimsConstant.EMP_ID).toString()); log.info("当前员工id:", empId); // 通过ThreadLocal存储当前员工id BaseContext.setCurrentId(empId); //3、通过,放行 return true; } catch (Exception ex) { 
      //4、不通过,响应401状态码 response.setStatus(401); return false; } } } 
  1. JwtUtils.java Jwt工具类
public class JwtUtil { 
      / * 生成jwt * 使用Hs256算法, 私匙使用固定秘钥 * * @param secretKey jwt秘钥 * @param ttlMillis jwt过期时间(毫秒) * @param claims 设置的信息 * @return */ public static String createJWT(String secretKey, long ttlMillis, Map<String, Object> claims) { 
      // 指定签名的时候使用的签名算法,也就是header那部分 SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.HS256; // 生成JWT的时间 long expMillis = System.currentTimeMillis() + ttlMillis; Date exp = new Date(expMillis); // 设置jwt的body JwtBuilder builder = Jwts.builder() // 如果有私有声明,一定要先设置这个自己创建的私有的声明,这个是给builder的claim赋值,一旦写在标准的声明赋值之后,就是覆盖了那些标准的声明的 .setClaims(claims) // 设置签名使用的签名算法和签名使用的秘钥 .signWith(signatureAlgorithm, secretKey.getBytes(StandardCharsets.UTF_8)) // 设置过期时间 .setExpiration(exp); return builder.compact(); } / * Token解密 * * @param secretKey jwt秘钥 此秘钥一定要保留好在服务端, 不能暴露出去, 否则sign就可以被伪造, 如果对接多个客户端建议改造成多个 * @param token 加密后的token * @return */ public static Claims parseJWT(String secretKey, String token) { 
      // 得到DefaultJwtParser Claims claims = Jwts.parser() // 设置签名的秘钥 .setSigningKey(secretKey.getBytes(StandardCharsets.UTF_8)) // 设置需要解析的jwt .parseClaimsJws(token).getBody(); return claims; } } 
  1. JwtProperties.java Jwt相关配置
package com.sky.properties; import lombok.Data; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.stereotype.Component; @Component @ConfigurationProperties(prefix = "sky.jwt") @Data public class JwtProperties { 
      / * 管理端员工生成jwt令牌相关配置 */ private String adminSecretKey; private long adminTtl; private String adminTokenName; / * 用户端微信用户生成jwt令牌相关配置 */ private String userSecretKey; private long userTtl; private String userTokenName; } 
  1. application.yml配置
sky: jwt: # 设置jwt签名加密时使用的秘钥 admin-secret-key: itcast # 设置jwt过期时间 admin-ttl:  # 设置前端传递过来的令牌名称 admin-token-name: token 
  1. 常见的JWT异常,封装成工具类JwtUtils
    SignatureVerificationException: 无效签名。
    TokenExpiredException: token过期。
    AlgorithmMismatchException: token算法不一致。
    InvalidClaimException: 失效payload异常。



package com.qiumin.utils; import com.auth0.jwt.JWT; import com.auth0.jwt.JWTCreator; import com.auth0.jwt.JWTVerifier; import com.auth0.jwt.algorithms.Algorithm; import com.auth0.jwt.interfaces.DecodedJWT; import java.util.Calendar; import java.util.Map; / * @author qiumin * @classname JWTUtils * @Description love code * @date 2022-07-10 15:33 */ public class JwtUtils { 
      private static final String SING = "!FhSD!#VhDZF%#FDBD"; / * 获得Token * @param map1 header * @param map2 payload * */ public static String getToken(Map<String,Object> map1, Map<String,String> map2){ 
      Calendar instance = Calendar.getInstance(); instance.add(Calendar.SECOND,180); //过期时间 JWTCreator.Builder builder = JWT.create(); builder.withHeader(map1); //header map2.forEach((k,v)->{ 
     builder.withClaim(k,v);}); //payload builder.withExpiresAt(instance.getTime()); //过期时间 String token = builder.sign(Algorithm.HMAC256(SING));//签名 return token; } / * 验证token * @param token 令牌 * */ public static void verify(String token){ 
      JWTVerifier build = JWT.require(Algorithm.HMAC256(SING)).build(); build.verify(token); } / * 获取token中的信息 * @param token 令牌 * */ public static DecodedJWT analysis(String token){ 
      return JWT.require(Algorithm.HMAC256(SING)).build().verify(token); } } 

免责声明:本站所有文章内容,图片,视频等均是来源于用户投稿和互联网及文摘转载整编而成,不代表本站观点,不承担相关法律责任。其著作权各归其原作者或其出版社所有。如发现本站有涉嫌抄袭侵权/违法违规的内容,侵犯到您的权益,请在线联系站长,一经查实,本站将立刻删除。 本文来自网络,若有侵权,请联系删除,如若转载,请注明出处:https://haidsoft.com/114505.html

(0)
上一篇 2025-12-08 17:21
下一篇 2025-12-08 17:33

相关推荐

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注微信