JJwt 使用
<!--JWT令牌-->
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.12.5</version>
</dependency>
JWT(JSON Web Token)令牌登录,是一种用于身份验证的开放标准。 它是一个基于 JSON 格式的安全令牌,主要用于在网络上传输声明或者用户身份信息。 JWT 通常被用作 API 的认证方式,以及跨域身份验证。
jwt 优点:
- 支持跨域访问:cookie 是无法跨域的,而 token 由于没有用到 cookie (前提是将 token 放到请求头中),所以跨域后不会存在信息丢失问题
- 无状态:token 机制在服务端不需要存储 session 信息,因为 token 自身包含了所有登录用户的信息,所以可以减轻服务端压力
- 更适用 CDN:可以通过内容分发网络请求服务端的所有资料
- 更适用于移动端:当客户端是非浏览器平台时,cookie 是不被支持的,此时采用 token 认证方式会简单很多
- 无需考虑 CSRF:由于不再依赖 cookie,所以采用 token 认证方式不会发生 CSRF,所以也就无需考虑 CSRF 的防御
JWT 令牌由三部分组成,分别是头部(Header)、载荷(Payload)和签名(Signature):
Header:记录令牌类型,加密算法。
Payload:包含声明,声明是有关实体(通常是用户)和其他数据的语句。
已注册声明:这些是一组预定义的声明,这些声明不是必需的,但建议使用,以提供一组有用的、可互操作的声明。其中一些是:iss(发行人),exp(到期时间),sub(主题),aud(受众)等。
注册的声明(建议但不强制使用): iss: jwt签发者 sub: jwt所面向的用户 aud: 接收jwt的一方 exp: jwt的过期时间,这个过期时间必须要大于签发时间 nbf: 定义在什么时间之前,该jwt都是不可用的. iat: jwt的签发时间 jti: jwt的唯一身份标识,主要用来作为一次性token,从而回避重放攻击。
公共声明:这些可以由使用 JWT 的人随意定义。但为了避免冲突,它们应该在 IANA JSON Web 令牌注册表中定义,或者定义为包含抗冲突命名空间的 URI。
专用声明:这些是创建的自定义声明,用于在同意使用它们的各方之间共享信息,既不是注册声明也不是公共声明。
Signature:签名,防止被篡改。(header+payload+密钥)*签名算法。
JWT 两种主要使用方式
JWS (JSON Web Signature):
<header>.<payload>.<signature>
—— 签名的 JWT 由三个部分组成:头部(Header)、载荷(Payload)和签名(Signature)。这种方式下,JWT 是签名的,确保消息的完整性和真实性。签名的目的是防止数据被篡改,并验证发送者的身份。常见的签名算法包括 HMAC、RSA 和 ECDSA。常见:登录功能在一般情况下,不需要在用户端保存敏感信息,也就不用加密处理;但需要保证用户请求不被伪造、篡改,就需要进行数字前面。所以一般用 jws 实现登录验证。
JWE (JSON Web Encryption):
<header>.<encryptedKey>.<iv>.<ciphertext>.<tag>
—— 加密的 JWT 包含五个部分:头部(Header)、加密密钥(Encrypted Key)、初始化向量(Initialization Vector)、密文(Ciphertext)和认证标签(Authentication Tag)。这种方式下,JWT 是加密的,用于保护敏感数据。加密的目的是确保只有授权方能够读取数据内容。常见的加密算法包括 AES 和 RSA。常见:加密的 JWT 对载荷进行加密处理,使得未经授权的一方无法读取载荷内容。
package com.ioufev;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.security.*;
import javax.crypto.SecretKey;
import java.nio.charset.StandardCharsets;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
public class Main {
public static void main(String[] args) {
// 使用固定密钥:使用固定的字符串生成 HMAC 密钥,方便在开发和测试时使用。
// String secretKey = "r-k.Uv4D@rrX2aYiLOJJC-)!XBD=-#[^i,&vykXQYtU6p.pF4'xQ#GZ-4AS+ri)vhBEAyQFfpZ+";
// SecretKey key = Keys.hmacShaKeyFor(secretKey.getBytes(StandardCharsets.UTF_8));
// 根据物理地址哈希得到,服务每次重启都不一样。
// 使用生成密钥:使用 HS512 算法生成对称密钥。每次生成的密钥不同,因为是基于系统资源(如物理地址)的哈希值。
MacAlgorithm alg = Jwts.SIG.HS512; //or HS384 or HS256
SecretKey key = alg.key().build();
// 设置自定义声明::可选的自定义的声明,即设置到信息中的键值对
Map<String, Object> inputClaims = new HashMap<>();
inputClaims.put("name", "张三");
// 签名方式或者加密方式,二选一
String token = Jwts.builder()
.issuer("auth-server") // 可选,令牌的发行者,在解析 JWT 时可以验证这个值来确保它是由预期的授权服务器签发的。
.subject("李四")
.claims(inputClaims)
.audience().add("api-client").and() // 可选,令牌的受众, 在解析 JWT 时可以验证这个值,确保令牌是为特定的客户端或 API 生成的。
// .notBefore(notBefore) // 可选,java.util.Date,不早于
.issuedAt(new Date()) // 设置签发时间为当前时间。
.expiration(new Date(System.currentTimeMillis() + 30 * 60 * 1000)) // 设置 JWT 的过期时间为 30 分钟后。
.id(UUID.randomUUID().toString()) // 设置一个随机生成的唯一标识符。
.signWith(key) // 签名方式
// .encryptWith(key, Jwts.ENC.A256CBC_HS512) // 加密方式,使用 AES 对称加密方式对 JWT 进行加密。
.compact(); // 生成 JWT 并压缩为一个字符串形式
System.out.println(token);
// 解析,对应的签名或者加密方式
Claims claims = Jwts.parser()
.verifyWith(key) // 指定用于验证的密钥
// .decryptWith(key) // 加密方式,指定用于解密的密钥。
.build()
.parseSignedClaims(token) // 签名方式
// .parseEncryptedClaims(token) // 加密方式,对 JWT 进行解密,并获取其声明部分。
.getPayload(); // 获取解密后的声明(claims)内容。
System.out.println(claims.getSubject());
System.out.println(claims.get("name"));
}
}
package com.ioufev;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.security.*;
import javax.crypto.SecretKey;
import java.nio.charset.StandardCharsets;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
public class Main {
public static void main(String[] args) {
// 使用固定密钥:使用固定的字符串生成 HMAC 密钥,方便在开发和测试时使用。
// String secretKey = "r-k.Uv4D@rrX2aYiLOJJC-)!XBD=-#[^i,&vykXQYtU6p.pF4'xQ#GZ-4AS+ri)vhBEAyQFfpZ+";
// SecretKey key = Keys.hmacShaKeyFor(secretKey.getBytes(StandardCharsets.UTF_8));
// 根据物理地址哈希得到,服务每次重启都不一样。
// 使用生成密钥:使用 HS512 算法生成对称密钥。每次生成的密钥不同,因为是基于系统资源(如物理地址)的哈希值。
MacAlgorithm alg = Jwts.SIG.HS512; //or HS384 or HS256
SecretKey key = alg.key().build();
// 设置自定义声明::可选的自定义的声明,即设置到信息中的键值对
Map<String, Object> inputClaims = new HashMap<>();
inputClaims.put("name", "张三");
// 签名方式或者加密方式,二选一
String token = Jwts.builder()
.issuer("auth-server") // 可选,令牌的发行者,在解析 JWT 时可以验证这个值来确保它是由预期的授权服务器签发的。
.subject("李四")
.claims(inputClaims)
.audience().add("api-client").and() // 可选,令牌的受众, 在解析 JWT 时可以验证这个值,确保令牌是为特定的客户端或 API 生成的。
// .notBefore(notBefore) // 可选,java.util.Date,不早于
.issuedAt(new Date()) // 设置签发时间为当前时间。
.expiration(new Date(System.currentTimeMillis() + 30 * 60 * 1000)) // 设置 JWT 的过期时间为 30 分钟后。
.id(UUID.randomUUID().toString()) // 设置一个随机生成的唯一标识符。
// .signWith(key) // 签名方式
.encryptWith(key, Jwts.ENC.A256CBC_HS512) // 加密方式,使用 AES 对称加密方式对 JWT 进行加密。
.compact(); // 生成 JWT 并压缩为一个字符串形式
System.out.println(token);
// 解析,对应的签名或者加密方式
Claims claims = Jwts.parser()
.verifyWith(key) // 指定用于验证的密钥
.decryptWith(key) // 加密方式,指定用于解密的密钥。
.build()
// .parseSignedClaims(token) // 签名方式
.parseEncryptedClaims(token) // 加密方式,对 JWT 进行解密,并获取其声明部分。
.getPayload(); // 获取解密后的声明(claims)内容。
System.out.println(claims.getSubject());
System.out.println(claims.get("name"));
}
}