2017-07-10 84 views
1

spring security oauth的錯誤格式符合OAuth規範,並且看起來像這樣。Spring Security Oauth - OAuth2Exceptions的自定義格式

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

尤其是在資源服務器上,我發現爲認證問題獲得不同的錯誤格式有點奇怪。所以我想改變這個異常的呈現方式。

documentation

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

所以,我想是這樣的自定義錯誤的格式:

@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); 
    } 
} 

但是,這是行不通的。

看着代碼,所有的錯誤渲染似乎都在DefaultWebResponseExceptionTranslator#handleOAuth2Exception中完成。但是實施自定義不允許更改格式。

任何提示?

回答

1

我發現了一個類似的問題的答案,真正幫助我解決這個 - Handle spring security authentication exceptions with @ExceptionHandler

但我的問題是,特別是約spring-security-oauth2 - 所以我覺得還是值得說明具體到spring-security-oauth2答案。我的解決方案是從上述問題的不同答案中挑選出來的。

我的示例對spring-security-oauth2 2.0.13

所以對我來說,以實現對資源服務器資源的oauth2誤差不同的自定義錯誤結構的解決方案是註冊自定義OAuth2AuthenticationEntryPointOAuth2AccessDeniedHandler,我註冊使用ResourceServerConfigurerAdapter。值得一提的是,這只是改變了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); 
     } 
    } 
} 
相關問題