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)