Trae-Maven/jwt-security

GitHub: Trae-Maven/jwt-security

一个面向现代 Java Spring 应用的轻量级 JWT 安全库,提供 Ed25519 签名、令牌轮换和多层防护机制。

Stars: 0 | Forks: 0

# Jwt 安全 一个轻量级且灵活的 Java JWT 认证与安全库。 Jwt Security 提供了简单的工具和辅助程序,用于在 Java 应用程序中生成、验证和管理 JSON Web Token。 该库设计轻量、快速,并且易于集成到现有的基于 Spring 的 Java 应用程序中。 专为现代 Java (Java 21+) 构建,旨在与现有的 Spring 基础设施无缝集成。 ## 功能特性 - Ed25519 (EdDSA) 非对称签名 —— 无共享密钥,符合 FAPI 2.0 标准,通过 TLS 1.3 批准 - 为访问令牌和刷新令牌使用独立的密钥对(密钥隔离) - 可配置的令牌生命周期 —— 通过设置提供程序设置访问和刷新过期时间 - 确定性或临时的密钥对 —— 从主密钥派生以支持多实例,或在启动时生成全新的密钥对 - 令牌指纹绑定 —— 防止通过 XSS 窃取令牌 - 带有重用检测的刷新令牌轮换 —— 重放的令牌会触发完整的账户撤销 - 并发轮换宽限期 —— 防止来自并行浏览器请求的误报重用检测 - 恒定时间哈希比较,以防止时序侧信道攻击 - `lastTokenIssueAt` 验证 —— 无需黑名单即可实现即时全局令牌失效(仅限安全事件) - 生产环境中使用 `__Host-` Cookie 前缀 —— 浏览器强制执行 Secure + Path=/ + no Domain - JWT 令牌生成和验证 - 令牌解析和声明访问 - 轻量级安全工具 - 最小化的依赖项 - 专为现代 Java (Java 21+) 设计 - 易于集成到 Spring 应用程序 ## 环境要求 Jwt Security 专为基于 Spring 的 Web 应用程序设计。 您的项目必须已包含以下依赖项(这些通常已包含在大多数 Spring Boot 应用程序中): ``` org.projectlombok lombok 1.18.36 provided org.springframework spring-context 7.0.3 org.springframework spring-web 7.0.3 jakarta.servlet jakarta.servlet-api 6.0.0 org.bouncycastle bcprov-jdk18on 1.83 ``` 这些依赖项在 Jwt Security 中被标记为 **provided**,因为预期它们已存在于您的应用程序中。 ## 内置依赖 Jwt Security 包含若干依赖项,在您安装该库时会自动包含。 - [Utilities](https://github.com/Trae-Maven/utilities) – 框架内部使用的共享辅助类和专注于性能的工具。 ``` io.jsonwebtoken jjwt-api 0.12.5 io.jsonwebtoken jjwt-impl 0.12.5 io.jsonwebtoken jjwt-jackson 0.12.5 ``` 这些依赖项在安装 Jwt Security 时会自动包含,无需手动添加。 ## 安装 将依赖项添加到您的 Maven 项目中: ``` io.github.trae jwt-security 0.0.2 ``` ## 集成指南 Jwt Security 需要在您的应用程序中设置五个类。每个提供程序都是一个接口,由您根据自己的应用程序逻辑实现,而具体的 `JwtService` 子类消除了每个注入点冗长的泛型。 ### 1. 定义您的角色枚举 创建一个实现 `JwtAccountRoleProvider` 的角色枚举: ``` public enum Role implements JwtAccountRoleProvider { ADMINISTRATOR, MODERATOR, STANDARD } ``` ### 2. 实现您的账户实体 您的账户类必须使用您的角色枚举实现 `JwtAccountProvider`: ``` @AllArgsConstructor @Getter @Setter public class Account implements JwtAccountProvider { private UUID id; private Role role; private long lastTokenIssueAt; private RefreshToken refreshToken; @Override public boolean hasRole(final Role role) { return this.getRole().ordinal() >= role.ordinal(); } } ``` ### 3. 实现您的账户管理器 创建一个处理账户持久化的服务: ``` @AllArgsConstructor @Service public class AccountManager implements JwtAccountManagerProvider { private final AccountRepository accountRepository; @Override public Optional getAccountById(final UUID id) { return this.accountRepository.findById(id); } @Override public void updateAccountLastTokenIssueAt(final Account account) { this.accountRepository.updateLastTokenIssueAt(account.getId(), account.getLastTokenIssueAt()); } @Override public void updateAccountRefreshToken(final Account account) { this.accountRepository.updateRefreshToken(account.getId(), account.getRefreshToken()); } } ``` ### 4. 实现您的设置 使用您的环境设置、令牌生命周期和密钥派生策略配置 JWT 服务。 **确定性密钥**(推荐用于多实例部署): ``` @Component public class MyJwtSettings implements JwtSettingsProvider { @Override public boolean isProduction() { return true; } @Override public Duration getAccessTokenExpiration() { return Duration.ofMinutes(5); } @Override public Duration getRefreshTokenExpiration() { return Duration.ofDays(14); } @Override public String getIssuer() { return "myapp.com"; } @Override public byte[] getAccessTokenKeySeed() { return KeyDerivation.derive("my-master-secret:access"); } @Override public byte[] getRefreshTokenKeySeed() { return KeyDerivation.derive("my-master-secret:refresh"); } } ``` **令牌生命周期建议:** | 令牌 | 推荐 | 范围 | 说明 | |---|---|---|---| | Access | 5 分钟 | 1–15 分钟 | 时间越短 = 被盗令牌的风险越低。无服务器端撤销,因此生命周期是唯一的控制手段。 | | Refresh | 14 天 | 7–30 天 | 时间越长 = 强制重新登录的次数越少。轮换和重用检测降低了风险。 | **临时密钥**(每次重启都会使令牌失效): ``` @Override public byte[] getAccessTokenKeySeed() { return null; } @Override public byte[] getRefreshTokenKeySeed() { return null; } ``` ### 5. 创建您的 JwtService 子类 创建一个具体的子类,在一个位置绑定所有泛型类型。这避免了在应用程序的每个注入点重复 `JwtService`: ``` @Service public class MyJwtService extends JwtService { public MyJwtService(final MyJwtSettings settings, final AccountManager accountManager) { super(settings, accountManager); } } ``` 这是推荐的方法。您只需在这里定义一次泛型,然后在其他地方注入 `MyJwtService`,无需任何泛型噪音。 ## 密钥派生 Jwt Security 支持两种 Ed25519 密钥对管理模式。 ### 确定性密钥(推荐用于多实例) 当 `getAccessTokenKeySeed()` 和 `getRefreshTokenKeySeed()` 返回一个 32 字节的种子时,确定性 Ed25519 密钥对将使用 BouncyCastle 派生。每个具有相同主密钥的应用程序实例都会生成相同的密钥对 —— 无需共享密钥文件、挂载卷或密钥分发。 种子在密钥派生后立即从内存中擦除。 这是负载均衡器后面有多个实例的生产部署的推荐方法。 ### 内置 KeyDerivation 工具(可选) Jwt Security 附带了一个 `KeyDerivation` 工具类,该类使用带有 HMAC-SHA256 的 HKDF 从上下文字符串派生出确定性的 32 字节密钥。这会生成一个适合 Ed25519 密钥对派生的种子,可以直接与设置提供程序一起使用: ``` import io.github.trae.jwtsecurity.utility.KeyDerivation; @Override public byte[] getAccessTokenKeySeed() { return KeyDerivation.derive("my-master-secret:access"); } @Override public byte[] getRefreshTokenKeySeed() { return KeyDerivation.derive("my-master-secret:refresh"); } ``` 这完全是可选的 —— 您可以使用任何密钥派生策略(HKDF、PBKDF2 等),只要种子方法在所有应用程序实例中为相同的输入返回一致的 32 字节数组即可。 ### 临时密钥(默认) 当种子方法返回 `null` 时,将使用 JDK 内置的 Ed25519 提供程序在启动时生成新的密钥对。每次重启时,所有未完成的令牌都会失效。这是最安全的选项,适用于可以接受在部署时强制重新认证的应用程序。 ## 用法 一旦您的 `MyJwtService` 注册完毕,您就可以在应用程序的任何地方注入它 —— 无需泛型: ``` @AllArgsConstructor @Controller public class AuthController { private final MyJwtService jwtService; @PostMapping("/login") public String login(HttpServletRequest request, HttpServletResponse response) { Account account = this.authenticate(request); // your authentication logic this.jwtService.applyTokenCookies(request, response, account); return "redirect:/dashboard"; } @PostMapping("/logout") public String logout(HttpServletRequest request, HttpServletResponse response) { Optional account = this.jwtService.getAccountByRequest(request, response); this.jwtService.removeTokenCookies(response, account.orElse(null)); return "redirect:/login"; } @GetMapping("/dashboard") public String dashboard(HttpServletRequest request, HttpServletResponse response, Model model) { Optional account = this.jwtService.getAccountByRequest(request, response); if (account.isEmpty()) { return "redirect:/login"; } return "dashboard"; } @GetMapping("/admin") public String admin(HttpServletRequest request, HttpServletResponse response) { if (!this.jwtService.isAuthenticatedByRole(request, response, Role.ADMINISTRATOR)) { return "redirect:/login"; } return "admin"; } } ``` ## 安全概览 | 层级 | 保护措施 | |---|---| | **签名算法** | Ed25519 (EdDSA) —— 非对称、确定性、抗侧信道攻击 | | **密钥隔离** | 为访问令牌和刷新令牌使用独立的密钥对 | | **密钥派生** | 从主密钥通过 HKDF 派生 → 确定性的 Ed25519 种子(多实例安全) | | **令牌生命周期** | 通过设置提供程序配置 —— 推荐 5 分钟 Access / 14 天 Refresh | | **令牌绑定** | JWT 中的指纹哈希 + HttpOnly Cookie 中的原始值 | | **XSS 防御** | HttpOnly Cookie —— JavaScript 无法访问令牌值 | | **CSRF 防御** | 生产环境中使用 SameSite=Strict —— 浏览器阻止跨源请求 | | **Cookie 加固** | `__Host-` 前缀强制执行 Secure + Path=/ + no Domain | | **令牌盗窃** | 指纹绑定使被盗的 JWT 如果没有 Cookie 则无法使用 | | **重放防护** | 每次轮换时在服务器端验证刷新令牌 JTI 哈希 | | **重用检测** | 刷新令牌哈希不匹配会触发完整的账户撤销(针对并发请求设有宽限期) | | **并发安全** | 轮换宽限期防止来自并行浏览器请求的误报重用检测 | | **会话失效** | lastTokenIssueAt —— 在安全事件(登录、密码更改、强制登出)时更新,以立即撤销所有令牌 | | **时序攻击** | 所有验证检查均采用恒定时间哈希比较 | | **内存安全** | 密钥种子在派生后立即从内存中擦除 |
标签:Cookie安全, Ed25519, EdDSA, FAPI2.0, Java21, JWT, LangChain, Modbus, Spring, SpringBoot, Streamlit, Token验证, Web安全, YAML, 云配置检测, 会话管理, 侧信道攻击防护, 刷新令牌, 单点登录, 域名枚举, 安全库, 开源库, 搜索引擎爬虫, 蓝队分析, 访问控制, 轻量级, 防XSS, 防重放攻击, 非对称加密