AuthenticationEntryPoint 仅有时被称为
AuthenticationEntryPoint only sometimes called
我有一个简单的 AuthenticationEntryPoint
,它应该为未经授权的请求设置 WWW-Authenticate header。
@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 header 或 Authorize header 值不以 'Basic' 开头时,它会被调用。但是,如果授权header以'Basic'开头,则不会调用开始方法(并且响应的值为Basic realm="oauth2/client"
)。如何确保调用此方法?
为了获得访问令牌,您应该通过 HTTP Basic:
验证您的客户端
Authorization: Basic Base64(client_id:client_secret)
This commence method is not always called, though. It gets called when
there is no Authorize header in the request or when the Authorize
header value doesn't start with 'Basic'. However, if the Authorize
header starts with 'Basic', the commence method is not called
Spring 安全部门在内部维护一个过滤器链,其中每个过滤器都有特定的职责,其中之一是 BasicAuthenticationFilter
which would process Basic Authentications. If you take a peek at its doFilterInteral
mthod, you would see:
if (header == null || !header.startsWith("Basic ")) {
chain.doFilter(request, response);
return;
}
如果您不传递 Authorization
header 或您的 Authorization
header 不是以 Basic
开头,它将跳过当前过滤器安全过滤器链中的其他过滤器。最终它会抛出一个 AuthenticationException
的实例,它会被 ExceptionTranslationFilter
捕获并且 ExceptionTranslationFilter
会调用你注册的 AuthenticationEntryPoint
.
但是当您通过基本授权 header 时,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;
}
正如 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
.
我有一个简单的 AuthenticationEntryPoint
,它应该为未经授权的请求设置 WWW-Authenticate header。
@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 header 或 Authorize header 值不以 'Basic' 开头时,它会被调用。但是,如果授权header以'Basic'开头,则不会调用开始方法(并且响应的值为Basic realm="oauth2/client"
)。如何确保调用此方法?
为了获得访问令牌,您应该通过 HTTP Basic:
验证您的客户端Authorization: Basic Base64(client_id:client_secret)
This commence method is not always called, though. It gets called when there is no Authorize header in the request or when the Authorize header value doesn't start with 'Basic'. However, if the Authorize header starts with 'Basic', the commence method is not called
Spring 安全部门在内部维护一个过滤器链,其中每个过滤器都有特定的职责,其中之一是 BasicAuthenticationFilter
which would process Basic Authentications. If you take a peek at its doFilterInteral
mthod, you would see:
if (header == null || !header.startsWith("Basic ")) {
chain.doFilter(request, response);
return;
}
如果您不传递 Authorization
header 或您的 Authorization
header 不是以 Basic
开头,它将跳过当前过滤器安全过滤器链中的其他过滤器。最终它会抛出一个 AuthenticationException
的实例,它会被 ExceptionTranslationFilter
捕获并且 ExceptionTranslationFilter
会调用你注册的 AuthenticationEntryPoint
.
但是当您通过基本授权 header 时,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;
}
正如 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
.