【问题标题】:AuthenticationEntryPoint only sometimes calledAuthenticationEntryPoint 只是有时被调用
【发布时间】:2016-09-04 13:32:46
【问题描述】:

我有一个简单的AuthenticationEntryPoint,它应该为未经授权的请求设置 WWW-Authenticate 标头。

@Component
public class CustomAuthenticationEntryPoint implements AuthenticationEntryPoint {

    @Override
    public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException)
            throws IOException, ServletException {
        response.setHeader("WWW-Authenticate", "FormBased");
        response.sendError(401, authException.getMessage());
    }
}

我在AuthorizationServerConfigurer的配置方法之一中使用它

@Override
public void configure(AuthorizationServerSecurityConfigurer authorizationServerSecurityConfigurer) throws Exception {
    authorizationServerSecurityConfigurer.authenticationEntryPoint(authenticationEntryPoint);
}

不过,这个开始方法并不总是被调用。当请求中没有 Authorize 标头或 Authorize 标头值不以“Basic”开头时,它会被调用。但是,如果 Authorize 标头以 'Basic' 开头,则不会调用 begin 方法(并且响应的值为Basic realm="oauth2/client")。如何确保调用此方法?

【问题讨论】:

  • 您将请求发送到哪些 URL?
  • 这些请求是发给/oauth/token的POST

标签: java spring authentication spring-security spring-security-oauth2


【解决方案1】:

为了获得访问令牌,您应该通过HTTP Basic对您的客户端进行身份验证:

Authorization: Basic Base64(client_id:client_secret)

不过,这个开始方法并不总是被调用。它在什么时候被调用 请求中没有 Authorize 标头或 Authorize 时 标头值不以“基本”开头。但是,如果授权 标头以'Basic'开头,不调用begin方法

Spring Security 在内部维护一个过滤器链,其中每个过滤器都有特定的职责,其中一个是 BasicAuthenticationFilter,它将处理基本身份验证。如果你看一下它的doFilterInteral 方法,you would see

if (header == null || !header.startsWith("Basic ")) {
    chain.doFilter(request, response);
    return;
}

如果您没有传递Authorization 标头或您的Authorization 标头不是以Basic 开头,它将跳过当前过滤器以查找安全过滤器链中的其他过滤器。最终它将抛出一个AuthenticationException 的实例,该实例将被ExceptionTranslationFilter 捕获,ExceptionTranslationFilter 将调用您注册的AuthenticationEntryPoint

但是当您传递基本授权标头时,BasicAuthenticationFilter 本身将处理身份验证令牌。如果传递的凭据无效,则BasicAuthenticationFilter would catch the exception itself 并调用BasicAuthenticationEntryPoint,而不是您的AuthenticationEntryPoint

catch (AuthenticationException failed) {
    SecurityContextHolder.clearContext();

    if (debug) {
        this.logger.debug("Authentication request for failed: " + failed);
    }

    this.rememberMeServices.loginFail(request, response);

    onUnsuccessfulAuthentication(request, response, failed);

    if (this.ignoreFailure) {
        chain.doFilter(request, response);
    }
    else {
        this.authenticationEntryPoint.commence(request, response, failed);
    }

    return;
}

【讨论】:

  • 啊,我明白了。如何设置 BasicAuthenticationFilter 使用哪个 AuthenticationEntryPoint?
【解决方案2】:

正如 AliDehghani 所指出的,这是因为 BasicAuthenticationFilter 使用 BasicApplicationEntryPoint 而不管 AuthorizationServerSecurityConfigurer 中声明的 ApplicationEntryPoint。为了让BasicAuthenticationFilter 使用我的CustomApplicationEntryPoint,我需要创建一个新的CustomBasicAuthenticationFilter 并将@Autowire 注释添加到构造函数中:

@Component
public class CustomBasicAuthenticationFilter extends BasicAuthenticationFilter {

    @Autowired
    public CustomBasicAuthenticationFilter(AuthenticationManager authenticationManager,
                                     AuthenticationEntryPoint authenticationEntryPoint) {
        super(authenticationManager, authenticationEntryPoint);
    }
}

然后将其添加到AuthorizationServerConfigurer的配置方法之一

@Override
public void configure(AuthorizationServerSecurityConfigurer authorizationServerSecurityConfigurer) throws Exception {
    authorizationServerSecurityConfigurer
            .authenticationEntryPoint(authenticationEntryPoint)
            .addTokenEndpointAuthenticationFilter(customBasicAuthenticationFilter);
}

现在应用程序使用我的CustomBasicAuthenticationFilter - 在功能上等同于BasicAuthenticationFilter。但是,它现在包括在构造期间声明的 AuthenticationEntryPoint bean - 这是我的 CustomAuthenticationEntryPoint

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2019-06-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-10-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多