JWT详解&工具类封装
JWT简介
JWT 是 JSON Web Token 的简称,是目前比较流行的用户身份验证解决方案。
JWT的数据结构
JWT 从本质上来说就是一个字符串,中间用点(.)分隔成三个部分:头部、负载、签名。
一个简单的 JWT 如下图所示:
Header
Header 部分用于描述 JWT 的元数据。
{
"alg": "HS256",
"typ": "JWT"
}
- alg 属性:签名的算法(algorithm),默认是 SHA256;
- typ 属性:这个令牌(token)的类型(type),JWT 令牌统一写为 JWT 。
Payload
Payload 部分用来存放实际需要传递的数据。JWT 规定了7个官方字段:
- iss (issuer):签发人
- exp (expiration time):过期时间
- sub (subject):主题
- aud (audience):受众
- nbf (Not Before):生效时间
- iat (Issued At):签发时间
- jti (JWT ID):编号
除了官方字段,还可以在这个部分定义私有字段,下面就是一个例子。
{
"sub": "1234567890",
"name": "John Doe",
"admin": true
}
注意: JWT 默认是不加密的,任何人都可以读到,不要把秘密信息放在这个部分。
Signature
Signature 部分是对前两部分的签名,防止数据篡改。
首先,需要指定一个密钥(secret)。这个密钥只有服务器才知道,不能泄露给用户。然后,使用 Header 里面指定的签名算法,按照下面的公式生成签名。
HMACSHA256(
base64UrlEncode(header) + "." +
base64UrlEncode(payload),
secret)
签名生成后,把 Header、Payload、Signature 三个部分拼成一个字符串,每个部分之间用"点"(.)分隔,就可以返回给用户。
在Spring Boot Web项目中使用JWT
1、引入 Maven 类库
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.9.1</version>
</dependency>
2、工具类封装
笔者主要对JWT工具类做了如下封装:
- 定义默认属性
- 生成 token
- 解析 token - 获取 username
- 解析 token - 根据键值获取属性值
2.1、定义默认属性
/**
* 默认签名密钥
*/
private static final String DEFAULT_SECRET_KEY = "thisisasimpledemoicomefromshandongprovincejiningcityrenchengcountryanjuzhenhelloworld";
/**
* 签发者
*/
private static final String ISSUER = "jasmine";
笔者定义了两个默认属性:签发者和默认签名密钥。
2.2、签名加密方法
public static String getBase64SecurityKey() {
return Base64.getEncoder().encodeToString(DEFAULT_SECRET_KEY.getBytes(StandardCharsets.UTF_8));
}
该方法主要是对签名进行 Base64 加密处理,生成的签名加密字符串主要用于生成 token 和解析 token 。
2.3、生成 token
/**
* 生成 Token
*/
public static String createToken(String username, Map<String, Object> claims) {
byte[] keyBytes = Decoders.BASE64.decode(getBase64SecurityKey());
Key key = Keys.hmacShaKeyFor(keyBytes);
return Jwts.builder()
// 受众
.setAudience(username)
// 签发者
.setIssuer(ISSUER)
.setClaims(claims)
// 面向的用户
.setSubject(username)
// 签发时间
.setIssuedAt(new Date())
// 签名算法、签名密钥
.signWith(key, SignatureAlgorithm.HS512)
.compact();
}
生成 token 笔者这里主要设置了受众、签发者、面向的用户、签发时间、签名算法、签名密钥。参数claims
中除官方定义字段外,还可以添加自定义字段。
2.4、获取 username 及属性值
/**
* 从 Token 中获取 username
*/
public static String getUsernameFromToken(String token) {
try {
final Claims claims = getClaimsFromToken(token);
if (Objects.isNull(claims) || StringUtils.isEmpty(claims.getSubject())) {
return null;
}
return claims.getSubject();
} catch (Exception e) {
LOGGER.error("获取用户名称失败,异常信息:", e);
return null;
}
}
/**
* 根据键值从 Token 中获取属性值
*/
public static String get(String token, String key) {
Claims claims = getClaimsFromToken(token);
if (Objects.isNull(claims) || StringUtils.isBlank(key)) {
return null;
}
Object value = claims.get(key);
return Objects.isNull(value) ? null : String.valueOf(value);
}
public static Claims getClaimsFromToken(String token) {
Claims claims;
try {
byte[] keyBytes = Decoders.BASE64.decode(getBase64SecurityKey());
Key key = Keys.hmacShaKeyFor(keyBytes);
claims = Jwts.parserBuilder()
.setSigningKey(key)
.build()
.parseClaimsJws(token)
.getBody();
} catch (Exception e) {
if (e instanceof ExpiredJwtException) {
throw new RuntimeException("TOKEN超时,请重新登录。");
} else {
LOGGER.error("解析Token信息时发生异常:", e);
claims = null;
}
}
return claims;
}
2.5、测试
public static void main(String[] args) {
Map<String, Object> userMap = Maps.newHashMap();
userMap.put("roleId", "1");
userMap.put("orgId", "1");
String token = createToken("admin", userMap);
System.out.printf("token: %s%n", token);
System.out.printf("username: %s%n", getUsernameFromToken(token));
System.out.printf("roleId: %s%n", get(token, "roleId"));
}
输出结果:
token: eyJhbGciOiJIUzUxMiJ9.eyJyb2xlSWQiOiIxIiwib3JnSWQiOiIxIiwic3ViIjoiYWRtaW4iLCJpYXQiOjE2NjYwODQzODh9.ZkAjtpBSQCVbHAHyIcKzNGE-S-L5MC3kgH_d2ekDnXmq-_K3kdmy4jyY7Qmbx9LqAr-8YzsohWHC2V6pkL9FJQ
username: admin
roleId: 1
- 点赞
- 收藏
- 关注作者
评论(0)