了解 JWT Token 释义及使用 | Authing 文档
JSON Web Token Introduction - jwt.io
JSON Web Token (JWT,RFC 7519 (opens new window)),是为了在网络应用环境间传递声明而执行的一种基于 JSON 的开放标准((RFC 7519)。该 token 被设计为紧凑且安全的,特别适用于分布式站点的单点登录(SSO)场景。JWT 的声明一般被用来在身份提供者和服务提供者间传递被认证的用户身份信息,以便于从资源服务器获取资源,也可以增加一些额外的其它业务逻辑所必须的声明信息,该 token 也可直接被用于认证,也可被加密。
1. 认证
传统的Session认证方式需要在服务器端存储用户登录信息,通常而言session都是保存在内存中,而随着认证用户的增多,服务端的开销会明显增大。基于Token的认证方式不需要在服务端去保留用户的认证信息或者会话信息。
流程上是这样的:
- 用户使用用户名密码来请求服务器
- 服务器进行验证用户的信息
- 服务器通过验证发送给用户一个token
- 客户端存储token,并在每次请求时附送上这个token值
- 服务端验证token值,并返回数据
2. JWT的结构
它采用缩写的形式,包含三个部分,由.
分割,包括以下三个部分
- Header——头部
- Payload——有效荷载
- Signature——签名
因此JWT呈现为这种形式:
xxxxx.yyyyy.zzzzz
2.1. Header
头部包含两个部分:(1)token的类型,即JWT;(2)使用的签名算法,如HMAC、SHA256或RSA
例如:
{
"alg": "HS256",
"typ": "JWT"
}
2.2. Payload
该部分包含一些实体和数据的声明,如用户等,包括已注册声明、公开声明、私有声明三类。
例如:
{
"sub": "1234567890",
"name": "John Doe",
"admin": true
}
2.3. Signature
要创建签名部分,您必须获取编码的标头、编码的有效负载、密钥、标头中指定的算法,然后对其进行签名。
3. 使用
jjwt10:GitHub - jwtk/jjwt: Java JWT: JSON Web Token for Java and Android
目前jjwt10以上版本和jjwt9使用方法不同,好像使用jjwt9还是偏多一些。
jjwt9:jjwt 0.9.1 javadoc (io.jsonwebtoken)
以下代码是从mall项目中学习的JWT的工具类,一般可作为模板使用,主要提供了生成token和解析token的API,使用的是jjwt9
@Component
public class JwtTokenUtil {
private static final Logger LOGGER = LoggerFactory.getLogger(JwtTokenUtil.class);
//sub: subject,主体,即登录的主体,一般是用户名
private static final String CLAIM_KEY_USERNAME = "sub";
//created表示创建时间
private static final String CLAIM_KEY_CREATED = "created";
@Value("${jwt.secret}")
private String secret;
@Value("${jwt.expiration}")
private Long expiration;
/**
* 根据负载生成JWT的token
* @param claims 声明,即JWT中的payload部分
* @return token:String
*/
private String generateToken(Map<String, Object> claims) {
return Jwts.builder() //生成一个DefaultJwtBuilder类实例
.setClaims(claims) //设置该DefaultJwtBuilder类实例的claims字段
.setExpiration(generateExpirationDate()) //设置过期时间
.signWith(SignatureAlgorithm.HS512, secret) //设置签名算法、密钥
.compact(); //调用compact函数才能生成JWT
}
/**
* 从token中获取JWT中的负载
* @param token
* @return Claims类实例
*/
private Claims getClaimsFromToken(String token) {
Claims claims = null;
try {
claims = Jwts.parser()
.setSigningKey(secret)
.parseClaimsJws(token)
.getBody();
} catch (Exception e) {
LOGGER.info("JWT格式验证失败:{}",token);
}
return claims;
}
/**
* 根据expiration生成token的过期时间
* @return Date
*/
private Date generateExpirationDate() {
return new Date(System.currentTimeMillis() + expiration * 1000);
}
/**
* 从token中获取登录用户名
*/
public String getUserNameFromToken(String token) {
String username;
try {
Claims claims = getClaimsFromToken(token);
username = claims.getSubject();
} catch (Exception e) {
username = null;
}
return username;
}
/**
* 验证token是否还有效
*
* @param token 客户端传入的token
* @param userDetails 从数据库中查询出来的用户信息
*/
public boolean validateToken(String token, UserDetails userDetails) {
String username = getUserNameFromToken(token);
return username.equals(userDetails.getUsername()) && !isTokenExpired(token);
}
/**
* 判断token是否已经失效
*/
private boolean isTokenExpired(String token) {
Date expiredDate = getExpiredDateFromToken(token);
return expiredDate.before(new Date());
}
/**
* 从token中获取过期时间
*/
private Date getExpiredDateFromToken(String token) {
Claims claims = getClaimsFromToken(token);
return claims.getExpiration();
}
/**
* 根据用户信息生成token
* @param userDetails UserDetails类实例
* @return token
*/
public String generateToken(UserDetails userDetails) {
Map<String, Object> claims = new HashMap<>();
claims.put(CLAIM_KEY_USERNAME, userDetails.getUsername());
claims.put(CLAIM_KEY_CREATED, new Date());
return generateToken(claims);
}
}