Automated summary
Provides implementation patterns for JWT authentication and authorization in Spring Boot 3.5.x applications using Spring Security 6.x and JJWT.
Strengths
- Covers the complete stateless flow: access and refresh tokens, Bearer and HttpOnly cookie strategies, and revocation.
- Details RBAC/ABAC method-level access control with @PreAuthorize and a custom PermissionEvaluator.
- Includes concrete dependency, property, and configuration tables for JJWT 0.12.6 and Spring Security 6.x.
- Explains integration with Spring Data JPA and OAuth2 providers.
Limitations
- Assumes Spring Boot 3.5.x, Spring Security 6.x, and JJWT 0.12.6; does not apply to older stacks.
- Provides patterns and code snippets, but not a complete runnable application or test suite.
- Some security-sensitive defaults (cookie-secure set to false, fallback secret) require manual hardening for production.
Use when adding JWT login, token refresh, or role and permission checks to a Spring Boot REST API or SPA backend.
Avoid for non-Spring stacks or when a managed identity provider already handles the entire authentication flow.
Security analysis
SafeThe skill is purely instructional documentation for Spring Security/JJWT; it declares Bash but only in the context of typical build/test usage, and contains no download-and-execute commands, obfuscated payloads, filesystem destruction, or credential exfiltration. The only notable issues are insecure-by-default configuration samples (fallback JWT secret, non-secure cookie), which are code-quality/security-posture nits rather than agent execution risk.
- •application.yml sample uses a hardcoded fallback secret `${JWT_SECRET:change-me-min-32-chars-in-production}`, which can silently reach production with a known key
- •sample sets `cookie-secure: false` by default (commented 'true in production'), an insecure default if copied verbatim
Examples
Implement JWT authentication in my Spring Boot 3.5 app: add the JJWT dependencies, create an access token and refresh token service, and expose /api/auth/login and /api/auth/refresh endpoints.Configure a Spring Security 6.x SecurityFilterChain for stateless Bearer token authentication and protect admin endpoints with @PreAuthorize("hasRole('ADMIN')") and permission checks.Switch my Spring Boot JWT authentication from Bearer headers to HttpOnly secure cookies for a React SPA, including logout and token revocation.name: spring-boot-security-jwt description: 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. allowed-tools: Read, Write, Edit, Bash, Glob, Grep
Spring Boot JWT Security
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.
Overview
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:
- Access and refresh token generation with configurable expiration
- Bearer token and HttpOnly cookie authentication strategies
- Integration with Spring Data JPA and OAuth2 providers
- RBAC with role/permission-based
@PreAuthorizerules - Token revocation and blacklisting for logout/rotation
When to Use
Activate when user requests involve:
- "Implement JWT authentication", "secure REST API with tokens"
- "Spring Security 6.x configuration", "SecurityFilterChain setup"
- "Role-based access control", "RBAC",
`@PreAuthorize` - "Refresh token", "token rotation", "token revocation"
- "OAuth2 integration", "social login", "Google/GitHub auth"
- "Stateless authentication", "SPA backend security"
- "JWT filter", "OncePerRequestFilter", "Bearer token"
- "Cookie-based JWT", "HttpOnly cookie"
- "Permission-based access control", "custom PermissionEvaluator"
Quick Reference
Dependencies (JJWT 0.12.6)
| 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.
Key Configuration Properties
| 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 |
Authorization Annotations
| 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 |
Instructions
Step 1 — Add Dependencies
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.
Step 2 — Configure application.yml
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.
Step 3 — Implement JwtService
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.
Step 4 — Create JwtAuthenticationFilter
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.
Step 5 — Configure SecurityFilterChain
@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.
Step 6 — Create Authentication Endpoints
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.
Step 7 — Implement Refresh Token Strategy
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.
Step 8 — Add Authorization Rules
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.
Step 9 — Write Security Tests
@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.
Best Practices
Token Security
- Use minimum 256-bit secret keys — load from environment variables, never hardcode
- Set short access token lifetimes (15 min); use refresh tokens for longer sessions
- Implement token rotation: revoke old refresh token when issuing a new one
- Use
jti(JWT ID) claim for blacklisting on logout
Cookie vs Bearer Header
- Prefer HttpOnly cookies for browser clients (XSS-safe)
- Use
Authorization: Bearerheader for mobile/API clients - Set
Secure,SameSite=LaxorStricton cookies in production
Spring Security 6.x
- Use
SecurityFilterChainbean — never extendWebSecurityConfigurerAdapter - Disable CSRF only for stateless APIs; keep it enabled for session-based flows
- Use
@EnableMethodSecurityinstead of deprecated@EnableGlobalMethodSecurity - Validate
issandaudclaims; reject tokens from untrusted issuers
Performance
- Cache
UserDetailswith@Cacheableto avoid DB lookup on every request - Cache signing key derivation (avoid re-computing HMAC key per request)
- Use Redis for refresh token storage at scale
What NOT to Do
- Do not store sensitive data (passwords, PII) in JWT claims — claims are only signed, not encrypted
- Do not issue tokens with infinite lifetime
- Do not accept tokens without validating signature and expiration
- Do not share signing keys across environments
Examples
Basic Authentication Flow
@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();
}
}
JWT Authorization on Controller Method
@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.
References
| 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 |
Constraints and Warnings
Security Constraints
- JWT tokens are signed but not encrypted — do not include sensitive data in claims
- Always validate
exp,iss, andaudclaims before trusting the token - Signing keys must be at least 256 bits; never use weak keys in production
- Load secrets from environment variables or secure vaults, never from config files
- SameSite cookie attribute is essential for CSRF protection in cookie-based flows
Spring Security 6.x Constraints
WebSecurityConfigurerAdapteris removed — useSecurityFilterChainbeans only@EnableGlobalMethodSecurityis deprecated — use@EnableMethodSecurity- Lambda DSL is required for
HttpSecurityconfiguration (no method chaining) WebSecurityConfigurerAdapter.order()replaced by@Orderon@Configurationclasses
Token Constraints
- Access tokens should expire in 5-15 minutes for security
- Refresh tokens should be stored server-side (DB or Redis), never in localStorage
- Implement token blacklisting for immediate revocation on logout
jticlaim is required for token blacklisting to work correctly
Related Skills
spring-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 endpoints
From the file to a first result
The download contains exactly the content displayed on this page. This copy may differ from the latest version in the source repository.
View the source and supporting filesThis download contains SKILL.md only. Any scripts, references, templates and dependencies are not included. Get the required files from the source, preserving their directory structure, before continuing.
1. Download the file
Download SKILL.mdFrom the root of a test project, save the file as .skill-downloads/spring-boot-jwt-security-mryiqhn9/SKILL.md, or use the command below. This folder is for inspecting the skill before installation.
Command for macOS/Linux or WSL: requires a POSIX shell and curl. On Windows without WSL, use the download button and create the folder manually.
mkdir -p '.skill-downloads/spring-boot-jwt-security-mryiqhn9' && curl --fail --show-error --location 'https://skills-guides.com/api/skills/spring-boot-jwt-security-mryiqhn9/download' --output '.skill-downloads/spring-boot-jwt-security-mryiqhn9/SKILL.md.part' && mv '.skill-downloads/spring-boot-jwt-security-mryiqhn9/SKILL.md.part' '.skill-downloads/spring-boot-jwt-security-mryiqhn9/SKILL.md'2. Check prerequisites
Read SKILL.md before using it. If it references scripts or external services, inspect them and prepare the required access in your test environment.
Tools declared by the author (check availability in your agent): Read, Write, Edit, Bash, Glob, Grep
Original excerpt: Quick Reference
Dependencies (JJWT 0.12.6)
| 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.
Key Configuration Properties
| 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 |
Authorization Annotations
| 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 |
The platforms on this page are catalog indications. The paths below follow tool documentation; they do not prove this skill has been run in those tools. No minimum tool version is confirmed for this listing.
3. Load the skill
After inspection, copy the complete folder into your project using one of the commands below, then open a new tool session. The command stops if a folder with the same name already exists: compare the files before replacing it.
Claude Code
test -f '.skill-downloads/spring-boot-jwt-security-mryiqhn9/SKILL.md' && test -s '.skill-downloads/spring-boot-jwt-security-mryiqhn9/SKILL.md' && mkdir -p '.claude/skills' && test ! -e '.claude/skills/spring-boot-security-jwt' && cp -R '.skill-downloads/spring-boot-jwt-security-mryiqhn9' '.claude/skills/spring-boot-security-jwt'OpenSkills
OpenSkills must already be installed. The local install/read procedure is checked with OpenSkills 1.5.0, which requires Node.js ≥ 20.6.0. Catalog slugs are not resolved automatically.
Official OpenSkills installationopenskills install './.skill-downloads/spring-boot-jwt-security-mryiqhn9'openskills read 'spring-boot-jwt-security-mryiqhn9'Reading should display the installed file content and its base directory. It does not run the skill or install its dependencies.
For an agent that reads AGENTS.md, then sync the index. This command changes AGENTS.md: review the diff before using it.
openskills syncManual method or another tool: attach SKILL.md to a conversation, or open it in an agent that can read files. Include any required supporting resources. No universal folder is recognized by every tool.
4. Try a first task
Use a short, synthetic input: an API endpoint, a small document or an interface brief. In a new conversation, attach the file or give the agent its path, then adapt this request:
Read .skill-downloads/spring-boot-jwt-security-mryiqhn9/SKILL.md and apply its instructions to this example: [describe a test input]. Identify any missing files, dependencies or access. Produce the result requested by the skill without inventing data or running unreviewed scripts.Expected result: output matching the skill instructions, or a precise list of missing prerequisites. A read confirmation alone does not validate a first use. If an external resource is unavailable, that path remains untested.
DeFi AMM Security
Security
Security checklist for Solidity AMM contracts, liquidity pools, and swap flows. Covers reentrancy, donation attacks, oracle manipulation, slippage, and integer math.
Semgrep Rule Creator
Security
Creates custom Semgrep rules for detecting security vulnerabilities, bug patterns, and code patterns. Includes test-first methodology, taint mode support, and validation steps. Helps when building targeted static analysis detections.
Dev Toolbar Review Guide
Security
Safety rules for the dev toolbar, PostHog client, and feature flags. Use when reviewing changes to dev-tools and related files, covering environment guards, override cookies, and SSE stream safety.