使用Spring Security在客户端身份验证上自定义OAuth2错误响应
虽然这似乎是一件容易的事,但事实却恰恰相反。我正在尝试自定义OAuth2客户端身份验证请求的错误处理。这样做的目的是从响应消息中删除异常堆栈跟踪/消息。
While this seems to be an easy task, it turns out the opposite. I'm trying to customize the error handling for OAuth2 client authentication requests. The purpose of this is to remove the exception stacktrace/message from the response message.
- vanilla Oauth2 Spring Security实现
- Java Spring配置
- 创建
OAuth2ExceptionRenderer的自定义实现
-
创建一个
@Bean
的实例OAuth2AuthenticationEntryPoint
@Bean
public OAuth2AuthenticationEntryPoint clientAuthEntryPoint()
{
OAuth2AuthenticationEntryPoint clientEntryPoint = new OAuth2AuthenticationEntryPoint();
clientEntryPoint.setTypeName("Basic");
clientEntryPoint.setRealmName("my-realm/client");
clientEntryPoint.setExceptionRenderer(new CustomOAuth2ExceptionRenderer());
return clientEntryPoint;
}
创建拒绝访问处理程序
Create an access denied handler
@Bean
public OAuth2AccessDeniedHandler accessDeniedHandler()
{
OAuth2AccessDeniedHandler adh = new OAuth2AccessDeniedHandler();
adh.setExceptionRenderer(new CustomOAuth2ExceptionRenderer());
return adh;
}
增加 AuthorizationServerSecurityConfigurer
,其中包括 AuthorizationServerConfiguration
Augment the AuthorizationServerSecurityConfigurer
, among others, with these specialized implementations in AuthorizationServerConfiguration
@Configuration
@EnableAuthorizationServer
protected static class AuthorizationServerConfiguration extends AuthorizationServerConfigurerAdapter
{
@Override
public void configure(AuthorizationServerSecurityConfigurer oauthServer) throws Exception
{
oauthServer.authenticationEntryPoint(clientAuthEntryPoint());
oauthServer.accessDeniedHandler(accessDeniedHandler());
oauthServer.realm("my-realm");
}
}
OAuth2请求
我们使用curl启动OAuth2 reuqests。以下是我们用来测试客户端身份验证的命令:
OAuth2 request
We use curl to initiate OAuth2 reuqests. Here is the command we use to test the client authenticaiton:
curl --insecure -H "Accept: application/json" -X POST -iu adfadsf:asdvadfgadf "https://localhost:8430/oauth/token?grant_type=password$username=john&pasword=johny"
观察到的行为
由于客户端身份验证是基本身份验证,因此Spring Security将分配 BasicAuthenticationFilter
到那一步。如果它在与此步骤相关的后端发生错误(例如SQL异常),则Spring Security将不会选择 OAuth2AuthenticationEntryPoint
并将退回到默认条目point BasicAuthenticationEntryPoint
。
Observed behavior
Since the client authentication is a Basic authentication, Spring Security will assign a BasicAuthenticationFilter
to that step. If it happens to have an error in the backend related to this step (e.g. SQL exception), Spring Security will not pick up the OAuth2AuthenticationEntryPoint
and will fall-back to a default entry point BasicAuthenticationEntryPoint
.
o.s.s.authentication.ProviderManager : Authentication attempt using org.springframework.security.authentication.dao.DaoAuthenticationProvider
o.s.s.w.a.www.BasicAuthenticationFilter : Authentication request for failed: org.springframework.security.authentication.InternalAuthenticationServiceException: show me the money
s.w.a.DelegatingAuthenticationEntryPoint : Trying to match using RequestHeaderRequestMatcher [expectedHeaderName=X-Requested-With, expectedHeaderValue=XMLHttpRequest]
s.w.a.DelegatingAuthenticationEntryPoint : No match found. Using default entry point org.springframework.security.web.authentication.www.BasicAuthenticationEntryPoint@649f92da
s.s.w.c.SecurityContextPersistenceFilter : SecurityContextHolder now cleared, as request processing completed```
您可以尝试使用Roman Wozniak在您的机票上发布的解决方案#483 。它对我来说非常好用:)
You could try the solution posted by Roman Wozniak on your ticket #483. It worked pretty well for me :)
-
Roman Wozniak的代码:
Code by Roman Wozniak:
@Configuration
@EnableAuthorizationServer
protected static class AuthorizationServerConfiguration extends AuthorizationServerConfigurerAdapter {
//...
@Override
public void configure(AuthorizationServerSecurityConfigurer oauthServer) throws Exception {
oauthServer
.realm(RESOURCE_ID + "/client")
.accessDeniedHandler(accessDeniedHandler)
.authenticationEntryPoint(entryPoint);
// This allows you to replace default filter for Basic authentication and customize error responses
oauthServer.addTokenEndpointAuthenticationFilter(
new BasicAuthenticationFilter(authenticationManager, entryPoint));
}
}