5 分钟认识JWT
JWT,全称为 JSON Web Token,是一种用于身份验证的标准。它使用 JSON 格式将信息进行编码,可以被签名和加密。JWT 通常用于 Web 应用程序中,用于验证用户身份。
JWT 由三部分组成:头部(header)、载荷(payload)和签名(signature)。
头部包含了使用的算法和令牌类型等信息,例如:
{
"alg":"HS256",
"type":"JWT"
}
载荷包含了需要传递的信息,例如:
{
"sub": "1653945284140511233",
"iat": 1683016853
}
签名用于验证消息的完整性,防止消息被篡改。
将Header和Payload用Base64URL编码后,再用点(.)连接起来。然后使用签名算法和密钥对这个字符串进行签名列如:
//伪代码
signature = HMACSHA256(header + "." + payload, secret);
生成签名的时候需要指定一个密码(secret)。该密码保存在服务器中,并且不能向用户公开。然后,使用标头中指定的签名算法根据以下公式生成签名。signature = HMACSHA256(header + "." + payload, secret); 在计算出签名哈希后,JWT头,有效载荷和签名哈希的三个部分组合成一个字符串,每个部分用"."分隔,就构成整个JWT对象。 以上三部分都是在服务器定义,当用户登陆成功后,根据用户信息,按照jwt规则生成token返回给客户端。
JWT 的工作流程如下:
- 用户提供用户名和密码进行登录。
- 服务器验证用户名和密码的正确性,并生成一个 JWT。
- 服务器将 JWT 发送给客户端。
- 客户端将 JWT 存储在本地。
- 客户端在后续的请求中将 JWT 添加到请求头中。
- 服务器验证 JWT 的有效性,并根据其中的信息进行相应的操作。
JWT 的优点是可以在跨域和分布式系统中进行身份验证,不需要使用 Session 或 Cookie,可以减少服务器端的存储和网络传输压力。但是需要注意的是,由于 JWT 是基于文本的标准,因此在网络传输中存在被篡改的风险。因此,在使用 JWT 进行身份验证时需要注意安全性。
JwtUtil 工具类封装
在 pom.xml 文件中引入 jjwt 依赖
<!--jwt-->
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.9.1</version>
</dependency>
配置文件设置密钥和过期时间
jwt.secret-key=xiaohuo@163.com!!
jwt.exp-time=PT1H
定义 JwtUtil 类
@Component
@Slf4j
public class JwtUtil {
@Value("${jwt.secret-key}")
private String secretKey;
@Value("${jwt.exp-time}")
private Duration expTime;
/**
* 签发token
* @param subject:我一般用来存用户id
* @param claims:存储在jwt的内容,一般不能放置敏感信息
* @return
*/
public String createToken(String subject, Map<String,Object> claims){
JwtBuilder builder= Jwts.builder();
/**
* 负载主要存一些放置在jwt的信息
* 不建议存敏感信息
*/
if(claims!=null){
builder.setClaims(claims);
}
/**
* 主题 一般存用户id
*/
if(StringUtils.hasLength(subject)){
builder.setSubject(subject);
}
long currentMillis=System.currentTimeMillis();
/**
* 签发时间
*/
builder.setIssuedAt(new Date(currentMillis));
long millis= expTime.toMillis();
/**
* 过期时间
*/
if(millis>0){
long expMillis=currentMillis+millis;
builder.setExpiration(new Date(expMillis));
}
/**
* 签名密钥 不要对外公布
*/
if(StringUtils.hasLength(secretKey)){
SignatureAlgorithm signatureAlgorithm=SignatureAlgorithm.HS256;
builder.signWith(signatureAlgorithm, DatatypeConverter.parseBase64Binary(secretKey));
}
return builder.compact();
}
/**
* 解析token
*/
public Claims pareToken(String token){
try {
Claims claims=Jwts.parser()
.setSigningKey(DatatypeConverter.parseBase64Binary(secretKey))
.parseClaimsJws(token).getBody();
return claims;
} catch (ExpiredJwtException e) {
log.error("ExpiredJwtException:{}",e);
} catch (UnsupportedJwtException e) {
log.error("UnsupportedJwtException:{}",e);
} catch (MalformedJwtException e) {
log.error("MalformedJwtException:{}",e);
} catch (SignatureException e) {
log.error("SignatureException:{}",e);
} catch (IllegalArgumentException e) {
log.error("IllegalArgumentException:{}",e);
}
return null;
}
/**
* 获取用户id方法
* @param token
* @return
*/
public String getUserId(String token){
Claims claims = pareToken(token);
if(claims!=null){
return claims.getSubject();
}
return null;
}
/**
* 校验token是否符合要求
* tue:校验通过
* false:校验失败
*/
public boolean validateToken(String token){
try {
Claims claims = pareToken(token);
Date exp= claims.getExpiration();
return exp.after(new Date());
} catch (Exception e) {
log.error("validateToken:{}",e);
return false;
}
}
}