Spring Security(十三):JWT令牌

本篇来了解下JWT令牌相关的知识,和传统JSESSIONID相比,差别还是很大的,一起来看看吧。

JWT令牌简介

  • 全称:Json Web Token
  • 特点:
    • 自包含,即自身包含了一些用户信息,和以前的随机串(JSSESSIONID)相比,以前JSSESSIONID方式缓存丢失后,登录信息就丢失了,而JWT可以通过自身包含的信息重新查出用户数据。
    • 密签,加salt进行签名,防止别人修改。
    • 可扩展,想放什么信息都可以放,但是一般不放敏感信息,因为只要有令牌都是可以查看里面信息的。
  • 应用场景,可以作为接口调用的凭证,可以很方便的实现SSO单点登录(下一篇文章分享)。

JWT的构成

  • 首先JWT分为三部分,举例如下:
eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIxMjk1NjJfRkVFREJBQ0siLCJleHAiOjE1OTQ5NzQxNjIsImJlbG9uZ1RvSWQiOjEsImlhdCI6MTU5MjM4MjE2Mn0.wrsn8OiDBAvu8uHhIR01ns2qnP6y-SE0ziEjb8wjxUA

头部(header)

  • 头部有两部分信息,一个是声明类型,这里是JWT;一个是声明加密的算法,一般使用HMAC/SHA256算法。
  • 举例如下:
{
    
    
  'typ': 'JWT',
  'alg': 'HS256'
}
  • 然后将头部进行base64加密(该加密是可以对称解密的),构成了JWT第一部分:
eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9

载荷(payload)

  • 载荷就是存放有效信息的地方。包括标准中注册的声明、公共的声明、私有的声明。
  • 标准中注册的声明包含:
    • iss: jwt签发者
    • sub: jwt所面向的用户
    • aud: 接收jwt的一方
    • exp: jwt的过期时间,这个过期时间必须要大于签发时间
    • nbf: 定义在什么时间之前,该jwt都是不可用的.
    • iat: jwt的签发时间
    • jti: jwt的唯一身份标识,主要用来作为一次性token,从而回避重放攻击。
  • 公共声明:公共的声明可以添加任何信息,一般添加用户的相关信息或其他业务需要的必要信息。不建议添加敏感信息,因为该部分在客户端可解密。
  • 私有声明:私有声明是提供者和消费者所共同定义的声明,一般不建议存放敏感信息,因为base64是对称解密的,意味着该部分信息可以归类为明文信息。
  • 举例如下:
{
    
    
  "sub": "1000000001",
  "name": "bajie",
  "admin": true
}
  • 然后将其进行base64加密,得到Jwt的第二部分:
eyJzdWIiOiIxMjk1NjJfRkVFREJBQ0siLCJleHAiOjE1OTQ5NzQxNjIsImJlbG9uZ1RvSWQiOjEsImlhdCI6MTU5MjM4MjE2Mn0

签证(signature)

  • 第三部分是一个签证信息,这个签证信息由三部分组成:header(base64加密后)、payload(base64加密后)、secret。
  • 这个部分需要base64加密后的header和base64加密后的payload使用.连接组成的字符串,然后通过header中声明的加密方式进行加盐secret组合加密,然后就构成了jwt的第三部分:
wrsn8OiDBAvu8uHhIR01ns2qnP6y-SE0ziEjb8wjxUA
  • 最后,三部分用.组合就构成了最终的jwt。

应用实践

一般用来作为登录凭证

  • 首先用户需要先登录,登录成功之后会在成功处理器构建一个JWT给到前端(或是响应头的方式返回或是直接塞进Cookie中)。
  • 然后用户每次请求接口的时候都需要解析验证JWT是否有效

实战

  • 添加依赖
		<dependency>
			<groupId>com.auth0</groupId>
			<artifactId>java-jwt</artifactId>
			<version>3.4.0</version>
		</dependency>
  • 构建JWT
		LoginDTO user = (LoginDTO)authentication.getPrincipal();
		user.setUserType("BA");
		Date date = new Date(System.currentTimeMillis() + securityConfig.getTokenExpireTimeInSecond() * 1000);
		
		// 使用head中声明的HMAC256加密方式,再加Salt组合加密
		Algorithm algorithm = Algorithm.HMAC256(securityConfig.getTokenEncryptSalt());

		String token = JWT.create()
				.withSubject(user.getId() + "_" + "BA") // 公有声明
				.withClaim(LoginDTO.BELONG_TO_ID, user.getBelongToId()) // 私有声明
				.withExpiresAt(date) // 设置过期时间
				.withIssuedAt(new Date()) // 构建时间
				.sign(algorithm); // 签证
				
		// 将JWT以响应头的方式返回前端
		response.setHeader(securityConfig.getTokenName(), token);
  • 解析JWT
		DecodedJWT jwt = ((JwtAuthenticationToken)authentication).getToken();

		String subject = jwt.getSubject();
		String userId = subject.split("_")[0];
		String userType = subject.split("_")[1];

		try {
    
    
			Algorithm algorithm = Algorithm.HMAC256(securityConfig.getTokenEncryptSalt());
			JWTVerifier verifier = JWT.require(algorithm)
					.withSubject(subject)
					.build();
			verifier.verify(jwt.getToken());
        } catch (TokenExpiredException e) {
    
    
			throw AuthenticationException.JWT_EXPIRED;
        } catch (Exception e){
    
    
			throw new BadCredentialsException("JWT token verify fail", e);
		}

猜你喜欢

转载自blog.csdn.net/qq_36221788/article/details/106605529