该文章已同步收录到我的博客网站,欢迎浏览我的博客网站,xhang’s blog
1.什么是JWT
JWT(JSON Web Token)是一种用于身份验证和授权的开放标准。它是一种轻量级的、基于JSON的令牌,可以在客户端和服务器之间传递信息。JWT由三部分组成:头部、载荷和签名。头部包含令牌类型和所使用的算法,载荷包含用户信息和其他元数据,签名用于验证令牌的完整性和真实性。JWT的优点包括可扩展性、可靠性和安全性。
简单的说,JWT就是通过数字签名的方式,以JSON对象为载体的开发标准,可以在不同的服务终端之间安全的传输信息。
JWT官网:https://jwt.io/
2.JWT的应用场景
- 授权:==这是使用 JWT 的最常见方案。用户登录后,每个后续请求都将包含 JWT,允许用户访问使用该令牌允许的路由、服务和资源。==单点登录是当今广泛使用 JWT 的一项功能,因为它的开销很小,并且能够跨不同域轻松使用。
- 信息交换:JSON Web Token是在各方之间安全传输信息的好方法。由于 JWT 可以签名(例如,使用公钥/私钥对),因此您可以确定发件人是他们所说的人。此外,由于签名是使用标头和有效负载计算的,因此您还可以验证内容是否未被篡改。
3.JWT的结构
3.1JWT的结构组成
JSON Web 令牌的紧凑形式由点分隔的三个部分组成,分别是:
- Header
- Payload
- Signature
因此,JWT 通常如下所示。
xxxxx.yyyyy.zzzzz
- Header
Header通常由两部分组成:
令牌的类型(即 JWT)和正在使用的签名算法,例如 HMAC SHA256 或 RSA。
例如:
{
"alg": "HS256",
"typ": "JWT"
}
然后,此JSON被Base64Url编码以形成JWT的第一部分。
- Payload
令牌的第二部分是有效负载,其中包含声明。声明是有关实体(通常是用户)和其他数据的语句。
示例有效负载可以是:
{
"sub": "1234567890",
"name": "John Doe",
"admin": true
}
然后对有效负载进行 Base64Url 编码以形成 JSON Web 令牌的第二部分。
请注意,对于签名令牌,此信息虽然受到篡改保护,但任何人都可以读取。不要将机密信息放在 JWT 的有效负载或标头元素中,除非它已加密。
- Signature
要创建签名部分,您必须获取
Header
、Payload
、secret
以及Header
中指定的算法并对其进行签名。签名用于验证消息在此过程中未被更改,并且在使用私钥签名的令牌的情况下,它还可以验证 JWT 的发件人是否是它所说的人。
3.2JWT的结构
JWT输出是由点分隔的Header
、Payload
、Signature
的 Base64-URL 字符串,可以在 HTML 和 HTTP 环境中轻松传递,同时与基于 XML 的标准(如 SAML)相比更加紧凑。
下面显示了对以前的标头和有效负载进行了编码并使用机密进行签名的 JWT。
4.JWT如何工作
-
==在身份验证中,当用户使用其凭据成功登录时,将返回 JSON Web Token。==由于令牌是凭据,因此必须非常小心以防止安全问题。通常,令牌的保留时间不应超过所需时间。
-
==每当用户想要访问受保护的路由或资源时,用户都应发送 JWT,通常在使用持有者架构的授权请求头中发送,即
Authorization
。==请求头的内容应如下所示:
Authorization: Bearer <token>
- ==在某些情况下,这可能是无状态授权机制。服务器的受保护路由将检查标头中的有效 JWT,如果存在,将允许用户访问受保护的资源。==如果 JWT 包含必要的数据,则可能会减少查询数据库以获取某些操作的需要,尽管情况可能并非总是如此。
5.整合JWT
- 引入对应依赖
说明:JDK8
只需要引入以下依赖就可以
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.9.1</version>
</dependency>
JDK8
以上的版本需要引入以下已离开
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.9.1</version>
</dependency>
<dependency>
<groupId>javax.xml.bind</groupId>
<artifactId>jaxb-api</artifactId>
<version>2.3.1</version>
</dependency>
<dependency>
<groupId>com.sun.xml.bind</groupId>
<artifactId>jaxb-impl</artifactId>
<version>2.3.3</version>
</dependency>
<dependency>
<groupId>com.sun.xml.bind</groupId>
<artifactId>jaxb-core</artifactId>
<version>2.3.0</version>
</dependency>
- 定义JWT工具类,包括生成JWT和解析JWT
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import java.util.Base64;
import java.util.Date;
import java.util.UUID;
/**
* JWT工具类
*/
public class JwtUtils {
//JWT有效期
public static final Long JWT_TTL = 60 * 60 *1000L;// 60 * 60 *1000 一个小时
//设置签名信息(秘钥明文)
public static final String JWT_KEY = "xha";
/**
* 生成uuid
*
* @return {@link String}
*/
public static String getUUID(){
String token = UUID.randomUUID().toString().replaceAll("-", "");
return token;
}
/**
* 生成jtw,无过期时间,则使用设置的时间
* @param subject token中要存放的数据(json格式)
* @return
*/
public static String createJWT(String subject) {
return getJwtBuilder(subject, JwtUtils.JWT_TTL, getUUID());
}
/**
* 生成jtw,有过期时间。默认为1个小时
* @param subject token中要存放的数据(json格式)
* @param ttlMillis token超时时间
* @return
*/
public static String createJWT(String subject, Long ttlMillis) {
return getJwtBuilder(subject, ttlMillis, getUUID());// 设置过期时间
}
/**
* 生成jtw,自定义过期时间。
* @param id
* @param subject
* @param ttlMillis
* @return
*/
public static String createJWT(String id, String subject, Long ttlMillis) {
return getJwtBuilder(subject, ttlMillis, id);// 设置过期时间
}
/**
* 创建JWT
*
* @param subject 主题
* @param ttlMillis ttl米尔斯
* @param uuid uuid
* @return {@link String}
*/
private static String getJwtBuilder(String subject, Long ttlMillis, String uuid) {
// 1.根据签名信息生成加密后的秘钥 secretKey
SecretKey secretKey = generalKey();
// 2.获取到当前时间戳
long nowMillis = System.currentTimeMillis();
// 3.签发时间
Date now = new Date(nowMillis);
// 4.JWT的过期时间:当前时间戳+JWT过期时间
long expMillis = nowMillis + ttlMillis;
Date expDate = new Date(expMillis);
return Jwts.builder()
// 1.设置Header信息:令牌的类型和签名算法
.setHeaderParam("typ","JWT")
.setHeaderParam("alg","HS256")
// 2.设置Payload信息,可以是JSON数据
.setSubject(subject)
// 2.1唯一ID
.setId(uuid)
// 2.2签发者
.setIssuer("xha")
// 2.3签发时间
.setIssuedAt(now)
// 2.4设置JWT过期时间
.setExpiration(expDate)
// 3.设置Signature
// 使用HS256对称加密算法签名,第二个参数为秘钥,根据密钥进行加密
.signWith(SignatureAlgorithm.HS256, secretKey)
// 4.对JWT的三部分进行拼接
.compact();
}
/**
* 根据签名信息生成加密后的秘钥 secretKey
* @return
*/
public static SecretKey generalKey() {
// 对签名信息进行Base64Url编码
byte[] encodedKey = Base64.getDecoder().decode(JwtUtils.JWT_KEY);
// SecretKeySpec用于创建加密算法的密钥
SecretKey key = new SecretKeySpec(encodedKey, 0, encodedKey.length, "AES");
return key;
}
/**
* 解析JWT
*
* @param jwt
* @return
* @throws Exception
*/
public static Claims parseJWT(String jwt) throws Exception {
// 根据签名信息生成加密后的秘钥 secretKey
SecretKey secretKey = generalKey();
return Jwts.parser()
// 根据密钥进行解密
.setSigningKey(secretKey)
// JWT对象
.parseClaimsJws(jwt)
.getBody();
}
}
- 测试
public class TestJWT {
public static void main(String[] args) {
String userId = "123456";
String jwt = JwtUtils.createJWT(userId);
System.out.println(jwt);
}
}
控制台输出JWT
使用JWT官网的JWT解析工具进行解析JSON Web Tokens - jwt.io
解析JWT
public static void main(String[] args) {
String userId = "123456";
String jwt = JwtUtils.createJWT(userId);
System.out.println(jwt);
try {
Claims claims = JwtUtils.parseJWT(jwt);
System.out.println(claims);
} catch (Exception e) {
throw new RuntimeException(e);
}
}