1. 程式人生 > 實用技巧 >基於Oauth2的微服務介面:自定義資料和異常的資料格式

基於Oauth2的微服務介面:自定義資料和異常的資料格式

在基於Oauth2的微服務專案中,往往希望有統一的資料返回,包括以下四種情況:

  • 介面正常資料的封裝
    {
        "code": 1,
        "message": "請求成功",
        "result": "介面資料"
    }
  • 介面異常資料的封裝
    {
        "code": 0,
        "message": "請求失敗",
        "result": "系統異常"
    } 
  • Oauth2資料的封裝
  • {
        "code": 1,
        "message": "請求成功",
        "result": {
            "access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE1OTY3MjEyNjUsInVzZXJfbmFtZSI6ImFkbWluIiwianRpIjoiYWE2NGM1YmYtYTdkYS00NWVkLTk1NjAtZWVhZDQ1YWViMTI0IiwiY2xpZW50X2lkIjoiZXNwLWNsaWVudCIsInNjb3BlIjpbImFsbCJdfQ.0godazU3OzjTOPKv46sNm3_UZa0kW8p0QynZhHpaTws",
            "refresh_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE1OTcyODI4NjUsInVzZXJfbmFtZSI6ImFkbWluIiwianRpIjoiZDE0MTA1NzAtM2E3MS00YzM1LWExOWEtMGQ5MDVhNGI5ZjBhIiwiY2xpZW50X2lkIjoiZXNwLWNsaWVudCIsInNjb3BlIjpbImFsbCJdLCJhdGkiOiJhYTY0YzViZi1hN2RhLTQ1ZWQtOTU2MC1lZWFkNDVhZWIxMjQifQ.LcEBds8WeQ8D2yX0VOqqNOp0J2_5ooQLKy4k7fmZsn0",
            "exp": "1596721265802"
        }
    }
  • Oauth2異常的封裝
    {
        "code": 0,
        "message": "請求失敗",
        "result": "無效token"
    }  

針對上述四種情況進行處理:

1.Oauth2異常的封裝:

   是四種情況中相對最為複雜的部分:分為資源伺服器的異常資訊處理和認證伺服器的異常資訊處理

   1.1認證伺服器的異常資訊處理(通常是獲取token時丟擲的異常)

    ①需要在AuthorizationServerConfigurerAdapter的AuthorizationServerEndpointsConfigurer進行配置

    endpoints.exceptionTranslator(customWebResponseExceptionTranslator)//自定義異常處理
    配置WebResponseExceptionTranslator自定義異常,並重寫translate方法返回自定義Oauth2認證異常資訊

    ② 由於在AuthorizationServerEndpointsConfigurer無法重寫客戶端配置資訊異常,需要進行另行處理

    需要在AuthorizationServerConfigurerAdapter的AuthorizationServerSecurityConfigurer進行配置

   CustomClientCredentialsTokenEndpointFilter endpointFilter = new CustomClientCredentialsTokenEndpointFilter(security);
   endpointFilter.afterPropertiesSet();
endpointFilter.setAuthenticationEntryPoint(customAuthenticationEntryPoint);
   security.addTokenEndpointAuthenticationFilter(endpointFilter);//自定義異常過濾器和客戶端端點過濾器
   配置ClientCredentialsTokenEndpointFilter自定義過濾器,並加入AuthenticationEntryPoint重寫commence方法,自定義返回Oauth2異常資訊。

 1.2資源伺服器的異常資訊處理(驗證token時丟擲的異常):包括認證異常和授權異常的配置

  在資源伺服器ResourceServerConfigurerAdapter的ResourceServerSecurityConfigurer中配置
   resources.authenticationEntryPoint(customAuthExceptionEntryPoint)//認證異常處理類
   resources.accessDeniedHandler(customAccessDeniedHandler)//許可權異常處理類

  配置認證異常處理AuthenticationEntryPoint,重寫commence方法,自定義Oauth2異常資訊。

  配置授權異常處理AccessDeniedHandler,重寫handle方法,自定義Oauth2異常資訊。

2.Oauth2資料的封裝

  使用aop重寫oauth2的TokenEndpoint.postAccessToken結果

//類註解@Component和@Aspect
@Around("execution(* org.springframework.security.oauth2.provider.endpoint.TokenEndpoint.postAccessToken(..))")
//獲取原有方法返回值為ResponseEntity<OAuth2AccessToken> responseEntity
//將responseEntity 中需要的資訊放入map中,返回
return ResponseEntity.status(HttpStatus.OK).body(map);
//這裡直接返回map,資料格式會在後續統一處理。
//同樣如果需要也可以通過aop重寫
CheckTokenEndpoint.checkToken

3.介面異常和資料的封裝 

//這裡為了方便將異常和資料放在一起進行處理(RestResponse為定義的統一資料類)

@ControllerAdvice
public class GlobalExceptionHandler implements ResponseBodyAdvice {
    private ObjectMapper objectMapper = new ObjectMapper();
   //統一異常處理
    @ExceptionHandler(value = Exception.class)
    @ResponseBody
    public RestResponse<Object> exceptionHandler(HttpServletRequest req, Exception e){
        return RestResponse.failure(e.getMessage());
    }

   //統一資料處理
    @Override
    public boolean supports(MethodParameter methodParameter, Class aClass) {
        return true;
    }
  
    @Override
    public Object beforeBodyWrite(Object o, MethodParameter methodParameter, MediaType mediaType, Class aClass, ServerHttpRequest serverHttpRequest, ServerHttpResponse serverHttpResponse) {
        //String型別返回會發生型別轉換異常,額外處理
        if (o instanceof String) {
            try {
                serverHttpResponse.getHeaders().set("Content-Type", "application/json;charset=utf-8");
                return objectMapper.writeValueAsString(RestResponse.success(o));
            } catch (JsonProcessingException e) {
                e.printStackTrace();
            }
        }else if(o instanceof RestResponse){
       //原有部分介面已經使用RestResponse包裝過,防止重複包裝。 return o; }
     //統一資料包裝,包括Oauth2的正常資料(Oauth2的異常資料已經直接),
Oauth2的異常資料已經直接httpServletResponse.getWriter().write()不需要處理
      return RestResponse.success(o);
   }
}

4.總結

整理總結在處理基於Oauth2的微服務介面的統一資料和異常問題。