Java Spring Security Cheat Sheet
Covers Spring Security filter chain configuration, password encoding, method-level authorization, and JWT resource server setup for securing APIs.
Security Filter Chain Configuration
Define HTTP security rules using the Spring Security 6 lambda DSL.
@Configuration@EnableWebSecuritypublic class SecurityConfig { @Bean public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { http .authorizeHttpRequests(auth -> auth .requestMatchers("/public/**").permitAll() .requestMatchers("/admin/**").hasRole("ADMIN") .anyRequest().authenticated()) .formLogin(Customizer.withDefaults()) .csrf(csrf -> csrf.ignoringRequestMatchers("/api/**")) .sessionManagement(sm -> sm .sessionCreationPolicy(SessionCreationPolicy.STATELESS)); return http.build(); }}
Password Encoding & UserDetailsService
Authenticate users against a custom source with a secure hash.
@Beanpublic PasswordEncoder passwordEncoder() { return new BCryptPasswordEncoder(); // never store plaintext passwords}@Beanpublic UserDetailsService userDetailsService(PasswordEncoder encoder) { UserDetails user = User.withUsername("alice") .password(encoder.encode("s3cret")) .roles("USER") .build(); return new InMemoryUserDetailsManager(user);}
Method-Level Security
Secure service methods directly with annotations.
@Configuration@EnableMethodSecurity // enables @PreAuthorize/@PostAuthorize/@Securedpublic class MethodSecurityConfig {}@Servicepublic class AccountService { @PreAuthorize("hasRole('ADMIN')") public void deleteAccount(Long id) { /* ... */ } @PreAuthorize("#username == authentication.name") public Account getOwnAccount(String username) { /* ... */ } @PostAuthorize("returnObject.owner == authentication.name") public Account getAccount(Long id) { /* ... */ }}
JWT Resource Server
Validate bearer JWTs for stateless API authentication.
@Beanpublic SecurityFilterChain apiFilterChain(HttpSecurity http) throws Exception { http .authorizeHttpRequests(auth -> auth .requestMatchers("/api/**").authenticated()) .oauth2ResourceServer(oauth2 -> oauth2.jwt(Customizer.withDefaults())); return http.build();}// application.yml// spring:// security:// oauth2:// resourceserver:// jwt:// issuer-uri: https://auth.example.com/
Core Concepts
Building blocks of the Spring Security architecture.
- SecurityFilterChain- Ordered chain of servlet filters that intercept requests to enforce authentication and authorization.
- AuthenticationManager- Central interface that processes an Authentication request and returns a fully authenticated token.
- UserDetailsService- Loads user-specific data (username, password, authorities) for authentication.
- SecurityContextHolder- Thread-bound holder for the current Authentication, accessible anywhere via SecurityContextHolder.getContext().
- GrantedAuthority- Represents a permission granted to the principal, e.g. ROLE_ADMIN or SCOPE_read.
- CSRF Protection- Enabled by default for browser sessions; typically disabled for stateless token-based APIs.
Custom AuthenticationProvider
Plug in fully custom credential verification logic, such as checking an external identity service.
@Componentpublic class ApiKeyAuthenticationProvider implements AuthenticationProvider { private final ApiKeyRepository repository; ApiKeyAuthenticationProvider(ApiKeyRepository repository) { this.repository = repository; } @Override public Authentication authenticate(Authentication authentication) throws AuthenticationException { String apiKey = (String) authentication.getCredentials(); var record = repository.findByKey(apiKey) .orElseThrow(() -> new BadCredentialsException("Invalid API key")); return new UsernamePasswordAuthenticationToken( record.owner(), apiKey, List.of(new SimpleGrantedAuthority("ROLE_SERVICE"))); } @Override public boolean supports(Class<?> authentication) { return UsernamePasswordAuthenticationToken.class.isAssignableFrom(authentication); }}
CORS Configuration
Wire an explicit CorsConfigurationSource into the filter chain instead of relying on permissive defaults.
@Beanpublic SecurityFilterChain filterChain(HttpSecurity http) throws Exception { http.cors(cors -> cors.configurationSource(corsConfigurationSource())); return http.build();}@Beanpublic CorsConfigurationSource corsConfigurationSource() { CorsConfiguration config = new CorsConfiguration(); config.setAllowedOrigins(List.of("https://app.example.com")); config.setAllowedMethods(List.of("GET", "POST", "PUT", "DELETE")); config.setAllowedHeaders(List.of("Authorization", "Content-Type")); config.setAllowCredentials(true); UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); source.registerCorsConfiguration("/api/**", config); return source;}
Testing Secured Endpoints
Simulate authenticated requests in slice tests without standing up a real authentication flow.
@WebMvcTest(AccountController.class)class AccountControllerTest { @Autowired MockMvc mockMvc; @Test @WithMockUser(username = "alice", roles = "ADMIN") void deleteAccount_asAdmin_succeeds() throws Exception { mockMvc.perform(delete("/admin/accounts/42")) .andExpect(status().isNoContent()); } @Test void deleteAccount_unauthenticated_isRejected() throws Exception { mockMvc.perform(delete("/admin/accounts/42")) .andExpect(status().isUnauthorized()); }}
Common Web Vulnerabilities & Built-in Mitigations
Attack classes Spring Security addresses out of the box, and the mechanism responsible.
- Session Fixation- Mitigated by default via changeSessionId() session strategy, which rotates the session ID on authentication.
- Clickjacking- Default X-Frame-Options: DENY header prevents the app from being embedded in a hostile iframe.
- CSRF- Enabled by default for stateful browser sessions via a synchronizer token embedded in forms; typically disabled only for stateless token APIs.
- Credential stuffing / brute force- Not handled automatically; pair with an account-lockout strategy or rate limiter, e.g. via a custom AuthenticationFailureHandler.
- Open redirect after login- The default SavedRequestAwareAuthenticationSuccessHandler validates the saved request originated from the same application.
- Sensitive data in HTTP responses- HttpSecurity#headers() configures HSTS, content-type sniffing protection, and cache-control headers to reduce data leakage.
Never disable CSRF protection globally just to make a form-based endpoint work - scope csrf.ignoringRequestMatchers() to stateless API paths only, since browser-session endpoints with cookies remain vulnerable to CSRF.