agentsclimarketplace

Java security

Skill Mattakushi432/Claude-Code-Skills-Custom-DevTools-Pack/plugins/devtools-pack/skills/java-security

When to activate: Java security, Spring Security, JWT, OAuth2, SecurityFilterChain, method security, CSRF, CORS, password encodingFrom its SKILL.md

Install
npx -y skills add Mattakushi432/Claude-Code-Skills-Custom-DevTools-Pack --skill java-security

Assembled 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.

SKILL.md

5.5 KB, ~1.1k tokens by cl100k_base, as published. Nobody here has run it

Java Security Patterns

Spring Security 6 Configuration

@Configuration
@EnableWebSecurity
@EnableMethodSecurity
public class SecurityConfig {

    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        return http
            .csrf(AbstractHttpConfigurer::disable)          // stateless API
            .sessionManagement(s -> s.sessionCreationPolicy(STATELESS))
            .authorizeHttpRequests(auth -> auth
                .requestMatchers("/api/auth/**", "/actuator/health").permitAll()
                .requestMatchers("/api/admin/**").hasRole("ADMIN")
                .anyRequest().authenticated()
            )
            .oauth2ResourceServer(oauth2 -> oauth2.jwt(Customizer.withDefaults()))
            .exceptionHandling(ex -> ex
                .authenticationEntryPoint(new BearerTokenAuthenticationEntryPoint())
                .accessDeniedHandler(new BearerTokenAccessDeniedHandler())
            )
            .build();
    }

    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder(12);
    }
}

JWT with Nimbus (Spring Security)

@Configuration
public class JwtConfig {

    @Value("${app.jwt.secret}")
    private String jwtSecret;

    @Bean
    public JwtDecoder jwtDecoder() {
        return NimbusJwtDecoder.withSecretKey(secretKey()).build();
    }

    @Bean
    public JwtEncoder jwtEncoder() {
        return new NimbusJwtEncoder(new ImmutableSecret<>(secretKey()));
    }

    private SecretKey secretKey() {
        return new SecretKeySpec(jwtSecret.getBytes(StandardCharsets.UTF_8), "HmacSHA256");
    }
}

@Service
@RequiredArgsConstructor
public class TokenService {
    private final JwtEncoder encoder;

    public String generateToken(UserDetails user) {
        Instant now = Instant.now();
        JwtClaimsSet claims = JwtClaimsSet.builder()
            .issuer("my-service")
            .issuedAt(now)
            .expiresAt(now.plus(1, ChronoUnit.HOURS))
            .subject(user.getUsername())
            .claim("roles", user.getAuthorities().stream()
                .map(GrantedAuthority::getAuthority).toList())
            .build();
        return encoder.encode(JwtEncoderParameters.from(claims)).getTokenValue();
    }
}

Method-Level Security

@Service
public class DocumentService {

    @PreAuthorize("hasRole('ADMIN') or #userId == authentication.name")
    public Document getDocument(Long id, String userId) { ... }

    @PostAuthorize("returnObject.ownerId == authentication.name")
    public Document findById(Long id) { ... }

    @PreAuthorize("hasAuthority('DOCUMENT_WRITE')")
    public Document create(CreateDocumentRequest request) { ... }

    @Secured("ROLE_ADMIN")
    public void deleteDocument(Long id) { ... }
}

CORS Configuration

@Bean
public CorsConfigurationSource corsConfigurationSource() {
    CorsConfiguration config = new CorsConfiguration();
    config.setAllowedOrigins(List.of("https://app.example.com"));
    config.setAllowedMethods(List.of("GET", "POST", "PUT", "DELETE", "OPTIONS"));
    config.setAllowedHeaders(List.of("Authorization", "Content-Type"));
    config.setAllowCredentials(true);
    config.setMaxAge(3600L);

    UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
    source.registerCorsConfiguration("/api/**", config);
    return source;
}

Input Validation & SQL Injection Prevention

// Always use parameterized queries
@Query("SELECT u FROM User u WHERE u.email = :email")
Optional<User> findByEmail(@Param("email") String email);

// Never concatenate user input
// BAD:  "SELECT * FROM users WHERE name = '" + name + "'"
// GOOD: jdbcTemplate.queryForObject("SELECT * FROM users WHERE name = ?", User.class, name)

// Validate at boundary
@PostMapping("/users")
public UserResponse create(@Valid @RequestBody CreateUserRequest request) { ... }

public record CreateUserRequest(
    @NotBlank @Size(max = 100) String name,
    @NotBlank @Email String email,
    @Pattern(regexp = "^(?=.*[A-Z])(?=.*\\d).{8,}$") String password
) {}

Secrets Management

// Never hardcode secrets — read from environment
@Value("${app.jwt.secret:#{null}}")
private String jwtSecret;

@PostConstruct
public void validateSecrets() {
    if (jwtSecret == null || jwtSecret.length() < 32) {
        throw new IllegalStateException("JWT secret must be at least 32 characters");
    }
}

// Use Spring Cloud Vault or AWS Secrets Manager in production
@Configuration
@EnableVaultConfiguration
public class VaultConfig extends AbstractVaultConfiguration {
    @Override
    public VaultEndpoint vaultEndpoint() {
        return VaultEndpoint.from(URI.create(System.getenv("VAULT_ADDR")));
    }
}

Key Rules

  • BCryptPasswordEncoder(12) — strength 12 is the minimum for production; never store plain or MD5 passwords
  • Disable CSRF only for stateless REST APIs; always enable for session-based apps
  • @PreAuthorize with SpEL is preferred over role checks in business logic
  • Never log passwords, tokens, or PII — use structured logging with explicit field exclusions
  • Validate all user input at the controller boundary with @Valid — never trust raw request data

What ships with it

Read from the repository

Just SKILL.md. No reference files, no scripts.

Keep looking

Skills are one crate of 325,949. Ordering is by how many stacks a row turns up in, so the top of any crate is what has actually been picked rather than what has the most stars.