Spring-Security: return status 401 When AuthenticationManager throws a BadCredentialsException

First of all, I would like to point out that I do not know Spring Security very well, in fact I know quite a bit about its interfaces and classes, but I need not a simple task, This is understandable. My code is based on the following post on the Spring Security Forum (I don't have the same problem as the post owner): http://forum.spring.io/forum/spring-projects/security/747178-security-filter-chain- is-always-calling-authenticationmanager-twice-per-request

I am programming a Spring MVC system that will serve HTTP content, but it has a preliminary check for this (which I am currently using RequestHeaderAuthenticationFilter with a custom AuthenticationManager ).

To authorize the user, I will check the token for two sources, the Redis database and the Oracle database. If the token is not found in any of these sources, the authentication method of my custom AuthenticationManager throws a BadCredentialsException (which, I believe, matches the AuthenticationManager contract).

Now I would like to return in the HTTP 401 - Unauthorized response, but Spring continues to return 500 - Server Error. Can I configure my installation to return only 401 not 500?

Here is the relevant code:

SecurityConfig - Spring's main security configuration

package br.com.oiinternet.imoi.web.config; import javax.validation.constraints.NotNull; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.boot.autoconfigure.security.SecurityProperties; import org.springframework.context.ApplicationContext; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.core.annotation.Order; import org.springframework.http.HttpMethod; import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; import org.springframework.security.config.http.SessionCreationPolicy; import org.springframework.security.web.AuthenticationEntryPoint; import org.springframework.security.web.access.AccessDeniedHandler; import org.springframework.security.web.access.AccessDeniedHandlerImpl; import org.springframework.security.web.authentication.Http403ForbiddenEntryPoint; import org.springframework.security.web.authentication.logout.LogoutFilter; import org.springframework.security.web.authentication.preauth.RequestHeaderAuthenticationFilter; @Configuration @EnableWebSecurity @Order(SecurityProperties.ACCESS_OVERRIDE_ORDER) public class SecurityConfig extends WebSecurityConfigurerAdapter { private static final Logger LOG = LoggerFactory.getLogger(SecurityConfig.class); public static final String X_AUTH_TOKEN = "X-Auth-Token"; private AccessDeniedHandler accessDeniedHandler = new AccessDeniedHandlerImpl(); @Bean public AuthenticationManager authenticationManager() { return new TokenBasedAuthenticationManager(); } @Bean public AuthenticationEntryPoint authenticationEntryPoint() { return new Http403ForbiddenEntryPoint(); } @Bean public RequestHeaderAuthenticationFilter requestHeaderAuthenticationFilter( final AuthenticationManager authenticationManager) { RequestHeaderAuthenticationFilter filter = new RequestHeaderAuthenticationFilter(); filter.setAuthenticationManager(authenticationManager); filter.setExceptionIfHeaderMissing(false); filter.setPrincipalRequestHeader(X_AUTH_TOKEN); filter.setInvalidateSessionOnPrincipalChange(true); filter.setCheckForPrincipalChanges(true); filter.setContinueFilterChainOnUnsuccessfulAuthentication(false); return filter; } /** * Configures the HTTP filter chain depending on configuration settings. * * Note that this exception is thrown in spring security headerAuthenticationFilter chain and will not be logged as * error. Instead the ExceptionTranslationFilter will handle it and clear the security context. Enabling DEBUG * logging for 'org.springframework.security' will help understanding headerAuthenticationFilter chain */ @Override protected void configure(final HttpSecurity http) throws Exception { RequestHeaderAuthenticationFilter requestHeaderAuthenticationFilter = fromContext(http, RequestHeaderAuthenticationFilter.class); AuthenticationEntryPoint authenticationEntryPoint = fromContext(http, AuthenticationEntryPoint.class); http.authorizeRequests() .antMatchers(HttpMethod.GET, "/auth").permitAll() .antMatchers(HttpMethod.GET, "/**").authenticated() .antMatchers(HttpMethod.POST, "/**").authenticated() .antMatchers(HttpMethod.HEAD, "/**").authenticated() .and() .sessionManagement() .sessionCreationPolicy(SessionCreationPolicy.STATELESS) .and().securityContext() .and().exceptionHandling() .authenticationEntryPoint(authenticationEntryPoint) .accessDeniedHandler(accessDeniedHandler) .and() .addFilterBefore(requestHeaderAuthenticationFilter, LogoutFilter.class); } private <T> T fromContext(@NotNull final HttpSecurity http, @NotNull final Class<T> requiredType) { @SuppressWarnings("SuspiciousMethodCalls") ApplicationContext ctx = (ApplicationContext) http.getSharedObjects().get(ApplicationContext.class); return ctx.getBean(requiredType); } } 

TokenBasedAuthenticationManager - my custom AuthenticationManager

 package br.com.oiinternet.imoi.web.config; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.authentication.BadCredentialsException; import org.springframework.security.core.Authentication; import org.springframework.security.core.AuthenticationException; import br.com.oi.oicommons.lang.message.Messages; import br.com.oiinternet.imoi.service.AuthService; import br.com.oiinternet.imoi.web.security.auth.AuthenticationAuthorizationToken; public class TokenBasedAuthenticationManager implements AuthenticationManager { @Autowired private AuthService authService; @Autowired private Messages messages; @Override public Authentication authenticate(Authentication authentication) throws AuthenticationException { final String token = (String) authentication.getPrincipal(); if (authService.isAuthorized(token) || authService.authenticate(token)) { return new AuthenticationAuthorizationToken(token); } throw new BadCredentialsException(messages.getMessage("access.bad.credentials")); } } 

Example request / response loop using curl:

 user@user-note :curl --header "X-Auth-Token: 2592cd35124dc3d79bdd82407220a6ea7fad9b8b313a1205cf1824a5ce726aa8dd763cde8c05faadae48b47252de95b0" http://localhost:8081/test/auth -v * Hostname was NOT found in DNS cache * Trying 127.0.0.1... * Connected to localhost (127.0.0.1) port 8081 (#0) > GET /test/auth HTTP/1.1 > User-Agent: curl/7.35.0 > Host: localhost:8081 > Accept: */* > X-Auth-Token: 2592cd35124dc3d79bdd82407220a6ea7fad9b8b313a1205cf1824a5ce726aa8dd763cde8c05faadae48b47252de95b0 > < HTTP/1.1 500 Server Error < X-Content-Type-Options: nosniff < X-XSS-Protection: 1; mode=block < Pragma: no-cache < X-Frame-Options: DENY < Content-Type: application/json;charset=UTF-8 < Connection: close * Server Jetty(9.1.0.v20131115) is not blacklisted < Server: Jetty(9.1.0.v20131115) < * Closing connection 0 {"timestamp":1414513379405,"status":500,"error":"Internal Server Error","exception":"org.springframework.security.authentication.BadCredentialsException","message":"access.bad.credentials","path":"/test/auth"} 
+6
source share
1 answer

I looked at the source. It would seem that you could achieve this quite easily by creating subclasses of RequestHeaderAuthenticationFilter and overriding the unsuccessfulAuthentication (...) method, which is called immediately after detecting a failed authentication and immediately before creating a new RuntimeException:

 public class MyRequestHeaderAuthenticationFilter extends RequestHeaderAuthenticationFilter { @Override protected void unsuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response, AuthenticationException failed) { super.unsuccessfulAuthentication(request, response, failed); // see comments in Servlet API around using sendError as an alternative response.setStatus(HttpServletResponse.SC_UNAUTHORIZED); } } 

Then just point your filter configuration to an instance of this.

+15
source

Source: https://habr.com/ru/post/1205716/


All Articles