【问题标题】:Spring Security with basic auth redirecting to /error for invalid credentials具有基本身份验证的 Spring Security 重定向到 /error 以获取无效凭据
【发布时间】:2015-11-04 10:01:37
【问题描述】:

我有一个使用基本身份验证的 spring 安全运行的 spring boot 应用程序。 当提供正确的基本身份验证凭据时,一切都很好,但对于不正确的身份验证凭据,spring 会出现HttpRequestMethodNotSupportedException: Request method 'POST' not supported 异常。

根据日志,spring已经识别出身份验证失败,但结果不是这样。

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

通过上面的日志&调试spring源码后发现,在识别出凭据不正确后,spring会创建一个BadCredentials异常,然后尝试重定向到“/error”,这个重定向是导致HttpMethodNotAllowed异常的原因.(我的应用程序没有 /error 端点)。

我试图通过配置以下内容告诉spring不要使用/error,

`公共类 ServerCustomization 扩展 ServerProperties {

@Override
public void customize(ConfigurableEmbeddedServletContainer container) {

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

}`

这将使 spring 停止吐出 HttpMethodnotallowed 异常并使其出现 401(未授权),但我的异常处理程序(使用 @ControllerAdvice 配置)未捕获此异常。

我还尝试配置自定义身份验证入口点,如下所示,但没有任何运气。

@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);
    }
}

}

有什么方法可以指定 spring 不重定向到 /error 并返回 Bad Credentials Exception 吗?

【问题讨论】:

    标签: spring security authentication basic-authentication credentials


    【解决方案1】:

    我创建了一个示例 Spring Boot 应用程序,具有以下安全配置:

    @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());
    
        }
    }
    

    当输入无效的用户名/密码(测试/密码以外的任何内容)时,我收到以下响应:

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

    这个错误是由org.springframework.boot.autoconfigure.web.BasicErrorController类返回的,如果你看一下,它确实定义了两个方法@RequestMapping("/error")-errorHtml和error。由于您正在构建一个 API,因此应该调用第二个 API,我会说这是“正确”的行为!

    所以,首先,请检查您是否在身份验证失败时访问了BasicErrorController。如果是,请确保您使用的是 error 方法而不是 errorHtml。

    如果以上都没有帮助,请检查是否有人覆盖了错误控制器的默认行为。一种常见(且有效)的扩展是实现您自己的org.springframework.boot.autoconfigure.web.ErrorAttributes 以更改默认错误有效负载。但是用非标准实现替换整个 BasicErrorController 也很容易,因此请在您的应用程序中检查它。

    如果所有其他方法都失败了,并且您坚持要禁用 Spring 的默认错误处理(我不建议这样做),请尝试将其添加到您的配置中:

    @EnableAutoConfiguration(exclude = {ErrorMvcAutoConfiguration.class})

    这将确保错误控制器不会加载到应用程序上下文中。

    【讨论】:

    • 感谢@grigori 为我指明了正确的方向。确实存在导致问题的 BasicErrorController 的非标准实现。使用适当的错误控制器解决了这个问题。
    • 谢谢,对我来说,mvc-exclude 修复了它,有时我喜欢 SpringBoot 的默认设置,有时不喜欢 :) 但这让我想起了没有做魔术 @EnableAutoConfiguration 而是只是专门的一个子集需要*配置。
    【解决方案2】:

    我认为您应该使用http.httpBasic().authenticationEntryPoint 而不是http.exceptionHandling().authenticationEntryPoint。对于基于表单的身份验证,这对我有用:

    http
        .formLogin()
            .failureHandler(authenticationFailureHandler())
            ...
        ...
    

    对于身份验证失败处理程序,可以使用 Spring 的SimpleUrlAuthenticationFailureHandler。当没有任何参数实例化时,它会做我们想要的:

    @Bean
    public AuthenticationFailureHandler authenticationFailureHandler() {
        return new SimpleUrlAuthenticationFailureHandler();
    }   
    

    This 是我完整的安全配置文件,以防万一。

    【讨论】:

    • 谢谢@Sanjay,稍后会尝试一下,然后告诉你进展如何
    • 我确实尝试了您的两种解决方案,但还没有运气。 formLogin() 看起来不正确,因为我的是一个重新设计的 API。关于authenticationEntryPoint 的其他建议看起来很有希望,但没有产生任何结果。
    猜你喜欢
    • 2013-06-17
    • 2011-02-11
    • 2016-03-20
    • 1970-01-01
    • 2016-04-30
    • 1970-01-01
    • 2013-01-11
    • 2015-07-22
    • 2017-11-11
    相关资源
    最近更新 更多