JWT(JSON Web Token)是一种用于在不同应用之间安全传输信息的开放标准(RFC 7519)。它是一种基于 JSON 的轻量级令牌,由三部分组成:头部(Header)、载荷(Payload)和签名(Signature)。JWT 被广泛用于实现身份验证和授权,特别适用于前后端分离的应用程序。
令牌类似下面这一大长串:
eyJhbGciOiJIUzUxMiJ9.eyJzdWIiOiJxdWFueGlhb2hhIiwiaXNzIjoicXVhbnhpYW9oYSIsImlhdCI6MTY5Mjk1OTY2MSwiZXhwIjoxNjkyOTYzMjYxfQ.wbqbn23C9vAe5sQZRCBzrIM4SiN1eNl55NIONmHoiPHPHSSu0QJGgPGUin80hA4XgMHEqN1Wm5KJlmKKucUyGQ
可以看到,由 header.payload.signature
三部分组成,你可以在此网站: https://jwt.io/
JWT 提供了一种在客户端和服务器之间传输安全信息的简单方法,具有以下优点:
- 无状态性(Stateless):JWT 本身包含了所有必要的信息,无需在服务器端存储会话信息,每个请求都可以独立验证。
- 灵活性:JWT 可以存储任意格式的数据,使其成为传递用户信息、权限、角色等的理想选择。
- 安全性:JWT 使用签名进行验证,确保信息在传输过程中不被篡改。
- 跨平台和跨语言:由于 JWT 使用 JSON 格式,它在不同的编程语言和平台之间都可以轻松传递。
导入依赖
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-api</artifactId>
<version>0.11.5</version>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-impl</artifactId>
<version>0.11.5</version>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-jackson</artifactId>
<version>0.11.5</version>
</dependency>
Jwt工具类
刚开始可以使用 generateBase64Key
来生成基于Hs512 算法的一个随机私钥,再进行一次Base64加密。
同时又使用 spring 的 @Value 方法提供 yml 配置项来配置 issuer 签发人和 secret 私钥。
import io.jsonwebtoken.*;
import io.jsonwebtoken.security.Keys;
import io.jsonwebtoken.security.SignatureException;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.CredentialsExpiredException;
import org.springframework.stereotype.Component;
import java.security.Key;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.Base64;
import java.util.Date;
@Component
public class JwtTokenHelper implements InitializingBean {
/**
* 签发人
*/
@Value("${jwt.issuer}")
private String issuer;
/**
* 秘钥
*/
private Key key;
/**
* JWT 解析
*/
private JwtParser jwtParser;
/**
* 解码配置文件中配置的 Base 64 编码 key 为秘钥
* @param base64Key
*/
@Value("${jwt.secret}")
public void setBase64Key(String base64Key) {
key = Keys.hmacShaKeyFor(Base64.getDecoder().decode(base64Key));
}
/**
* 初始化 JwtParser
* @throws Exception
*/
@Override
public void afterPropertiesSet() throws Exception {
// 考虑到不同服务器之间可能存在时钟偏移,setAllowedClockSkewSeconds 用于设置能够容忍的最大的时钟误差
jwtParser = Jwts.parserBuilder().requireIssuer(issuer)
.setSigningKey(key).setAllowedClockSkewSeconds(10)
.build();
}
/**
* 生成 Token
* @param username
* @return
*/
public String generateToken(String username) {
LocalDateTime now = LocalDateTime.now();
// Token 一个小时后失效
LocalDateTime expireTime = now.plusHours(1);
return Jwts.builder().setSubject(username)
.setIssuer(issuer)
.setIssuedAt(Date.from(now.atZone(ZoneId.systemDefault()).toInstant()))
.setExpiration(Date.from(expireTime.atZone(ZoneId.systemDefault()).toInstant()))
.signWith(key)
.compact();
}
/**
* 解析 Token
* @param token
* @return
*/
public Jws<Claims> parseToken(String token) {
try {
return jwtParser.parseClaimsJws(token);
} catch (SignatureException | MalformedJwtException | UnsupportedJwtException | IllegalArgumentException e) {
throw new BadCredentialsException("Token 不可用", e);
} catch (ExpiredJwtException e) {
throw new CredentialsExpiredException("Token 失效", e);
} }
/**
* 生成一个 Base64 的安全秘钥
* @return
*/
private static String generateBase64Key() {
// 生成安全秘钥
Key secretKey = Keys.secretKeyFor(SignatureAlgorithm.HS512);
// 将密钥进行 Base64 编码
String base64Key = Base64.getEncoder().encodeToString(secretKey.getEncoded());
return base64Key;
}
public static void main(String[] args) {
String key = generateBase64Key();
System.out.println("key: " + key);
}}