- 5.1.3 Applying custom authentication logic
- 5.2 Using the SecurityContext
- 5.2.1 Using a holding strategy for the security context
- 5.2.2 Using a holding strategy for asynchronous calls
- 5.2.3 Using a holding strategy for standalone applications
- 5.2.4 Forwarding the securtiy context with DelegatingSecurityContextRunnable
- 5.2.5 Forwarding the security context with DelegatingSecurityContextExecutorService
- 5.3 Understanding HTTP Basic and form-based login authentications
- 5.3.1 Using and configuring HTTP Basic
- 5.3.2 Implementing authentication with form-based login
5.1.3 Applying custom authentication logic
Overriding the supports methods of the AuthenticationProvider
@Componentpublic class CustomAuthenticationProvider implements AuthenticationProvider {//....@Overridepublic boolean supports(Class<?> authenticationType) {return authenticationType.equals(UsernamePasswordAuthenticationToken.class);}}
implementing the authentication logic
@Componentpublic class CustomAuthenticationProvider implements AuthenticationProvider {@Autowiredprivate UserDetailsService userDetailsService;@Autowiredprivate PasswordEncoder passwordEncoder;@Overridepublic Authentication authenticate(Authentication authentication) {String username = authentication.getName();String password = authentication.getCredentials().toString();UserDetails u = userDetailsService.loadUserByUsername(username);if (passwordEncoder.matches(password, u.getPassword())) {return new UsernamePasswordAuthenticationToken(username, password, u.getAuthorities());} else {throw new BadCredentialsException("Something went wrong!");}}//...}
Registering the AuthenticationProvider in the configuration class
@Configurationpublic class ProjectConfig extends WebSecurityConfigurerAdapter {@Autowiredprivate AuthenticationProvider authenticationProvider;@Overrideprotected void configure(AuthenticationManagerBuilder auth) {auth.authenticationProvider(authenticationProvider);}}
5.2 Using the SecurityContext
The SecurityContext interface
public interface SecurityContext extends Serializable {Authentication getAuthentication();void setAuthentication(Authentication authentication);}
Spring Security offers three strategies to manage the SecurityContext with an object in the role of a manager. It’s named the SecurityContextHolder:
- MODE_THREADLOCAL
- MODE_INHERITABLETHREADLOCAL
-
5.2.1 Using a holding strategy for the security context
Obtaining the SecurityContext from the SecurityContextHolder
@RestControllerpublic class HelloController {@Autowiredprivate HelloService helloService;@GetMapping("/hello")public String hello() {SecurityContext context = SecurityContextHolder.getContext();Authentication a = context.getAuthentication();return "Hello, " + a.getName() + "!";}}
Spring injects Authentication value in the parameter of the method
@GetMapping("/hello")public String hello(Authentication a) {return "Hello, " + a.getName() + "!";}
5.2.2 Using a holding strategy for asynchronous calls
An @Async, the method is executed on a separate thread
@GetMapping("/bye")@Asyncpublic void goodbye() {SecurityContext context = SecurityContextHolder.getContext();String username = context.getAuthentication().getName();}
To enable the functionality of the @Async annotation, you need must to do: ```java @Configuration @EnableAsync public class ProjectConfig {
}
If you try the code as it is now, it throws a NullPointerException <br />you could solve the problem by using the MODE_INHERITABLETHREADLOCAL strategy.<br />This can be set either by calling the SecurityContextHolder.setStrategyName() method or by using the system property spring.security.strategy<br />Using InitializingBean to set SecurityContextHolder mode```java@Configuration@EnableAsyncpublic class ProjectConfig {@Beanpublic InitializingBean initializingBean() {return () -> SecurityContextHolder.setStrategyName(SecurityContextHolder.MODE_INHERITABLETHREADLOCAL);}}
5.2.3 Using a holding strategy for standalone applications
5.2.4 Forwarding the securtiy context with DelegatingSecurityContextRunnable
Defining a Callable object and executing it as a task on a separate thread
@GetMapping("/ciao")public String ciao() throws Exception {Callable<String> task = () -> {SecurityContext context = SecurityContextHolder.getContext();return context.getAuthentication().getName();};//...}
Defining an ExecutorService and submitting the task
@GetMapping("/ciao")public String ciao() throws Exception {Callable<String> task = () -> {SecurityContext context = SecurityContextHolder.getContext();return context.getAuthentication().getName();};ExecutorService e = Executors.newCachedThreadPool();try {var contextTask = new DelegatingSecurityContextCallable<>(task);return "Ciao, " + e.submit(contextTask).get() + "!";} finally {e.shutdown();}}
5.2.5 Forwarding the security context with DelegatingSecurityContextExecutorService

Propagating the SecurityContext
@GetMapping("/hola")public String hola() throws Exception {Callable<String> task = () -> {SecurityContext context = SecurityContextHolder.getContext();return context.getAuthentication().getName();};ExecutorService e = Executors.newCachedThreadPool();e = new DelegatingSecurityContextExecutorService(e);try {return "Hola, " + e.submit(task).get() + "!";} finally {e.shutdown();}}
5.3 Understanding HTTP Basic and form-based login authentications
5.3.1 Using and configuring HTTP Basic
Setting the HTTP Basic authentication method
@Configurationpublic class ProjectConfig extends WebSecurityConfigurerAdapter {@Overrideprotected void configure(HttpSecurity http) throws Exception {http.httpBasic();}}
Configuring the realm name for the response of failed authentications
@Configurationpublic class ProjectConfig extends WebSecurityConfigurerAdapter {@Overrideprotected void configure(HttpSecurity http) throws Exception {http.httpBasic(c -> {c.realmName("OTHER");});http.authorizeRequests().anyRequest().authenticated();}}
Implementing an AuthenticationEntryPoint
public class CustomEntryPoint implements AuthenticationEntryPoint {@Overridepublic void commence(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AuthenticationException e) throws IOException {httpServletResponse.addHeader("message", "Luke, I am your father!");httpServletResponse.sendError(HttpStatus.UNAUTHORIZED.value());}}
Setting the custion AuthenticationEntryPoint
@Configurationpublic class ProjectConfig extends WebSecurityConfigurerAdapter {@Overrideprotected void configure(HttpSecurity http) throws Exception {http.httpBasic(c -> {c.realmName("OTHER");c.authenticationEntryPoint(new CustomEntryPoint());});http.authorizeRequests().anyRequest().authenticated();}}
5.3.2 Implementing authentication with form-based login
Changing the authentication method to a form-based login
@Configurationpublic class ProjectConfig extends WebSecurityConfigurerAdapter {@Autowiredprivate CustomAuthenticationSuccessHandler authenticationSuccessHandler;@Autowiredprivate CustomAuthenticationFailureHandler authenticationFailureHandler;@Overrideprotected void configure(HttpSecurity http) throws Exception {http.formLogin()http.authorizeRequests().anyRequest().authenticated();}}
Defining the action method of the controller for the home.html page
@Controllerpublic class HelloController {@GetMapping("/home")public String home() {return "home.html";}}
Setting a default success URL for the login form
@Overrideprotected void configure(HttpSecurity http)throws Exception {http.formLogin().defaultSuccessUrl("/home", true);http.authorizeRequests().anyRequest().authenticated();}
Implementing an AuthenticationSuccessHandler
@Componentpublic class CustomAuthenticationSuccessHandler implements AuthenticationSuccessHandler {@Overridepublic void onAuthenticationSuccess(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Authentication authentication) throws IOException {var authorities = authentication.getAuthorities();var auth = authorities.stream().filter(a -> a.getAuthority().equals("read")).findFirst();if (auth.isPresent()) {httpServletResponse.sendRedirect("/home");} else {httpServletResponse.sendRedirect("/error");}}}
Implementing an AuthenticationFailureHandler
@Componentpublic class CustomAuthenticationFailureHandler implements AuthenticationFailureHandler {@Overridepublic void onAuthenticationFailure(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AuthenticationException e) {httpServletResponse.setHeader("failed", LocalDateTime.now().toString());}}
Registering the handler objects in the configuration class
@Configurationpublic class ProjectConfig extends WebSecurityConfigurerAdapter {@Autowiredprivate CustomAuthenticationSuccessHandler authenticationSuccessHandler;@Autowiredprivate CustomAuthenticationFailureHandler authenticationFailureHandler;@Overrideprotected void configure(HttpSecurity http) throws Exception {http.formLogin().successHandler(authenticationSuccessHandler).failureHandler(authenticationFailureHandler)http.authorizeRequests().anyRequest().authenticated();}}
Using form-based login and HTTP Basic together
@Configurationpublic class ProjectConfig extends WebSecurityConfigurerAdapter {@Autowiredprivate CustomAuthenticationSuccessHandler authenticationSuccessHandler;@Autowiredprivate CustomAuthenticationFailureHandler authenticationFailureHandler;@Overrideprotected void configure(HttpSecurity http) throws Exception {http.formLogin().successHandler(authenticationSuccessHandler).failureHandler(authenticationFailureHandler).and().httpBasic();http.authorizeRequests().anyRequest().authenticated();}}
