2017-07-10 9 views
1

スプリングセキュリティoauthのエラーフォーマットはOAuth仕様に準拠しており、このようになります。スプリングセキュリティOauth - OAuth2Exceptionsのカスタムフォーマット

{ 
    "error":"insufficient_scope", 
    "error_description":"Insufficient scope for this resource", 
    "scope":"do.something" 
} 

特にリソースサーバー上では、認証の問題に対して別のエラー形式を取得するのが少し奇妙です。だから私はこの例外がレンダリングされる方法を変更したいと思います。

@ControllerAdvice 
@Order(Ordered.HIGHEST_PRECEDENCE) 
public class MyErrorHandler { 

    @ExceptionHandler(value = {InsufficientScopeException.class}) 
    ResponseEntity<MyErrorRepresentation> handle(RuntimeException ex, HttpServletRequest request) { 
     return errorResponse(HttpStatus.FORBIDDEN, 
       MyErrorRepresentation.builder() 
         .errorId("insufficient.scope") 
         .build(), 
       request); 
    } 
} 

しかし、これは動作しません:

documentation

Error handling in an Authorization Server uses standard Spring MVC features, namely @ExceptionHandler methods

は、だから私は、エラーのフォーマットをカスタマイズするために、このような何かを試してみましたと言います。

コードを見ると、すべてのエラーレンダリングはDefaultWebResponseExceptionTranslator#handleOAuth2Exceptionで行われているようです。しかし、カスタムを実装すると、フォーマットを変更することはできません。

ヒント

答えて

1

私は本当に私がこれを解決する助けの答えと似た質問見つかった - Handle spring security authentication exceptions with @ExceptionHandler

をしかし、私の質問は具体的には約spring-security-oauth2である - ので、私はそれはまだspring-security-oauth2に特定の答えを述べる価値があると思います。私の解決策は、上記の質問に対する異なる答えから選んだ。

私のサンプルはspring-security-oauth2 2.0.13

のために働くので、私は、リソースサーバリソース上のOAuth2エラーの異なるカスタムエラー構造を達成するための解決策は、私がResourceServerConfigurerAdapterを使用して登録したカスタムOAuth2AuthenticationEntryPointOAuth2AccessDeniedHandlerを登録することでした。 ResourceServerエンドポイントの形式を変更するだけで、TokenEndpointなどのAuthorizationServerエンドポイントは変更されないことに注意してください。

class MyCustomOauthErrorConversionConfigurerAdapter extends ResourceServerConfigurerAdapter { 

    @Override 
    public void configure(ResourceServerSecurityConfigurer configurer) throws Exception { 
    configurer.authenticationEntryPoint(new MyCustomOauthErrorOAuth2AuthenticationEntryPoint()); 
    configurer.accessDeniedHandler(new MyCustomOauthErrorOAuth2AccessDeniedHandler()); 
    } 
} 

関連するメソッドが例外を翻訳し、同じ方法でそれを洗い流すので、私はOAuth2AuthenticationEntryPointOAuth2AccessDeniedHandlerで機能を再利用することができませんでした。だから私はいくつかのコードをコピーする必要があった:

 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); 
     } 
    } 
} 
関連する問題