Skill262 estrellas del repoactualizado 5d ago
spring-security-jwt
This Claude Code skill implements JWT (JSON Web Token) authentication with Spring Security for Spring Boot applications. It provides configuration for stateless token-based security with role-based access control, includes dependencies for JWT generation and validation using JJWT library, and implements a JWT service for token management. Use this when building REST APIs requiring stateless authentication without server sessions, particularly for microservices or applications needing scalable token-based user verification.
Instalar en Claude Code
Copiargit clone --depth 1 https://github.com/rrezartprebreza/spring-boot-skills /tmp/spring-security-jwt && cp -r /tmp/spring-security-jwt/skills/spring-boot-4/spring-security-jwt ~/.claude/skills/spring-security-jwtDespués abre una sesión nueva de Claude Code; el skill carga automáticamente.
Definición
SKILL.md
# Spring Security — JWT
Spring Boot 4.x ships **Spring Security 7**: the lambda DSL is the *only* style — `and()`,
`authorizeRequests()`, `antMatchers()`, and `WebSecurityConfigurerAdapter` no longer exist, and
`AntPathRequestMatcher`/`MvcRequestMatcher` are replaced by `PathPatternRequestMatcher`
(`requestMatchers("/path/**")` uses it under the hood).
## Dependencies
```xml
<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>
```
## Security Configuration
```java
@Configuration
@EnableWebSecurity
@EnableMethodSecurity
@RequiredArgsConstructor
public class SecurityConfig {
private final JwtAuthenticationFilter jwtAuthFilter;
private final UserDetailsService userDetailsService;
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
return http
.csrf(AbstractHttpConfigurer::disable)
.sessionManagement(s -> s.sessionCreationPolicy(STATELESS))
.authorizeHttpRequests(auth -> auth
.requestMatchers("/api/v1/auth/**").permitAll()
.requestMatchers("/actuator/health").permitAll()
.requestMatchers("/api/v1/admin/**").hasRole("ADMIN")
.anyRequest().authenticated()
)
.addFilterBefore(jwtAuthFilter, UsernamePasswordAuthenticationFilter.class)
.build();
}
@Bean
public AuthenticationProvider authenticationProvider() {
var provider = new DaoAuthenticationProvider(userDetailsService); // Security 7: constructor, not setter
provider.setPasswordEncoder(passwordEncoder());
return provider;
}
@Bean
public AuthenticationManager authenticationManager(AuthenticationConfiguration config) throws Exception {
return config.getAuthenticationManager();
}
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder(12);
}
}
```
## JWT Service
```java
@Service
public class JwtService {
@Value("${app.jwt.secret}")
private String secretKey;
@Value("${app.jwt.access-token-expiry:900000}") // 15 min default
private long accessTokenExpiry;
@Value("${app.jwt.refresh-token-expiry:604800000}") // 7 days default
private long refreshTokenExpiry;
public String generateAccessToken(UserDetails user) {
return generateToken(Map.of("type", "access"), user, accessTokenExpiry);
}
public String generateRefreshToken(UserDetails user) {
return generateToken(Map.of("type", "refresh"), user, refreshTokenExpiry);
}
private String generateToken(Map<String, Object> claims, UserDetails user, long expiry) {
return Jwts.builder()
.claims(claims)
.subject(user.getUsername())
.issuedAt(new Date())
.expiration(new Date(System.currentTimeMillis() + expiry))
.signWith(getSigningKey())
.compact();
}
public boolean isAccessTokenValid(String token, UserDetails user) {
Claims claims = extractClaims(token);
return "access".equals(claims.get("type", String.class))
&& user.getUsername().equals(claims.getSubject())
&& !claims.getExpiration().before(new Date());
}
public String extractUsername(String token) {
return extractClaims(token).getSubject();
}
private Claims extractClaims(String token) {
return Jwts.parser().verifyWith(getSigningKey()).build()
.parseSignedClaims(token).getPayload();
}
private SecretKey getSigningKey() {
return Keys.hmacShaKeyFor(Decoders.BASE64.decode(secretKey));
}
}
```
## JWT Filter
```java
@Component
@RequiredArgsConstructor
public class JwtAuthenticationFilter extends OncePerRequestFilter {
private final JwtService jwtService;
private final UserDetailsService userDetailsService;
@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 token = authHeader.substring(7);
try {
String username = jwtService.extractUsername(token);
if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) {
UserDetails user = userDetailsService.loadUserByUsername(username);
if (jwtService.isAccessTokenValid(token, user)) {
var auth = new UsernamePasswordAuthenticationToken(user, null, user.getAuthorities());
auth.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
SecurityContextHolder.getContext().setAuthentication(auth);
}
}
} catch (ExpiredJwtException | MalformedJwtException | SignatureException e) {
// Parsing throws on expired/tampered tokens. Without this catch the exception
// escapes the filter chain as a 500. Leave the context empty — the entry
// point below turns it into a clean 401.
SecurityContextHolder.clearContext();
}
chain.doFilter(request, response);
}
}
```
## JSON