Spring security
Skill Mattakushi432/Claude-Code-Skills-Custom-DevTools-Pack/plugins/devtools-pack/skills/spring-security
A curated pack of custom Claude Code skills for developers — installable as a Claude Code plugin marketplace.
npx -y skills add Mattakushi432/Claude-Code-Skills-Custom-DevTools-Pack --skill spring-securityAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
One thing to look at
- 0 stars0 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.
What its author says it does
Copied from the file, not written here
When to activate: Spring Security, authentication, authorization, OAuth2 resource server, custom filters, UserDetailsService, security context
SKILL.md
5.8 KB, ~1.0k tokens by cl100k_base, as published. Nobody here has run it
Spring Security Patterns
Custom UserDetailsService
@Service
@RequiredArgsConstructor
public class AppUserDetailsService implements UserDetailsService {
private final UserRepository userRepository;
@Override
public UserDetails loadUserByUsername(String email) throws UsernameNotFoundException {
return userRepository.findByEmail(email)
.map(this::toUserDetails)
.orElseThrow(() -> new UsernameNotFoundException("User not found: " + email));
}
private UserDetails toUserDetails(User user) {
List<GrantedAuthority> authorities = user.getRoles().stream()
.map(role -> new SimpleGrantedAuthority("ROLE_" + role.name()))
.collect(Collectors.toList());
return new org.springframework.security.core.userdetails.User(
user.getEmail(), user.getPasswordHash(), user.isActive(), true, true, true, authorities);
}
}
OAuth2 Resource Server (JWT)
@Configuration
@EnableWebSecurity
public class ResourceServerConfig {
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
return http
.oauth2ResourceServer(oauth2 -> oauth2
.jwt(jwt -> jwt.jwtAuthenticationConverter(jwtAuthConverter()))
)
.authorizeHttpRequests(auth -> auth
.requestMatchers("/public/**").permitAll()
.requestMatchers("/api/admin/**").hasAuthority("SCOPE_admin")
.anyRequest().hasAuthority("SCOPE_api")
)
.build();
}
@Bean
public JwtAuthenticationConverter jwtAuthConverter() {
var grantedAuthoritiesConverter = new JwtGrantedAuthoritiesConverter();
grantedAuthoritiesConverter.setAuthoritiesClaimName("roles");
grantedAuthoritiesConverter.setAuthorityPrefix("ROLE_");
var converter = new JwtAuthenticationConverter();
converter.setJwtGrantedAuthoritiesConverter(grantedAuthoritiesConverter);
return converter;
}
}
Custom Authentication Filter
public class ApiKeyAuthFilter extends OncePerRequestFilter {
private static final String API_KEY_HEADER = "X-API-Key";
private final ApiKeyService apiKeyService;
@Override
protected void doFilterInternal(HttpServletRequest request,
HttpServletResponse response,
FilterChain chain) throws ServletException, IOException {
String apiKey = request.getHeader(API_KEY_HEADER);
if (apiKey != null) {
apiKeyService.validateKey(apiKey).ifPresent(principal -> {
var auth = new UsernamePasswordAuthenticationToken(
principal, null, principal.getAuthorities());
auth.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
SecurityContextHolder.getContext().setAuthentication(auth);
});
}
chain.doFilter(request, response);
}
@Override
protected boolean shouldNotFilter(HttpServletRequest request) {
return new AntPathMatcher().match("/public/**", request.getServletPath());
}
}
// Register in SecurityFilterChain
http.addFilterBefore(apiKeyAuthFilter(), UsernamePasswordAuthenticationFilter.class);
Security Context Access
// In any Spring bean
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
String username = auth.getName();
boolean isAdmin = auth.getAuthorities().stream()
.anyMatch(a -> a.getAuthority().equals("ROLE_ADMIN"));
// In controller via @AuthenticationPrincipal
@GetMapping("/me")
public UserResponse getCurrentUser(@AuthenticationPrincipal JwtAuthenticationToken token) {
String userId = token.getToken().getSubject();
return userService.findBySubject(userId);
}
// With custom principal
@GetMapping("/profile")
public Profile getProfile(@AuthenticationPrincipal AppUserPrincipal principal) {
return profileService.findByUserId(principal.getUserId());
}
Request-Level Authorization with SpEL
.authorizeHttpRequests(auth -> auth
// Only users accessing their own data
.requestMatchers("/api/users/{id}/**").access(
new WebExpressionAuthorizationManager("#id == authentication.name or hasRole('ADMIN')")
)
// IP allowlist
.requestMatchers("/actuator/**").access(
new WebExpressionAuthorizationManager("hasIpAddress('10.0.0.0/8')")
)
)
Security Event Listening
@Component
public class AuthenticationEventListener {
@EventListener
public void onSuccess(AuthenticationSuccessEvent event) {
log.info("Login success: {}", event.getAuthentication().getName());
}
@EventListener
public void onFailure(AbstractAuthenticationFailureEvent event) {
log.warn("Login failure for {}: {}", event.getAuthentication().getName(),
event.getException().getMessage());
auditService.recordFailedLogin(event.getAuthentication().getName());
}
}
Key Rules
- Clear
SecurityContextHolderafter async processing — it'sThreadLocaland leaks across thread boundaries - Never expose internal user IDs in JWT subjects; use opaque UUIDs or email
- Use
@AuthenticationPrincipalin controllers instead of callingSecurityContextHolderdirectly - Rate-limit authentication endpoints independently — Spring Security has no built-in rate limiting
hasAuthority('ROLE_X')andhasRole('X')are equivalent —hasRoleauto-prefixesROLE_; be consistent