plugins/developer-kit-java/skills/spring-boot-security-jwt/SKILL.md
Provides JWT authentication and authorization patterns for Spring Boot 3.5.x covering token generation with JJWT, Bearer/cookie authentication, database/OAuth2 integration, and RBAC/permission-based access control using Spring Security 6.x. Use when implementing authentication or authorization in Spring Boot applications.
npx skillsauth add giuseppe-trisciuoglio/developer-kit spring-boot-security-jwtInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
JWT authentication and authorization patterns for Spring Boot 3.5.x using Spring Security 6.x and JJWT. Covers token generation, validation, refresh strategies, RBAC/ABAC, and OAuth2 integration.
This skill provides implementation patterns for stateless JWT authentication in Spring Boot applications. It covers the complete authentication flow including token generation with JJWT 0.12.6, Bearer/cookie-based authentication, refresh token rotation, and method-level authorization with @PreAuthorize expressions.
Key capabilities:
@PreAuthorize rulesActivate when user requests involve:
`@PreAuthorize`| Artifact | Scope |
|----------|-------|
| spring-boot-starter-security | compile |
| spring-boot-starter-oauth2-resource-server | compile |
| io.jsonwebtoken:jjwt-api:0.12.6 | compile |
| io.jsonwebtoken:jjwt-impl:0.12.6 | runtime |
| io.jsonwebtoken:jjwt-jackson:0.12.6 | runtime |
| spring-security-test | test |
See references/jwt-quick-reference.md for Maven and Gradle snippets.
| Property | Example Value | Notes |
|----------|--------------|-------|
| jwt.secret | ${JWT_SECRET} | Min 256 bits, never hardcode |
| jwt.access-token-expiration | 900000 | 15 min in milliseconds |
| jwt.refresh-token-expiration | 604800000 | 7 days in milliseconds |
| jwt.issuer | my-app | Validated on every token |
| jwt.cookie-name | jwt-token | For cookie-based auth |
| jwt.cookie-http-only | true | Always true in production |
| jwt.cookie-secure | true | Always true with HTTPS |
| Annotation | Example |
|-----------|---------|
| @PreAuthorize("hasRole('ADMIN')") | Role check |
| @PreAuthorize("hasAuthority('USER_READ')") | Permission check |
| @PreAuthorize("hasPermission(#id, 'Doc', 'READ')") | Domain object check |
| @PreAuthorize("@myService.canAccess(#id)") | Spring bean check |
Include spring-boot-starter-security, spring-boot-starter-oauth2-resource-server, and the three JJWT artifacts in your build file. See references/jwt-quick-reference.md for exact Maven/Gradle snippets.
jwt:
secret: ${JWT_SECRET:change-me-min-32-chars-in-production}
access-token-expiration: 900000
refresh-token-expiration: 604800000
issuer: my-app
cookie-name: jwt-token
cookie-http-only: true
cookie-secure: false # true in production
See references/jwt-complete-configuration.md for the full properties reference.
Core operations: generate access token, generate refresh token, extract username, validate token.
@Service
public class JwtService {
public String generateAccessToken(UserDetails userDetails) {
return Jwts.builder()
.subject(userDetails.getUsername())
.issuer(issuer)
.issuedAt(new Date())
.expiration(new Date(System.currentTimeMillis() + accessTokenExpiration))
.claim("authorities", getAuthorities(userDetails))
.signWith(getSigningKey())
.compact();
}
public boolean isTokenValid(String token, UserDetails userDetails) {
try {
String username = extractUsername(token);
return username.equals(userDetails.getUsername()) && !isTokenExpired(token);
} catch (JwtException e) {
return false;
}
}
}
See references/jwt-complete-configuration.md for the complete JwtService including key management and claim extraction.
Extend OncePerRequestFilter to extract a JWT from the Authorization: Bearer header (or HttpOnly cookie), validate it, and set the SecurityContext.
@Component
public class JwtAuthenticationFilter extends OncePerRequestFilter {
@Override
protected void doFilterInternal(HttpServletRequest request,
HttpServletResponse response, FilterChain chain)
throws ServletException, IOException {
String authHeader = request.getHeader("Authorization");
if (authHeader == null || !authHeader.startsWith("Bearer ")) {
chain.doFilter(request, response);
return;
}
String jwt = authHeader.substring(7);
String username = jwtService.extractUsername(jwt);
if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) {
UserDetails userDetails = userDetailsService.loadUserByUsername(username);
if (jwtService.isTokenValid(jwt, userDetails)) {
UsernamePasswordAuthenticationToken authToken =
new UsernamePasswordAuthenticationToken(
userDetails, null, userDetails.getAuthorities());
authToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
SecurityContextHolder.getContext().setAuthentication(authToken);
}
}
chain.doFilter(request, response);
}
}
See references/configuration.md for the cookie-based variant.
@Configuration
@EnableWebSecurity
@EnableMethodSecurity
public class SecurityConfig {
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
return http
.csrf(AbstractHttpConfigurer::disable)
.sessionManagement(s -> s.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
.authorizeHttpRequests(auth -> auth
.requestMatchers("/api/auth/**", "/swagger-ui/**").permitAll()
.anyRequest().authenticated()
)
.authenticationProvider(authenticationProvider)
.addFilterBefore(jwtAuthFilter, UsernamePasswordAuthenticationFilter.class)
.build();
}
}
See references/jwt-complete-configuration.md for CORS, logout handler, and OAuth2 login integration.
Expose /register, /authenticate, /refresh, and /logout via @RestController. Return accessToken + refreshToken in the response body (and optionally set an HttpOnly cookie).
See references/examples.md for the complete AuthenticationController and AuthenticationService.
Store refresh tokens in the database with user_id, expiry_date, revoked, and expired columns. On /refresh, verify the stored token, revoke it, and issue a new pair (token rotation).
See references/token-management.md for RefreshToken entity, rotation logic, and Redis-based blacklisting.
Use @EnableMethodSecurity and @PreAuthorize annotations for fine-grained control:
@PreAuthorize("hasRole('ADMIN')")
public Page<UserResponse> getAllUsers(Pageable pageable) { ... }
@PreAuthorize("hasPermission(#documentId, 'Document', 'READ')")
public Document getDocument(Long documentId) { ... }
See references/authorization-patterns.md for RBAC entity model, PermissionEvaluator, and ABAC patterns.
@SpringBootTest
@AutoConfigureMockMvc
class AuthControllerTest {
@Test
void shouldDenyAccessWithoutToken() throws Exception {
mockMvc.perform(get("/api/orders"))
.andExpect(status().isUnauthorized());
}
@Test
@WithMockUser(roles = "ADMIN")
void shouldAllowAdminAccess() throws Exception {
mockMvc.perform(get("/api/admin/users"))
.andExpect(status().isOk());
}
}
See references/testing.md and references/jwt-testing-guide.md for full test suites, Testcontainers setup, and a security test checklist.
jti (JWT ID) claim for blacklisting on logoutAuthorization: Bearer header for mobile/API clientsSecure, SameSite=Lax or Strict on cookies in productionSecurityFilterChain bean — never extend WebSecurityConfigurerAdapter@EnableMethodSecurity instead of deprecated @EnableGlobalMethodSecurityiss and aud claims; reject tokens from untrusted issuersUserDetails with @Cacheable to avoid DB lookup on every request@RestController
@RequestMapping("/api/auth")
@RequiredArgsConstructor
public class AuthController {
private final AuthService authService;
@PostMapping("/authenticate")
public ResponseEntity<AuthResponse> authenticate(
@RequestBody LoginRequest request) {
return ResponseEntity.ok(authService.authenticate(request));
}
@PostMapping("/refresh")
public ResponseEntity<AuthResponse> refresh(@RequestBody RefreshRequest request) {
return ResponseEntity.ok(authService.refreshToken(request.refreshToken()));
}
@PostMapping("/logout")
public ResponseEntity<Void> logout() {
authService.logout();
return ResponseEntity.ok().build();
}
}
@RestController
@RequestMapping("/api/admin")
@PreAuthorize("hasRole('ADMIN')")
public class AdminController {
@GetMapping("/users")
public ResponseEntity<List<UserResponse>> getAllUsers() {
return ResponseEntity.ok(adminService.getAllUsers());
}
}
See references/examples.md for complete entity models and service implementations.
| File | Content | |------|---------| | references/jwt-quick-reference.md | Dependencies, minimal service, common patterns | | references/jwt-complete-configuration.md | Full config: properties, SecurityFilterChain, JwtService, OAuth2 RS | | references/configuration.md | JWT config beans, CORS, CSRF, error handling, session options | | references/examples.md | Complete application setup: controllers, services, entities | | references/authorization-patterns.md | RBAC/ABAC entity model, PermissionEvaluator, SpEL expressions | | references/token-management.md | Refresh token entity, rotation, blacklisting with Redis | | references/testing.md | Unit and MockMvc tests, test utilities | | references/jwt-testing-guide.md | Testcontainers, load testing, security test checklist | | references/security-hardening.md | Security headers, HSTS, rate limiting, audit logging | | references/performance-optimization.md | Caffeine cache config, async validation, connection pooling | | references/oauth2-integration.md | Google/GitHub OAuth2 login, OAuth2UserService | | references/microservices-security.md | Inter-service JWT propagation, resource server config | | references/migration-spring-security-6x.md | Migration from Spring Security 5.x | | references/troubleshooting.md | Common errors, debugging tips |
exp, iss, and aud claims before trusting the tokenWebSecurityConfigurerAdapter is removed — use SecurityFilterChain beans only@EnableGlobalMethodSecurity is deprecated — use @EnableMethodSecurityHttpSecurity configuration (no method chaining)WebSecurityConfigurerAdapter.order() replaced by @Order on @Configuration classesjti claim is required for token blacklisting to work correctlyspring-boot-dependency-injection — Constructor injection patterns used throughoutspring-boot-rest-api-standards — REST API security patterns and error handlingunit-test-security-authorization — Testing Spring Security configurationsspring-data-jpa — User entity and repository patternsspring-boot-actuator — Security monitoring and health endpointsdevelopment
Provides final code cleanup after task review approval. Removes debug logs, temporary comments, dead code, optimizes imports, and improves readability. Use when asked to clean up code, polish, finalize, tidy up, remove technical debt, or prepare code for completion after review. Not for refactoring logic or fixing bugs—focused solely on cosmetic and hygiene cleanup.
tools
Ralph Wiggum-inspired automation loop for specification-driven development. Orchestrates task implementation, review, cleanup, and synchronization using a Python script. Use when: user runs /loop command, user asks to automate task implementation, user wants to iterate through spec tasks step-by-step, or user wants to run development workflow automation with context window management. One step per invocation. State machine: init → choose_task → implementation → review → fix → cleanup → sync → update_done. Supports --from-task and --to-task for task range filtering. State persisted in fix_plan.json.
testing
Creates, updates, validates, and displays the architectural DNA of a project through two shared documents: docs/specs/architecture.md (technology stack, architectural rules, security constraints, AI guardrails) and docs/specs/ontology.md (domain glossary / Ubiquitous Language). Use BEFORE brainstorm as a project setup step, or at any point in the SDD lifecycle to validate specs/tasks against architecture principles. Triggers on 'create constitution', 'update constitution', 'constitution check', 'validate against constitution', 'project principles', 'architectural guardrails', 'setup project architecture', 'define ontology'.
tools
Provides Qwen Coder CLI delegation workflows for coding tasks using Qwen2.5-Coder and QwQ models, including English prompt formulation, execution flags, and safe result handling. Use when the user explicitly asks to use Qwen for tasks such as code generation, refactoring, debugging, or architectural analysis. Triggers on "use qwen", "use qwen coder", "delegate to qwen", "ask qwen", "second opinion from qwen", "qwen opinion", "continue with qwen", "qwen session".