使用 Spring Boot 实现 JWT 生成与验证的自定义类
在现代 web 应用中,JWT(JSON Web Tokens)被广泛用于用户身份验证。本文将展示如何创建一个自定义的 JWT 生成与验证类 JwtPlus
,该类使用对称加密算法,并支持灵活的配置选项。我们将通过以下步骤实现这个功能:
1. 背景介绍
JWT 的结构通常包含三个部分:头部(Header)、载体(Payload)和签名(Signature)。通过对这三个部分进行编码和加密,JWT 能够安全地传递用户信息。有效的 JWT 需要在客户端和服务器之间传递,因此确保它们的安全性是至关重要的。
2. 主要功能需求
- 生成 JWT:支持自定义 Header 和 Claims,并使用对称加密算法生成签名。
- 验证 JWT:能够验证签名的有效性,并提取载体部分的数据。
- 支持自定义算法:允许开发者使用不同的加密算法。
3. 实现步骤
3.1. 创建 Algorithm
类
该类用于定义加密算法及其密钥:
public class Algorithm { private String name; private String key; public Algorithm(String name, String key) { this.name = name; this.key = key; } public String getName() { return name; } public String getKey() { return key; } }
3.2. 创建 JwtPlus
类
这是我们主要的 JWT 处理类,包含生成和验证的逻辑。
import javax.crypto.Mac; import javax.crypto.spec.SecretKeySpec; import java.nio.charset.StandardCharsets; import java.util.Base64; import java.util.HashMap; import java.util.Map; public class JwtPlus { private Map<String, String> header; private Map<String, String> claims; private Algorithm algorithm; // 私有构造函数,防止直接实例化private JwtPlus(Builder builder) { this.header = builder.header; this.claims = builder.claims; this.algorithm = builder.algorithm; } // 静态方法,用于获取 Builder 实例public static Builder builder() { return new Builder(); } // 静态方法,用于获取 JwtVerifier 实例public static JwtVerifier require() { return new JwtVerifier(); } // 生成 Token 方法public String sign() { if (header == null || header.isEmpty()) { header = new HashMap<>(); header.put("alg", algorithm.getName()); header.put("typ", "JWT"); } String headerEncoded = encodeBase64(mapToString(header)); String claimsEncoded = encodeBase64(mapToString(claims)); String signature = generateSignature(headerEncoded, claimsEncoded); return headerEncoded + "." + claimsEncoded + "." + signature; } // 生成签名private String generateSignature(String header, String claims) { try { String data = header + "." + claims; Mac mac = Mac.getInstance(algorithm.getName()); SecretKeySpec secretKeySpec = new SecretKeySpec(algorithm.getKey().getBytes(StandardCharsets.UTF_8), algorithm.getName()); mac.init(secretKeySpec); byte[] signatureBytes = mac.doFinal(data.getBytes(StandardCharsets.UTF_8)); return encodeBase64(new String(signatureBytes, StandardCharsets.UTF_8)); } catch (Exception e) { throw new RuntimeException("Error generating signature", e); } } // 内部类 JwtVerifierpublic static class JwtVerifier { private String secretKey; private Algorithm algorithm; public JwtVerifier secret(String secretKey) { this.secretKey = secretKey; return this; } public JwtVerifier algorithm(Algorithm algorithm) { this.algorithm = algorithm; return this; } public boolean verify(String token) { String[] parts = token.split("\\."); if (parts.length != 3) { return false; } String headerEncoded = parts[0]; String claimsEncoded = parts[1]; String signature = parts[2]; String expectedSignature = JwtPlus.builder() .claims(new HashMap<>()) // 空 claims 仅用于签名 .algorithm(algorithm) .build() .generateSignature(headerEncoded, claimsEncoded); return expectedSignature.equals(signature); } public Map<String, String> getClaims(String token) { String[] parts = token.split("\\."); if (parts.length != 3) { throw new IllegalArgumentException("Invalid Token format"); } String claimsEncoded = parts[1]; return stringToMap(decodeBase64(claimsEncoded)); } } // 其他方法保持不变 private static String encodeBase64(String data) { return Base64.getUrlEncoder().withoutPadding().encodeToString(data.getBytes(StandardCharsets.UTF_8)); } private static String decodeBase64(String data) { return new String(Base64.getUrlDecoder().decode(data), StandardCharsets.UTF_8); } private static String mapToString(Map<String, String> map) { StringBuilder sb = new StringBuilder(); for (Map.Entry<String, String> entry : map.entrySet()) { sb.append(entry.getKey()).append("=").append(entry.getValue()).append(";"); } return sb.toString(); } private static Map<String, String> stringToMap(String token) { Map<String, String> map = new HashMap<>(); String[] pairs = token.split(";"); for (String pair : pairs) { String[] keyValue = pair.split("="); if (keyValue.length == 2) { map.put(keyValue[0], keyValue[1]); } } return map; } // Builder 类public static class Builder { private Map<String, String> header = new HashMap<>(); private Map<String, String> claims = new HashMap<>(); private Algorithm algorithm = new Algorithm("HmacSHA256", "default_secret_key"); public Builder withHeader(Map<String, String> header) { this.header.putAll(header); return this; } public Builder claim(String key, String value) { this.claims.put(key, value); return this; } public Builder claims(Map<String, String> claims) { this.claims.putAll(claims); return this; } public Builder algorithm(Algorithm algorithm) { this.algorithm = algorithm; return this; } public JwtPlus build() { return new JwtPlus(this); } } }
4. 调用示例
以下是如何使用 JwtPlus
和 JwtVerifier
的示例:
public class TokenExample {public static void main(String[] args) { // 使用 builder 模式生成 Token Algorithm algorithm = new Algorithm("HmacSHA256", "your_secret_key"); String token = JwtPlus.builder() .claim("userId", "12345") .claim("role", "admin") .algorithm(algorithm) .build() .sign(); System.out.println("Generated Token: " + token); // 使用 JwtVerifier 验证 Token JwtPlus.JwtVerifier verifier = JwtPlus.require() .secret("your_secret_key") .algorithm(algorithm); boolean isValid = verifier.verify(token); System.out.println("Is Token valid? " + isValid); // 获取 Token 中的载体部分 Map<String, String> claims = verifier.getClaims(token); System.out.println("Claims: " + claims); } }
5. 实现原理与方法
- Token 结构:JWT 由三部分组成,分别为 Header、Payload 和 Signature。Header 指定了所使用的签名算法,Payload 存放用户信息,Signature 则是使用 Header 和 Payload 生成的。
- 生成 JWT:
- 使用 Builder 模式构建 JWT 对象。
- 编码 Header 和 Payload,并生成签名。
- 最终将三个部分通过点(
.
)连接成一个字符串。
- 验证 JWT:
- 使用
JwtVerifier
类来验证 JWT 的有效性。 - 验证过程包括拆分 Token,重新生成签名,并与原始签名进行比较。
- 灵活性:通过
Algorithm
类,可以方便地实现对称加密算法的切换,使得代码更加灵活且易于扩展。
结论
本文展示了如何使用 Java 创建一个自定义的 JWT 生成与验证类,具有灵活的算法选择和安全的签名生成。这种方法为开发现代 web 应用提供了可靠的身份验证机制,同时保持了代码的可维护性和扩展性。希望这能帮助你在项目中实现 JWT 认证!