私は本当に私がこれを解決する助けの答えと似た質問見つかった - Handle spring security authentication exceptions with @ExceptionHandler
をしかし、私の質問は具体的には約spring-security-oauth2
である - ので、私はそれはまだspring-security-oauth2
に特定の答えを述べる価値があると思います。私の解決策は、上記の質問に対する異なる答えから選んだ。
私のサンプルはspring-security-oauth2 2.0.13
のために働くので、私は、リソースサーバリソース上のOAuth2エラーの異なるカスタムエラー構造を達成するための解決策は、私がResourceServerConfigurerAdapter
を使用して登録したカスタムOAuth2AuthenticationEntryPoint
とOAuth2AccessDeniedHandler
を登録することでした。 ResourceServerエンドポイントの形式を変更するだけで、TokenEndpointなどのAuthorizationServerエンドポイントは変更されないことに注意してください。
class MyCustomOauthErrorConversionConfigurerAdapter extends ResourceServerConfigurerAdapter {
@Override
public void configure(ResourceServerSecurityConfigurer configurer) throws Exception {
configurer.authenticationEntryPoint(new MyCustomOauthErrorOAuth2AuthenticationEntryPoint());
configurer.accessDeniedHandler(new MyCustomOauthErrorOAuth2AccessDeniedHandler());
}
}
関連するメソッドが例外を翻訳し、同じ方法でそれを洗い流すので、私はOAuth2AuthenticationEntryPoint
とOAuth2AccessDeniedHandler
で機能を再利用することができませんでした。だから私はいくつかのコードをコピーする必要があった:
public class MyCustomOauthErrorOAuth2AccessDeniedHandler extends OAuth2AccessDeniedHandler {
private final MyCustomOauthErrorOAuth2SecurityExceptionHandler oAuth2SecurityExceptionHandler = new MyCustomOauthErrorOAuth2SecurityExceptionHandler();
/**
* Does exactly what OAuth2AccessDeniedHandler does only that the body is transformed to {@link MyCustomOauthError} before rendering the exception
*/
@Override
public void handle(HttpServletRequest request, HttpServletResponse response, org.springframework.security.access.AccessDeniedException authException)
throws IOException, ServletException {
oAuth2SecurityExceptionHandler.handle(request, response, authException, this::enhanceResponse);
}
}
public class ExceptionMessageOAuth2AuthenticationEntryPoint extends OAuth2AuthenticationEntryPoint {
private final MyCustomOauthErrorOAuth2SecurityExceptionHandler oAuth2SecurityExceptionHandler = new MyCustomOauthErrorOAuth2SecurityExceptionHandler();
/**
* Does exactly what OAuth2AuthenticationEntryPoint does only that the body is transformed to {@link MyCustomOauthError} before rendering the exception
*/
@Override
public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException, ServletException {
oAuth2SecurityExceptionHandler.handle(request, response, authException, this::enhanceResponse);
}
}
@RequiredArgsConstructor
public class MyCustomOauthErrorOAuth2SecurityExceptionHandler {
private final WebResponseExceptionTranslator exceptionTranslator = new DefaultWebResponseExceptionTranslator();
private final OAuth2ExceptionRenderer exceptionRenderer = new DefaultOAuth2ExceptionRenderer();
private final HandlerExceptionResolver handlerExceptionResolver = new DefaultHandlerExceptionResolver();
/**
* This is basically what {@link org.springframework.security.oauth2.provider.error.AbstractOAuth2SecurityExceptionHandler#doHandle(HttpServletRequest, HttpServletResponse, Exception)} does.
*/
public void handle(HttpServletRequest request, HttpServletResponse response, RuntimeException authException,
BiFunction<ResponseEntity<OAuth2Exception>, Exception, ResponseEntity<OAuth2Exception>> oauthExceptionEnhancer)
throws IOException, ServletException {
try {
ResponseEntity<OAuth2Exception> defaultErrorResponse = exceptionTranslator.translate(authException);
defaultErrorResponse = oauthExceptionEnhancer.apply(defaultErrorResponse, authException);
//this is the actual translation of the error
final MyCustomOauthError customErrorPayload =
MyCustomOauthError.builder()
.errorId(defaultErrorResponse.getBody().getOAuth2ErrorCode())
.message(defaultErrorResponse.getBody().getMessage())
.details(defaultErrorResponse.getBody().getAdditionalInformation() == null ? emptyMap() : defaultErrorResponse.getBody().getAdditionalInformation())
.build();
final ResponseEntity<MyCustomOauthError> responseEntity = new ResponseEntity<>(customErrorPayload, defaultErrorResponse.getHeaders(), defaultErrorResponse.getStatusCode());
exceptionRenderer.handleHttpEntityResponse(responseEntity, new ServletWebRequest(request, response));
response.flushBuffer();
} catch (ServletException e) {
// Re-use some of the default Spring dispatcher behaviour - the exception came from the filter chain and
// not from an MVC handler so it won't be caught by the dispatcher (even if there is one)
if (handlerExceptionResolver.resolveException(request, response, this, e) == null) {
throw e;
}
} catch (IOException | RuntimeException e) {
throw e;
} catch (Exception e) {
// Wrap other Exceptions. These are not expected to happen
throw new RuntimeException(e);
}
}
}