Using spring boot 3.3.2
I have an auth filter that extends AbstractPreAuthenticatedProcessingFilter, which itself extends GenericFilterBean.
When my filter throws a ServletException, because it didn’t like what it found in the request, my custom /error page is not being used. I get the stack trace from Tomcat itself. Obviously, I want something “pretty” to show the end user.
It seems as though the filter is not aware of being within the context of Spring. What am I missing here?
In other scenarios, where this particular filter does not throw an exception, the error page presents itself as expected.
application.properties
spring.application.name=Pre Auth Filter
logging.level.org.example=DEBUG
logging.level.org.springframework.security=DEBUG
logging.level.org.springframework.web=DEBUG
org.springframework.security.config.annotation.web.builders.WebSecurity.debugEnabled=true
spring.mvc.log-request-details=true
spring.codec.log-request-details=true
spring.autoconfigure.exclude=org.springframework.boot.autoconfigure.security.servlet.UserDetailsServiceAutoConfiguration
validReferers={'http://site1.example.com','http://site2.example.com'}
server.error.whitelabel.enabled=false
server.error.path=/error
SecurityConfig
@Configuration
@EnableWebSecurity(debug = true)
public class SecurityConfig {
@Value("#{${validReferers}}")
private List<String> validReferers;
@Bean
public AuthenticationProvider authenticationProvider() {
PreAuthenticatedAuthenticationProvider provider = new PreAuthenticatedAuthenticationProvider();
provider.setPreAuthenticatedUserDetailsService( new CustomPreAuthUserDetailsService() );
return provider;
}
@Bean
public AuthenticationManager authenticationManager(AuthenticationProvider authenticationProvider) {
return new ProviderManager(authenticationProvider);
}
public CustomPreAuthFilter customPreAuthFilter( AuthenticationManager authenticationManager ) {
CustomPreAuthFilter customPreAuthFilter = new CustomPreAuthFilter( validReferers );
customPreAuthFilter.setAuthenticationManager( authenticationManager );
customPreAuthFilter.setPrincipalRequestParam( "email" );
customPreAuthFilter.setAuthenticationDetailsSource( new CustomPreAuthenticationDetailsSource() );
customPreAuthFilter.setAuthenticationSuccessHandler( new CustomPreAuthSuccessHandler() );
return customPreAuthFilter;
}
@Bean
SecurityFilterChain securityFilterChain(HttpSecurity http,
AuthenticationManager authenticationManager) throws Exception {
return http
.csrf(AbstractHttpConfigurer::disable)
.formLogin(AbstractHttpConfigurer::disable)
.httpBasic(AbstractHttpConfigurer::disable)
.anonymous(AbstractHttpConfigurer::disable)
.logout(AbstractHttpConfigurer::disable)
.authorizeHttpRequests( request ->
request.anyRequest().authenticated() )
.addFilter( customPreAuthFilter( authenticationManager ) )
// permit being loaded into iframe
.headers( headers -> headers.frameOptions(HeadersConfigurer.FrameOptionsConfig::disable) )
.build();
}
}
from the filter
@Override
@ResponseStatus(value= HttpStatus.FORBIDDEN, reason="Direct access not permitted")
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
HttpServletRequest httpRequest = (HttpServletRequest) request;
String referer = httpRequest.getHeader( "Referer" );
HttpSession httpSession = httpRequest.getSession(false);
if ( httpSession == null ) {
log.debug( "no active session yet" );
boolean isValidReferer = false;
if ( referer != null ) {
for (String validReferer : this.validReferers) {
if (referer.startsWith(validReferer)) {
isValidReferer = true;
}
}
}
if ( referer == null || !isValidReferer ) {
throw new ServletException( "Direct access not permitted" );
}
}
super.doFilter( request, response, chain );
}
The entire project sample can be found at https://github.com/smaring/spring-pre-auth-filter
The use-case is that if the initial request contains an email param we will assume that the user was properly authenticated in the referer. The app is loaded into an iframe. If the refer is not valid the app throws the exception.