The use case is to store the user name/id in a database, when logging out. When trying to retrieve the username from the context, I will receive always null.
In Debug mode I evaluated this line in CustomLogoutHandler via my IDE (not in the code of CustomLogoutHandler
) and it's null.
SecurityContextHolder.getContext().getAuthentication().getName();
@Service
public class CustomLogoutHandler implements LogoutHandler {
private final AuditorService auditorService;
public CustomLogoutHandler(AuditorService auditorService) {
this.auditorService = auditorService;
}
@Override
public void logout(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Authentication authentication) {
auditorService.addLogoutActivity(ActivityEnum.LOGOUT, null, authentication.getName());
// authentication object is null
}
}
The SecurityConfiguration
class
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true)
@Import(SecurityProblemSupport.class)
@Configuration
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
private final JHipsterProperties jHipsterProperties;
private final TokenProvider tokenProvider;
private final CorsFilter corsFilter;
private final SecurityProblemSupport problemSupport;
private final DomainUserDetailsService domainUserDetailsService;
private final ApplicationProperties applicationProperties;
private final CustomLogoutHandler customLogoutHandler;
// constructor omitted
@Override
public void configure(WebSecurity web) {
web
.ignoring()
.antMatchers(HttpMethod.OPTIONS, "/**")
.antMatchers("/app/**/*.{js,html}")
.antMatchers("/i18n/**")
.antMatchers("/content/**")
.antMatchers("/swagger-ui/**")
.antMatchers("/test/**");
}
@Override
public void configure(HttpSecurity http) throws Exception {
// @formatter:off
http
.logout(logout -> logout
.logoutUrl("/api/logout")
.logoutSuccessUrl("/login")
.addLogoutHandler(customLogoutHandler)
.invalidateHttpSession(true)
.logoutSuccessHandler(new HttpStatusReturningLogoutSuccessHandler(HttpStatus.OK))
)
.csrf()
.disable()
.addFilterBefore(corsFilter, UsernamePasswordAuthenticationFilter.class)
.exceptionHandling()
.authenticationEntryPoint(problemSupport)
.accessDeniedHandler(problemSupport)
.and()
.headers()
.contentSecurityPolicy(jHipsterProperties.getSecurity().getContentSecurityPolicy())
.and()
.referrerPolicy(ReferrerPolicyHeaderWriter.ReferrerPolicy.STRICT_ORIGIN_WHEN_CROSS_ORIGIN)
.and()
.permissionsPolicy().policy("camera=(), fullscreen=(self), geolocation=(), gyroscope=(), magnetometer=(), microphone=(), midi=(), payment=(), sync-xhr=()")
.and()
.frameOptions()
.deny()
.and()
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.authorizeRequests()
.antMatchers("/api/authenticate").permitAll()
.antMatchers("/api/logout").permitAll()
.antMatchers("/api/register").permitAll()
.antMatchers("/api/activate").permitAll()
.antMatchers("/api/account/reset-password/init").permitAll()
.antMatchers("/api/account/reset-password/finish").permitAll()
.antMatchers("/api/admin/**").hasAuthority(AuthoritiesConstants.ADMIN)
.antMatchers("/api/**").authenticated()
.antMatchers("/websocket/**").authenticated()
.antMatchers("/management/health").permitAll()
.antMatchers("/management/health/**").permitAll()
.antMatchers("/management/info").permitAll()
.antMatchers("/management/prometheus").permitAll()
.antMatchers("/management/**").hasAuthority(AuthoritiesConstants.ADMIN)
.antMatchers("/audit/**").hasAuthority(AuthoritiesConstants.AUDITOR)
.and()
.httpBasic()
.and()
.apply(securityConfigurerAdapter());
// @formatter:on
}
// stuff omitted
}
I don't know where the context and the user name/id gets consumed in the sense of destroyed, before I can catch him/her, who issued the logout event and store it to the DB.
Is it because something else behind the scenes destroys the
Authentication
contents? I once had the issue, when twoApplicationListener<SessionDisconnectEvent>
classes were listening to a disconnect. The first consumed the event and the SecurityContextHolder returned the user's name and did it's work and the second class with itsonApplicationEvent(SessionDisconnectEvent event)
couldn't work becauseSecurityContextHolder
was null or empty (don't remember exactly). It seems, thatSecurityContextHolder
can only be used once for the identical event. Forgive me, but I don't understand Spring behind the curtains completely.Shall I setup a "/custom_logout" as another endpoint and then internally navigate to spring's "/logout" endpoint, when the use case, described initially, is completed?