在基於Oauth2的微服務項目中,往往希望有統一格式的數據返回,包括以下四種情況:
- Oauth2異常數據的封裝
{ "code": 0, "message": "請求失敗", "result": "無效token" }
- Oauth2正常數據的封裝
{ "code": 1, "message": "請求成功", "result": { "access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9", "refresh_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9", "exp": "1596721265802" } }
- 接口異常數據的封裝
{ "code": 0, "message": "請求失敗", "result": "異常信息" }
- 接口正常數據的封裝
{ "code": 1, "message": "請求成功", "result": "接口數據" }
針對上述四種情況進行處理:
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的微服務接口中,正常數據和異常數據自定義統一數據格式的問題。