カスタムResponseEntityExceptionHandler
またはOAuth2ExceptionRenderer
を取得して、純粋なリソースサーバーでSpringセキュリティによって発生した例外を処理するにはどうすればよいですか?
実装しました
@ControllerAdvice
@RestController
public class GlobalExceptionHandler extends ResponseEntityExceptionHandler {
そのため、リソースサーバーでエラーが発生するたびに、応答するようにします。
{
"message": "...",
"type": "...",
"status": 400
}
リソースサーバーはapplication.properties設定を使用します。
security.oauth2.resource.userInfoUri: http://localhost:9999/auth/user
認証サーバーに対してリクエストを認証および承認します。
ただし、春のセキュリティエラーは常に例外ハンドラをバイパスします
@ExceptionHandler(InvalidTokenException.class)
public ResponseEntity<Map<String, Object>> handleInvalidTokenException(InvalidTokenException e) {
return createErrorResponseAndLog(e, 401);
}
そしてどちらかを生産する
{
"timestamp": "2016-12-14T10:40:34.122Z",
"status": 403,
"error": "Forbidden",
"message": "Access Denied",
"path": "/api/templates/585004226f793042a094d3a9/schema"
}
または
{
"error": "invalid_token",
"error_description": "5d7e4ab5-4a88-4571-b4a4-042bce0a076b"
}
リソースサーバーのセキュリティ例外処理を構成するにはどうすればよいですか?私が見つけたのは、カスタムOAuth2ExceptionRenderer
を実装して認証サーバーをカスタマイズする方法の例です。しかし、これをリソースサーバーのセキュリティチェーンに接続する場所を見つけることができません。
唯一の構成/セットアップは次のとおりです。
@SpringBootApplication
@Configuration
@ComponentScan(basePackages = {"our.packages"})
@EnableAutoConfiguration
@EnableResourceServer
前のコメントで述べたように、リクエストはMVC層に到達する前にセキュリティフレームワークによって拒否されるため、@ControllerAdvice
はここではオプションではありません。
Spring Securityフレームワークには、ここで興味深い3つのインターフェイスがあります。
これらの各インターフェイスの実装を作成して、ログインの成功、ログインの失敗、不十分な権限で保護されたリソースへのアクセスの試行など、さまざまなイベントに対して送信される応答をカスタマイズできます。
以下は、ログイン試行の失敗時にJSON応答を返します。
@Component
public class RestAuthenticationFailureHandler implements AuthenticationFailureHandler
{
@Override
public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response,
AuthenticationException ex) throws IOException, ServletException
{
response.setStatus(HttpStatus.FORBIDDEN.value());
Map<String, Object> data = new HashMap<>();
data.put("timestamp", new Date());
data.put("status",HttpStatus.FORBIDDEN.value());
data.put("message", "Access Denied");
data.put("path", request.getRequestURL().toString());
OutputStream out = response.getOutputStream();
com.fasterxml.jackson.databind.ObjectMapper mapper = new ObjectMapper();
mapper.writeValue(out, data);
out.flush();
}
}
また、実装をセキュリティフレームワークに登録する必要があります。 Java configでは、これは以下のようになります。
@Configuration
@EnableWebSecurity
@ComponentScan("...")
public class SecurityConfiguration extends WebSecurityConfigurerAdapter
{
@Override
public void configure(HttpSecurity http) throws Exception
{
http.addFilterBefore(corsFilter(), ChannelProcessingFilter.class).logout().deleteCookies("JESSIONID")
.logoutUrl("/api/logout").logoutSuccessHandler(logoutSuccessHandler()).and().formLogin().loginPage("/login")
.loginProcessingUrl("/api/login").failureHandler(authenticationFailureHandler())
.successHandler(authenticationSuccessHandler()).and().csrf().disable().exceptionHandling()
.authenticationEntryPoint(authenticationEntryPoint()).accessDeniedHandler(accessDeniedHandler());
}
/**
* @return Custom {@link AuthenticationFailureHandler} to send suitable response to REST clients in the event of a
* failed authentication attempt.
*/
@Bean
public AuthenticationFailureHandler authenticationFailureHandler()
{
return new RestAuthenticationFailureHandler();
}
/**
* @return Custom {@link AuthenticationSuccessHandler} to send suitable response to REST clients in the event of a
* successful authentication attempt.
*/
@Bean
public AuthenticationSuccessHandler authenticationSuccessHandler()
{
return new RestAuthenticationSuccessHandler();
}
/**
* @return Custom {@link AccessDeniedHandler} to send suitable response to REST clients in the event of an attempt to
* access resources to which the user has insufficient privileges.
*/
@Bean
public AccessDeniedHandler accessDeniedHandler()
{
return new RestAccessDeniedHandler();
}
}
_@EnableResourceServer
_を使用している場合、_@Configuration
_クラスでResourceServerConfigurerAdapter
の代わりにWebSecurityConfigurerAdapter
を拡張すると便利な場合があります。これを行うことで、configure(ResourceServerSecurityConfigurer resources)
をオーバーライドし、メソッド内でresources.authenticationEntryPoint(customAuthEntryPoint())
を使用することで、単純にカスタムAuthenticationEntryPoint
を登録できます。
このようなもの:
_@Configuration
@EnableResourceServer
public class CommonSecurityConfig extends ResourceServerConfigurerAdapter {
@Override
public void configure(ResourceServerSecurityConfigurer resources) throws Exception {
resources.authenticationEntryPoint(customAuthEntryPoint());
}
@Bean
public AuthenticationEntryPoint customAuthEntryPoint(){
return new AuthFailureHandler();
}
}
_
また、(最終ではないので)拡張し、カスタムAuthenticationEntryPoint
の実装中に部分的に再利用できるNice _OAuth2AuthenticationEntryPoint
_もあります。特に、エラー関連の詳細を含む「WWW-Authenticate」ヘッダーを追加します。
スプリングセキュリティフィルターは、Spring MVCよりもずっと前に起動するため、@ControllerAdvice
などのSpring MVC例外ハンドラーアノテーションを使用することはできません。
Spring Security Oauth2でRemoteTokenServicesを使用してリソースサーバーを構成する に類似した構成でトークン検証URLを使用している場合、無許可の場合にHTTPステータス401を返します。
@Primary
@Bean
public RemoteTokenServices tokenService() {
RemoteTokenServices tokenService = new RemoteTokenServices();
tokenService.setCheckTokenEndpointUrl("https://token-validation-url.com");
tokenService.setTokenName("token");
return tokenService;
}
他の回答( https://stackoverflow.com/a/44372313/5962766 )で説明されているカスタムauthenticationEntryPoint
の実装は、 RemoteTokenService が400を使用するため機能しません401のような他のステータスに対して未処理の例外をスローします。
public RemoteTokenServices() {
restTemplate = new RestTemplate();
((RestTemplate) restTemplate).setErrorHandler(new DefaultResponseErrorHandler() {
@Override
// Ignore 400
public void handleError(ClientHttpResponse response) throws IOException {
if (response.getRawStatusCode() != 400) {
super.handleError(response);
}
}
});
}
したがって、例外をスローせずに401を処理するRestTemplate
configでカスタムRemoteTokenServices
を設定する必要があります。
@Primary
@Bean
public RemoteTokenServices tokenService() {
RemoteTokenServices tokenService = new RemoteTokenServices();
tokenService.setCheckTokenEndpointUrl("https://token-validation-url.com");
tokenService.setTokenName("token");
RestOperations restTemplate = new RestTemplate();
restTemplate.setRequestFactory(new HttpComponentsClientHttpRequestFactory());
((RestTemplate) restTemplate).setErrorHandler(new DefaultResponseErrorHandler() {
@Override
// Ignore 400 and 401
public void handleError(ClientHttpResponse response) throws IOException {
if (response.getRawStatusCode() != 400 && response.getRawStatusCode() != 401) {
super.handleError(response);
}
}
});
}
tokenService.setRestTemplate(restTemplate);
return tokenService;
}
HttpComponentsClientHttpRequestFactory の依存関係を追加します。
<dependency>
<groupId>org.Apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
</dependency>
OAuth2ExceptionRendererは、承認サーバー用です。正しい答えは、この投稿で詳しく説明されているように処理する可能性があります(つまり、oauthであることを無視し、他のスプリングセキュリティ認証メカニズムと同様に扱います): https:// stackoverflow.com/a/26502321/5639571
もちろん、これはoauth関連する例外(リソースエンドポイントに到達する前にスローされる))をキャッチしますが、リソースエンドポイント内で発生する例外には@ExceptionHandlerメソッドが必要です。