Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Spring Security with basic auth redirecting to /error for invalid credentials

I have a spring boot application running with spring security using basic auth. When proper basic auth credentials are supplied, everything is good, but for incorrect auth credentials, spring comes up with a HttpRequestMethodNotSupportedException: Request method 'POST' not supported exception.

According to the logs, the authentication failure has been identified by spring, but that's not what comes out.

2015-08-12 09:33:10.922 INFO 16988 --- [nio-8080-exec-4] o.s.b.a.audit.listener.AuditListener : AuditEvent [timestamp=Wed Aug 12 09:33:10 AEST 2015, principal=anonymousUser, type=AUTHORIZATION_FAILURE, data={type=org.springframework.security.access.AccessDeniedException, message=Access is denied}] 2015-08-12 09:33:10.927 TRACE 16988 --- [nio-8080-exec-4] o.s.web.servlet.DispatcherServlet : Bound request context to thread: FirewalledRequest[ org.apache.catalina.core.ApplicationHttpRequest@483e1fc6] 2015-08-12 09:33:10.927 DEBUG 16988 --- [nio-8080-exec-4] o.s.web.servlet.DispatcherServlet : DispatcherServlet with name 'dispatcherServlet' processing POST request for [/myapplication/error] 2015-08-12 09:33:10.927 TRACE 16988 --- [nio-8080-exec-4] o.s.web.servlet.DispatcherServlet : Testing handler map [org.springframework.web.servlet.handler.SimpleUrlHandlerMapping@331bb032] in DispatcherServlet with name 'dispatcherServlet' 2015-08-12 09:33:10.927 TRACE 16988 --- [nio-8080-exec-4] o.s.w.s.handler.SimpleUrlHandlerMapping : No handler mapping found for [/error] 2015-08-12 09:33:10.927 TRACE 16988 --- [nio-8080-exec-4] o.s.web.servlet.DispatcherServlet : Testing handler map [org.springframework.boot.actuate.endpoint.mvc.EndpointHandlerMapping@69e79b9b] in DispatcherServlet with name 'dispatcherServlet' 2015-08-12 09:33:10.927 TRACE 16988 --- [nio-8080-exec-4] o.s.web.servlet.DispatcherServlet : Testing handler map [org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping@27cc0354] in DispatcherServlet with name 'dispatcherServlet' 2015-08-12 09:33:10.927 DEBUG 16988 --- [nio-8080-exec-4] s.w.s.m.m.a.RequestMappingHandlerMapping : Looking up handler method for path /error 2015-08-12 09:33:10.928 DEBUG 16988 --- [nio-8080-exec-4] .m.m.a.ExceptionHandlerExceptionResolver : Resolving exception from handler [null]: org.springframework.web.HttpRequestMethodNotSupportedException: Request method 'POST' not supported

With the above logs & after debugging the spring source, I found out that upon identifying that the credentials are incorrect, spring creates a BadCredentials exception and then tries to redirect to “/error”, this redirection is what causes the HttpMethodNotAllowed exception.(my application doesn't have a /error endpoint).

I tried to tell spring not to use /error by configuring the following,

`public class ServerCustomization extends ServerProperties {

@Override
public void customize(ConfigurableEmbeddedServletContainer container) {

    super.customize(container);
    container.addErrorPages(new ErrorPage(HttpStatus.UNAUTHORIZED, null));

}`

This will make spring stop spitting out the HttpMethodnotallowed exception and make it come up with a 401(Unauthorized), BUT my this exception is not caught by my Exception handler (configured using @ControllerAdvice).

I also tried configuring a custom authentication entry point like below without any luck.

@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {

AlwaysSendUnauthorized401AuthenticationEntryPoint alwaysSendUnauthorized401AuthenticationEntryPoint = 
        new AlwaysSendUnauthorized401AuthenticationEntryPoint();


@Override
protected void configure(HttpSecurity http) throws Exception {
    http.headers().httpStrictTransportSecurity().xssProtection().and().authorizeRequests().anyRequest().fullyAuthenticated()
            .and().csrf().disable();

    http.exceptionHandling().authenticationEntryPoint(alwaysSendUnauthorized401AuthenticationEntryPoint);
}

public class AlwaysSendUnauthorized401AuthenticationEntryPoint implements AuthenticationEntryPoint {
    @Override
    public final void commence(HttpServletRequest request, HttpServletResponse response,
            AuthenticationException authException) throws IOException {
        response.sendError(HttpServletResponse.SC_UNAUTHORIZED);
    }
}

}

Is there any way we can specify to spring not to redirect to /error and return the Bad Credentials Exception ?

like image 286
dds Avatar asked Aug 12 '15 00:08

dds


People also ask

What is the default username and password for spring security?

The default username is: user and the default password will be printed in the console at the time when your Spring Boot project is starting.

What is HTTP basic authentication in Spring Security?

In case of HTTP basic authentication, instead of using a form, user login credentials are passed on the HTTP request header, precisely “Authorization” request header. This header allows you to send username and password into request headers instead of the request body, as is the case of form login authentication.

How to login spring security?

Once application up, open the http://localhost:8080/login URL in your browser. We will have the custom login page from spring security. Provide the valid credentials (which you used while registration), click on the “Sign In” button.


1 Answers

I created a sample Spring Boot app, with the following security config:

@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
        auth.inMemoryAuthentication().withUser("test").password("password").roles("USER");
    }

    @Bean
    public AuthenticationEntryPoint authenticationEntryPoint() {
        return (request, response, authException) -> response.sendError(HttpServletResponse.SC_UNAUTHORIZED);
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.headers().httpStrictTransportSecurity().xssProtection()
                .and().authorizeRequests().anyRequest().fullyAuthenticated()
                .and().csrf().disable();

        http.httpBasic().authenticationEntryPoint(authenticationEntryPoint());
        http.exceptionHandling().authenticationEntryPoint(authenticationEntryPoint());

    }
}

When entering an invalid username/password (anything other than test/password), I get the following response:

{"timestamp":1439381390204,"status":401,"error":"Unauthorized","message":"Bad credentials","path":"/"}

This error is returned by org.springframework.boot.autoconfigure.web.BasicErrorController class, which if you have a look does define two methods with @RequestMapping("/error") - errorHtml and error. Since you're building an API, it's the second one that should be invoked and I would say that's the "correct" behaviour!

So, first thing, check that you're getting to the BasicErrorController when authentication fails. If you are, make sure that you're hitting the error method NOT errorHtml.

If none of the above proves helpful, check whether someone has overriden the default behaviour of the error controller. One common (and valid) extension is to implement your own org.springframework.boot.autoconfigure.web.ErrorAttributes to change the default error payload. But it's just as easy to replace the entire BasicErrorController with a non-standard implementation, so check for that in your application.

If all else fails and you're adamant that you want to disable Spring's default error handling (which I don't recommend), try adding this to your configuration:

@EnableAutoConfiguration(exclude = {ErrorMvcAutoConfiguration.class})

What this will do is ensure that the error controllers are not loaded into the application context.

like image 144
grigori Avatar answered Nov 03 '22 23:11

grigori