基於Oauth2的微服務介面:自定義資料和異常的資料格式
阿新 • • 發佈:2020-08-06
在基於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的微服務介面的統一資料和異常問題。