繁体   English   中英

Spring-Security:返回状态401当AuthenticationManager抛出BadCredentialsException时

[英]Spring-Security: Return Status 401 When AuthenticationManager Throws BadCredentialsException

首先,我想指出我不太了解Spring Security, 实际上我对它的接口和类知之甚少 ,但我有一个不那么简单的任务要做,并且不能完全解决它。 我的代码基于Spring安全论坛的以下帖子(我没有和帖子所有者一样的问题): http//forum.spring.io/forum/spring-projects/security/747178-security-过滤器链是-总是主叫的AuthenticationManager-两次每次请求

我正在编写一个Spring MVC系统,它将提供HTTP内容,但为了做到这一点,它有一个preauth检查(我目前正在使用带有自定义AuthenticationManager的 RequestHeaderAuthenticationFilter )。

为了授权用户,我将针对两个来源检查令牌,即Redis缓存“数据库”和Oracle。 如果在任何这些源中找不到令牌,我的自定义AuthenticationManager的authenticate方法会抛出BadCredentialsException(我认为它遵循AuthenticationManager合同)。

现在我想返回HTTP响应401 - 未经授权,但Spring不断返回500 - 服务器错误。 是否可以自定义我的设置以仅返回401而不是500?

这是相关的代码:

SecurityConfig - 主要的弹簧安全配置

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 - 我的自定义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"));
    }

}

使用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"}

我看了看源头。 看起来你可以通过继承RequestHeaderAuthenticationFilter并覆盖在检测到失败的身份验证之后以及抛出新的RuntimeException之前调用的unsuccessfulAuthentication(...)方法来相当容易地实现这一点:

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

然后只需将Filter Config指向此实例即可。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM