关于Oauth2的微服务接口:正常数据和异常数据如何自定义统一的数据格式
在基于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的微服务接口中,正常数据和异常数据自定义统一数据格式的问题。