Implementing Spring Security
1. Adding Spring Security Dependencies
Example: Spring Security dependencies
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-test</artifactId>
<scope>test</scope>
</dependency>
Note: Out of the box: HTTP Basic, generated user/password, CSRF on, all
endpoints secured.
2. Configuring Security Filter Chain
Example: Stateless JWT security filter chain
@Configuration
@EnableWebSecurity
public class SecurityConfig {
@Bean
SecurityFilterChain api(HttpSecurity http) throws Exception {
return http
.csrf(csrf -> csrf.disable())
.cors(Customizer.withDefaults())
.sessionManagement(s -> s.sessionCreationPolicy(STATELESS))
.authorizeHttpRequests(a -> a
.requestMatchers("/public/**", "/actuator/health").permitAll()
.requestMatchers("/admin/**").hasRole("ADMIN")
.anyRequest().authenticated())
.oauth2ResourceServer(o -> o.jwt(Customizer.withDefaults()))
.build();
}
}
3. Using In-Memory Authentication
Example: In-memory users with encoded passwords
@Bean
UserDetailsService users(PasswordEncoder enc) {
return new InMemoryUserDetailsManager(
User.withUsername("admin").password(enc.encode("secret")).roles("ADMIN").build(),
User.withUsername("user").password(enc.encode("pwd")).roles("USER").build()
);
}
4. Using JDBC Authentication
Example: JDBC-backed user details
@Bean
UserDetailsService jdbcUsers(DataSource ds) {
return new JdbcUserDetailsManager(ds);
}
| Default Tables | Schema |
|---|---|
users |
username, password, enabled |
authorities |
username, authority |
5. Implementing UserDetailsService Interface
Example: Load user from database
@Service
public class DbUserDetailsService implements UserDetailsService {
private final UserRepository repo;
public DbUserDetailsService(UserRepository repo) { this.repo = repo; }
@Override public UserDetails loadUserByUsername(String username) {
return repo.findByEmail(username)
.map(u -> User.withUsername(u.getEmail())
.password(u.getPasswordHash())
.roles(u.getRoles().toArray(String[]::new))
.build())
.orElseThrow(() -> new UsernameNotFoundException(username));
}
}
6. Configuring Password Encoders
| Encoder | Recommendation |
|---|---|
BCryptPasswordEncoder |
Default; cost 10–12 |
Argon2PasswordEncoder |
Modern, memory-hard (preferred new apps) |
Pbkdf2PasswordEncoder |
FIPS-compliant |
SCryptPasswordEncoder |
Memory-hard alt |
DelegatingPasswordEncoder |
Default — supports {bcrypt}, {argon2} prefixes |
Example: Delegating password encoder
@Bean PasswordEncoder encoder() {
return PasswordEncoderFactories.createDelegatingPasswordEncoder();
}
7. Securing Endpoints with HTTP Security
| Matcher | Use |
|---|---|
requestMatchers("/api/**") |
Ant patterns |
permitAll() |
Open |
denyAll() |
Block |
authenticated() |
Any auth user |
hasRole("X") / hasAuthority("SCOPE_x") |
Role / authority check |
access(expr) |
SpEL or AuthorizationManager |
8. Using Method-Level Security
Example: Method security: role and ownership check
@Configuration
@EnableMethodSecurity
public class MethodSecConfig {}
@Service
public class OrderService {
@PreAuthorize("hasRole('ADMIN') or #ownerId == authentication.name")
public Order get(String ownerId, Long id) { /* … */ }
@PostAuthorize("returnObject.owner == authentication.name")
public Document load(Long id) { /* … */ }
}
9. Configuring CORS
Example: CORS configuration source bean
@Bean
CorsConfigurationSource corsSource() {
CorsConfiguration cfg = new CorsConfiguration();
cfg.setAllowedOrigins(List.of("https://app.example.com"));
cfg.setAllowedMethods(List.of("GET","POST","PUT","DELETE"));
cfg.setAllowedHeaders(List.of("*"));
cfg.setAllowCredentials(true);
UrlBasedCorsConfigurationSource src = new UrlBasedCorsConfigurationSource();
src.registerCorsConfiguration("/**", cfg);
return src;
}
10. Implementing CSRF Protection
| Mode | When |
|---|---|
| Default (cookie/header) | Browser-based form / SPA with cookie auth |
| Disabled | Stateless API with bearer token |
CookieCsrfTokenRepository.withHttpOnlyFalse() |
SPA reading XSRF-TOKEN cookie |
11. Configuring Session Management
| Policy | Behavior |
|---|---|
ALWAYS |
Always create session |
IF_REQUIRED (default) |
Create when needed |
NEVER |
Use existing only |
STATELESS |
JWT/API style — no session |
12. Using Security Context (SecurityContextHolder)
Example: Read username and roles from context
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
String username = auth.getName();
boolean isAdmin = auth.getAuthorities().stream()
.anyMatch(a -> "ROLE_ADMIN".equals(a.getAuthority()));
| Strategy | Use |
|---|---|
MODE_THREADLOCAL (default) |
Per-thread |
MODE_INHERITABLETHREADLOCAL |
Child threads inherit |
MODE_GLOBAL |
JVM-wide (rarely useful) |