前言:
大家好,我是Felix。祝大家周一愉快!今天我们来细致的讨论一下jwt,以及jwt的使用姿势。
什么是jwt
在此之前,我们先了解一下什么是token。只要是有过项目经验的朋友,应该都知道,token就是一个令牌,相当于是一个认证。用户初次登陆,服务端会生成一个token,前端会保存起来。客户端下次来访问我的应用,会带着token过来,如果我验证后,你的token是正确,且未过期,我就可以让你访问我的应用,否则跳转到登陆页面。
但是,传统这种token会有一些问题,如下:
服务端会保存token,这样的话,服务端相对来说会有存储压力 |
---|
token能够存储的信息有限,如果说我还想要通过token拿到用户的其他信息,那就避免不了db的访问 |
token缺少一定的安全性,token一般都是直接对于用户信息进行加密存储,比如base加密等 |
不太容易扩展,因为存储在服务端,当你新增服务或者做集群时候,都会有额外的工作量 |
因此,为了解决这些问题,jwt也就应运而生。JWT是json web token。 可以说它是token的升级版,具有良好的安全性和可扩展性,灵活性。
jwt的构成
jwt由三部分构成
- header
- payload
- signature
header
头部通常由两部分构成,token的类型以及签名所使用的算法,然后通过BASE64加密后即得到了header部分
{
"alg": "HS256",
"typ": "JWT"
}
payload
载荷是一组声明,这些声明包含了有关用户的信息。声明被分为三种类型:注册声明、公共声明和私有声明。
- 注册声明:这些是一组预定义的声明,不是强制的,但建议使用,以提供一组有用的、可互操作的声明。例如:iss(签发者)、exp(过期时间)、sub(主题)、aud(受众)等。
- 公共声明:这些是可以由使用JWT的人随意定义的声明。
- 私有声明:这些是在同意使用这些声明的各方之间共享的自定义声明。
{
"sub": "1234567890",
"name": "John Doe",
"iat": 1516239022
}
signature
签名用于验证消息的完整性和真实性。它是通过将头部和载荷进行编码,然后使用密钥和头部中指定的算法进行签名计算得到的。
HMACSHA256(
base64UrlEncode(header) + "." +
base64UrlEncode(payload),
secret
)
最终,jwt就类似于下方的例子(由三部分构成,用 . 隔开)
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
那在java中如何使用JWT呢
可以看到在项目中,使用JWT就是这样一个步骤,那如何去生成JWT以及如何去校验呢?
首先要引入必要的依赖
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.9.1</version>
</dependency>
定义一个工具类
这里我弄得比较简单,搞了一个工具类,大家可以在项目中做自定义的扩展
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
public class JwtUtil {
// 密钥,应该保存在安全的地方,不要硬编码在代码中
private static final String SECRET_KEY = "your_secret_key";
// 生成JWT
public static String generateToken(String userId) {
Map<String, Object> claims = new HashMap<>();
claims.put("sub", userId);
claims.put("iat", new Date());
return Jwts.builder()
.setClaims(claims)
.signWith(SignatureAlgorithm.HS256, SECRET_KEY)
.compact();
}
// 验证JWT
public static boolean validateToken(String token) {
try {
Jwts.parser().setSigningKey(SECRET_KEY).parseClaimsJws(token);
return true;
} catch (Exception e) {
return false;
}
}
// 解析JWT获取声明
public static Map<String, Object> getClaims(String token) {
return Jwts.parser().setSigningKey(SECRET_KEY).parseClaimsJws(token).getBody();
}
public static void main(String[] args) {
String token = generateToken("123");
System.out.println("Generated Token: " + token);
boolean isValid = validateToken(token);
System.out.println("Is Token Valid: " + isValid);
Map<String, Object> claims = getClaims(token);
System.out.println("Claims: " + claims);
}
}
我们把结果输出,就可以看到JWT的组成,以及校验是否有效,以及可以看到jwt声明中的东西。
当然,这个声明其实很自由,你可以自定义你的key以及value。
当然,我们可以把这个校验放到web项目的过滤器里面,这样就具有通用性了,大家可以试一下,至于过滤器如何使用可以参考我之前的文章。