mcpField
S
spring-security-jwt

spring-security-jwt

github.com/rrezartprebreza安全328125 次安装6 天前更新
代码仓库

关于 spring-security-jwt

面向 Spring 开发者的 JWT 安全技能,提供实现模板与无状态 Bearer 认证、账户状态检查和令牌处理指南,帮助规避常见配置问题。

主要功能

  1. 01JWT 服务与认证过滤器模板
  2. 02无状态 Bearer 认证配置
  3. 03角色与方法级授权
  4. 04JSON 格式的 401 和 403 错误处理
  5. 05访问令牌与刷新令牌指南

适用场景

  1. 01为 Spring API 添加 JWT Bearer 认证
  2. 02在令牌认证时检查账户状态
  3. 03为端点和方法配置基于角色的访问控制

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/rrezartprebreza/spring-boot-skills --skill spring-security-jwt

安装后在 Claude Code 中输入/spring-security-jwt确认已加载。

包含的文件

除 SKILL.md 外,安装时一起下载的参考资料和脚本。

agents/
openai.yaml
examples/
bad-security-config.java
good-security-config.java
templates/
JwtAuthenticationFilter.java
JwtService.java
SecurityConfig.java

详细介绍

Spring Security — JWT

依赖项

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
    <groupId>io.jsonwebtoken</groupId>
    <artifactId>jjwt-api</artifactId>
    <version>0.12.6</version>
</dependency>
<dependency>
    <groupId>io.jsonwebtoken</groupId>
    <artifactId>jjwt-impl</artifactId>
    <version>0.12.6</version>
    <scope>runtime</scope>
</dependency>
<dependency>
    <groupId>io.jsonwebtoken</groupId>
    <artifactId>jjwt-jackson</artifactId>
    <version>0.12.6</version>
    <scope>runtime</scope>
</dependency>

安全配置

使用编译后的 SecurityConfig,并配合下面的过滤器和服务。 它使用 lambda DSL、无状态 Bearer 身份验证、角色规则、JSON 401/403 处理器,并禁用安全链过滤器的 servlet 注册。Bean 方法注入可避免配置类与其自身的 AuthenticationProvider Bean 之间出现构造函数循环依赖。 请根据项目调整路由和角色。禁用 CSRF 仅适用于只通过请求头传递 Bearer 令牌的 API;如果浏览器会自动发送身份验证 Cookie,则应保留 CSRF 防护。

JWT 实现

配合使用经过测试的 JwtService 和 JwtAuthenticationFilter 模板。 配置项为 app.jwt.secret、app.jwt.access-token-expiration 和 app.jwt.refresh-token-expiration(时长单位为毫秒)。

过滤器会拒绝已过期、格式错误、遭篡改、缺少过期时间以及刷新令牌,并返回 401 和 Bearer 挑战。在身份验证前,它会检查当前用户是否已启用、已锁定、账户是否过期以及凭据是否过期。已删除的用户也会收到 401。 数据库中断和下游应用故障必须作为服务器故障处理,不能将其伪装成无效凭据。即使在公开端点上,也会拒绝提供的无效令牌。

过滤器示例中的错误响应正文使用 Problem Details。对于已有的旧版 API,请根据既定错误契约调整此响应和下面的入口点。切勿记录 Bearer 令牌。 只在安全链中注册过滤器 Bean:使用 FilterRegistrationBean<JwtAuthenticationFilter>,并将其 enabled 标志设为 false,以禁用 servlet 容器注册。

这些模板展示的是单服务、第一方令牌契约。在跨服务共享签名密钥或接受其他服务的令牌之前,请定义并验证签发者和受众、密钥轮换以及吊销机制。Spring Security 的资源服务器支持也可以验证自定义 JWT;如果它已适用于应用,请保留该支持。令牌生成并不构成完整的刷新流程:仍需满足下文的轮换和重用检测要求。

JSON 401/403

配置和过滤器模板对身份验证及授权错误使用 Problem Details。对于旧版错误契约,请同时调整两者。访问受保护路由时缺少凭据将返回 401;已通过身份验证但不具备所需角色的调用者将返回 403。即使路由允许匿名访问,提供的无效令牌仍会返回 401。 Controller advice 无法处理在 dispatcher servlet 之前抛出的异常。

Auth Controller

@RestController
@RequestMapping("/api/v1/auth")
@RequiredArgsConstructor
public class AuthController {

    private final AuthService authService;

    @PostMapping("/login")
    public ApiResponse<AuthResponse> login(@Valid @RequestBody LoginRequest request) {
        return ApiResponse.ok(authService.login(request));
    }

    @PostMapping("/refresh")
    public ApiResponse<AuthResponse> refresh(@Valid @RequestBody RefreshRequest request) {
        return ApiResponse.ok(authService.refresh(request.refreshToken()));
    }

    @PostMapping("/register")
    public ResponseEntity<ApiResponse<AuthResponse>> register(@Valid @RequestBody RegisterRequest request) {
        return ResponseEntity.status(201).body(ApiResponse.ok(authService.register(request)));
    }
}

public record AuthResponse(String accessToken, String refreshToken, long expiresIn) {}

方法级安全

// On service methods
@PreAuthorize("hasRole('ADMIN')")
public void deleteUser(UUID userId) { ... }

@PreAuthorize("hasRole('ADMIN') or #userId == authentication.principal.id")
public UserProfile getProfile(UUID userId) { ... }

@PostAuthorize("returnObject.email == authentication.name")
public User findById(UUID id) { ... }

application.yml

app:
  jwt:
    secret: ${JWT_SECRET} # min 256-bit base64 encoded key
    access-token-expiration: 900000   # 15 minutes
    refresh-token-expiration: 604800000 # 7 days

示例会创建刷新令牌,但没有实现刷新端点。生产级刷新流程必须只接受 type=refresh 的令牌,每次使用时都轮换刷新令牌,并吊销之前的令牌(例如,在数据库或 Redis 中使用经过哈希处理的令牌族记录)。

易踩问题

  • Agent 在用户查询周围笼统地捕获 AuthenticationException——应保留 AuthenticationServiceException,将其视为服务器故障。
  • Agent 会让持有有效 JWT 的禁用或锁定用户登录——除了校验声明,还要检查当前账户状态。
  • Agent 使用旧版 API HttpSecurity.csrf().disable()——应使用 AbstractHttpConfigurer::disable
  • Agent 让 ExpiredJwtException 从过滤器中逸出——过期令牌会导致返回 500,而不是 401;应在过滤器中捕获
  • Agent 跳过 exceptionHandling()——客户端会收到空的 401/403 响应正文(或被重定向到登录页);@RestControllerAdvice 无法捕获过滤器级别的异常
  • Agent 将 JWT 密钥存储在代码中——始终从环境变量读取 ${JWT_SECRET}(HS256 需要 ≥256 位的密钥,否则 Keys.hmacShaKeyFor 会抛出 WeakKeyException)
  • Agent 使用 SessionCreationPolicy.IF_REQUIRED——JWT 必须使用 STATELESS
  • Agent 只校验签名和过期时间——Bearer 过滤器必须只接受 type=access 令牌;刷新令牌应由单独的刷新端点处理
  • Agent 忘记启用 @EnableMethodSecurity,导致 @PreAuthorize 无法生效
  • Agent 使用强度 < 10 的 BCrypt——生产环境应使用 12
  • Agent 将令牌校验逻辑放在控制器中——这部分逻辑应放在过滤器中
  • Agent 在示例中将刷新令牌放入 localStorage——应推荐 httpOnly Cookie 或安全存储;刷新令牌是长期有效的凭据

评论

支持 Markdown