Authentication & Authorisation
IntermediateAuthentication verifies who the caller is (UserDetailsService, in-memory, LDAP); authorisation decides what resources they can access (@PreAuthorize, security DSL).
Overview
Spring Security separates the concerns of authentication (who are you?) and authorisation (what can you do?). Authentication is handled by an AuthenticationManager that delegates to one or more AuthenticationProvider implementations — the most common being DaoAuthenticationProvider, which calls UserDetailsService to load a user by username and then compares the password using a PasswordEncoder. Authorisation is enforced at two levels: the HTTP layer (via the SecurityFilterChain request matcher rules) and the method layer (@PreAuthorize, @PostAuthorize powered by AOP). Spring Security 6 replaced the deprecated WebSecurityConfigurerAdapter with a bean-based SecurityFilterChain; all security config now lives in @Bean methods.
UserDetailsService and SecurityFilterChain (Spring Security 6)
Load users from the database and define HTTP-level security rules in a SecurityFilterChain bean.
@Configuration
@EnableWebSecurity
@EnableMethodSecurity // enables @PreAuthorize on service methods
public class SecurityConfig {
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.csrf(csrf -> csrf.disable()) // disable for REST APIs
.sessionManagement(session -> session
.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
.authorizeHttpRequests(auth -> auth
.requestMatchers("/auth/**", "/public/**").permitAll()
.requestMatchers("/admin/**").hasRole("ADMIN")
.anyRequest().authenticated()
)
.addFilterBefore(jwtAuthFilter, // JWT filter before UsernamePassword
UsernamePasswordAuthenticationFilter.class);
return http.build();
}
@Bean
public AuthenticationManager authenticationManager(
UserDetailsService userDetailsService,
PasswordEncoder encoder) {
DaoAuthenticationProvider provider = new DaoAuthenticationProvider();
provider.setUserDetailsService(userDetailsService);
provider.setPasswordEncoder(encoder);
return new ProviderManager(provider);
}
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder(12);
}
}UserDetailsService loading from database
Implement UserDetailsService to load user credentials and roles from a JPA repository. Spring Security calls this during authentication.
@Service
@RequiredArgsConstructor
public class AppUserDetailsService implements UserDetailsService {
private final UserRepository userRepository;
@Override
public UserDetails loadUserByUsername(String email)
throws UsernameNotFoundException {
User user = userRepository.findByEmail(email)
.orElseThrow(() -> new UsernameNotFoundException("User not found: " + email));
return org.springframework.security.core.userdetails.User.builder()
.username(user.getEmail())
.password(user.getPasswordHash()) // already BCrypt-encoded in DB
.roles(user.getRole().name()) // e.g. "USER", "ADMIN"
.accountExpired(!user.isActive())
.credentialsExpired(user.isPasswordExpired())
.build();
}
}Method-level authorisation with @PreAuthorize
@PreAuthorize runs before the method and can use Spring EL expressions including the authenticated principal, method arguments, and custom permission evaluators.
@Service
public class OrderService {
// Only users with ADMIN role can access
@PreAuthorize("hasRole('ADMIN')")
public List<Order> getAllOrders() { ... }
// User can only see their own orders; ADMIN can see any
@PreAuthorize("hasRole('ADMIN') or #userId == authentication.principal.id")
public List<Order> getOrdersByUser(Long userId) { ... }
// Custom permission evaluator (implement PermissionEvaluator bean)
@PreAuthorize("hasPermission(#orderId, 'ORDER', 'READ')")
public Order getOrder(Long orderId) { ... }
// @PostAuthorize — run after method, filter result
@PostAuthorize("returnObject.ownerId == authentication.principal.id")
public Order getOrderSecure(Long orderId) { ... }
}
// Extract current user anywhere in the app
@Component
public class SecurityUtils {
public static Long currentUserId() {
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
return ((AppUserDetails) auth.getPrincipal()).getId();
}
}Key Points to Remember
- 1Spring Security 6 uses SecurityFilterChain beans — WebSecurityConfigurerAdapter is removed.
- 2Always use BCryptPasswordEncoder — never store plaintext or MD5/SHA-1 hashes.
- 3@EnableMethodSecurity (replaces @EnableGlobalMethodSecurity) must be on a @Configuration class to activate @PreAuthorize.
- 4STATELESS session management is correct for JWT/REST APIs — no server-side session is created.
- 5SecurityContextHolder stores the current Authentication; use it to access the logged-in user anywhere without parameter threading.
- 6@PostAuthorize is useful to prevent leaking entity data even if a user guesses an ID; runs after the query.
Interview Questions
Sign in to ask AriaWhat is the difference between authentication and authorisation in Spring Security?
How does DaoAuthenticationProvider interact with UserDetailsService and PasswordEncoder?
What is the difference between @PreAuthorize and @Secured, and which should you prefer?
How would you implement row-level security so users can only access their own data?
Explain how the SecurityContextHolder stores and propagates authentication across threads in an async context.
Ask Aria about Authentication & Authorisation
Your personal AI tutor — ask anything about this concept
Revision Status
Personal Notes
Sign in to save personal notes for this topic.
Discussion
Sign in to join the discussion.