前面我们使用 Spring Security+OAuth2做认证授权时,默认返回都是提供好的默认返回格式,返回结果不是很友好,大体如下:

{"error": "invalid_client","error_description": "Bad client credentials"
}

针对这些异常,我们自定义返回格式内容,做异常统一返回格式处理。

首先结果集基类格式如下:

public class BaseResult implements Serializable {private static final long serialVersionUID = 1L;/*** 是否成功*/protected boolean success;/*** 错误信息*/protected String message;/*** 详细信息*/protected String detailMessage;/*** 返回数据*/protected Object data;
...getter/setter
}

一、资源服务异常处理

资源服异常场景,一般就是分为 token异常(未携带token,token解析失败,token过期或者无效token)和权限不足异常。

OAuth2AuthenticationProcessingFilter 是 OAuth2受保护资源的身份验证过滤器。在它的 doFilter方法中我们大致了解其抛出的异常类型。所以这个处理起来还是比较简单的。

1、token异常

创建 MyExtendAuthenticationEntryPointHandler类实现 OAuth2AuthenticationEntryPoint接口。

@Component
public class MyExtendAuthenticationEntryPointHandler extends OAuth2AuthenticationEntryPoint {private static final Logger log = LoggerFactory.getLogger(MyExtendAuthenticationEntryPointHandler.class);@Overridepublic void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException {Throwable cause = authException.getCause();//自定义返回格式内容BaseResult baseResult = new BaseResult();baseResult.setSuccess(false);baseResult.setDetailMessage(authException.getMessage());baseResult.setMessage(authException.getMessage());if (cause instanceof OAuth2AccessDeniedException) {baseResult.setMessage("资源ID不在resource_ids范围内");}  else if (cause instanceof InvalidTokenException) {baseResult.setMessage("Token解析失败");}else if (authException instanceof InsufficientAuthenticationException) {baseResult.setMessage("未携带token");}else{baseResult.setMessage("未知异常信息");}response.setStatus(HttpStatus.OK.value());response.setCharacterEncoding("utf-8");response.setHeader("Content-Type", "application/json;charset=UTF-8");PrintWriter printWriter = response.getWriter();printWriter.append(new ObjectMapper().writeValueAsString(baseResult));}}

2、权限不足异常

创建 MyExtendAccessDeniedHandler类实现 AccessDeniedHandler接口。

@Component
public class MyExtendAccessDeniedHandler implements AccessDeniedHandler {private static final Logger log = LoggerFactory.getLogger(MyExtendAccessDeniedHandler.class);@Overridepublic void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException accessDeniedException) throws IOException, ServletException {// 自定义返回格式内容BaseResult baseResult = new BaseResult();baseResult.setSuccess(false);baseResult.setMessage("认证过的用户访问无权限资源时的异常");baseResult.setDetailMessage(accessDeniedException.getMessage());response.setStatus(HttpStatus.OK.value());response.setCharacterEncoding("utf-8");response.setHeader("Content-Type", "application/json;charset=UTF-8");response.setStatus(HttpStatus.FORBIDDEN.value());// 权限不足403response.getWriter().write(new ObjectMapper().writeValueAsString(baseResult));}
}

3、在资源配置类中配置处理器

在资源配置类中配置自定义的处理器。

    @Overridepublic void configure(ResourceServerSecurityConfigurer resources) throws Exception {resources.resourceId("order_source_api")//资源ID.tokenStore(jdbcTokenStore())//令牌策略.authenticationEntryPoint(myExtendAuthenticationEntryPointHandler)//token异常类重写.accessDeniedHandler(myExtendAccessDeniedHandler)//权限不足异常类重写;}

重启资源服务,自定义响应结果ok。

二、认证服务异常处理

在 AuthorizationServerEndpointsConfigurer端点配置类有一个 WebResponseExceptionTranslator异常翻译器

DefaultWebResponseExceptionTranslator类是 WebResponseExceptionTranslator的唯一默认实现类。

所以,创建 MyExtendAuth2ResponseExceptionTranslator类实现 WebResponseExceptionTranslator接口。
然后重写 translate方法,translate方法就是根据不同的异常栈返回不同的异常对象。可以参考 DefaultWebResponseExceptionTranslator类。

1、创建 MyExtendAuth2ResponseExceptionTranslator类

@Component
public class MyExtendOAuth2ResponseExceptionTranslator implements WebResponseExceptionTranslator<OAuth2Exception> {private ThrowableAnalyzer throwableAnalyzer = new DefaultThrowableAnalyzer();@Overridepublic ResponseEntity<OAuth2Exception> translate(Exception e) throws Exception {// Try to extract a SpringSecurityException from the stacktraceThrowable[] causeChain = throwableAnalyzer.determineCauseChain(e);// 异常栈获取 OAuth2Exception 异常Exception ase = (OAuth2Exception) throwableAnalyzer.getFirstThrowableOfType(OAuth2Exception.class, causeChain);if (ase != null) {return handleOAuth2Exception((OAuth2Exception) ase);}// 否则服务器内部错误return handleOAuth2Exception(new ServerErrorException(HttpStatus.INTERNAL_SERVER_ERROR.getReasonPhrase(), e));}private ResponseEntity<OAuth2Exception> handleOAuth2Exception(OAuth2Exception e) throws IOException {int status = e.getHttpErrorCode();HttpHeaders headers = new HttpHeaders();headers.set("Cache-Control", "no-store");headers.set("Pragma", "no-cache");if (status == HttpStatus.UNAUTHORIZED.value() || (e instanceof InsufficientScopeException)) {headers.set("WWW-Authenticate", String.format("%s %s", OAuth2AccessToken.BEARER_TYPE, e.getSummary()));}// 使用自定义扩展OAuth2Exceptione = new MyExtendOAuth2Exception(e.getMessage(), e.getOAuth2ErrorCode(), e);ResponseEntity<OAuth2Exception> response = new ResponseEntity<OAuth2Exception>(e, headers, HttpStatus.valueOf(status));return response;}}

translate方法这里简单点,除了 OAuth2Exception异常就是自定义的 ServerErrorException异常。
handleOAuth2Exception方法是,将 OAuth2Exception异常包装成 自定义的 MyExtendOAuth2Exception异常。

其实我们可以根据不同的异常栈返回不同的异常对象,做不同的异常信息提示,我这里就全部放到 MyExtendOAuth2Exception类中处理,主要把 OAuth2Exception异常信息添加对应的中文信息返回。

1.1 MyExtendOAuth2Exception类

@JsonSerialize(using = MyExtendOAuth2ExceptionSerializer.class)
public class MyExtendOAuth2Exception extends OAuth2Exception {private static final Logger log = LoggerFactory.getLogger(MyExtendOAuth2Exception.class);/*** OAuth2Exception*/public static final String ERROR = "error";public static final String DESCRIPTION = "error_description";public static final String URI = "error_uri";public static final String INVALID_REQUEST = "invalid_request";public static final String INVALID_CLIENT = "invalid_client";public static final String INVALID_GRANT = "invalid_grant";public static final String UNAUTHORIZED_CLIENT = "unauthorized_client";public static final String UNSUPPORTED_GRANT_TYPE = "unsupported_grant_type";public static final String INVALID_SCOPE = "invalid_scope";public static final String INSUFFICIENT_SCOPE = "insufficient_scope";public static final String INVALID_TOKEN = "invalid_token";public static final String REDIRECT_URI_MISMATCH ="redirect_uri_mismatch";public static final String UNSUPPORTED_RESPONSE_TYPE ="unsupported_response_type";public static final String ACCESS_DENIED = "access_denied";/*** 其他异常*/public static final String METHOD_NOT_ALLOWED = "method_not_allowed";public static final String SERVER_ERROR = "server_error";public static final String UNAUTHORIZED = "unauthorized";private static ConcurrentHashMap<String, String> oAuth2ErrorMap = new ConcurrentHashMap<>();/*** 映射了一部分*/static {oAuth2ErrorMap.put(INVALID_CLIENT,"无效的客户端");oAuth2ErrorMap.put(INVALID_GRANT,"无效的授权模式");oAuth2ErrorMap.put(INVALID_SCOPE,"权限不足");oAuth2ErrorMap.put(UNSUPPORTED_GRANT_TYPE,"不支持的授权模式类型");oAuth2ErrorMap.put(ACCESS_DENIED,"拒绝访问");oAuth2ErrorMap.put(METHOD_NOT_ALLOWED,"方法不允许访问");oAuth2ErrorMap.put(SERVER_ERROR,"服务器内部异常");oAuth2ErrorMap.put(UNAUTHORIZED,"未授权");}/*** oAuth2ErrorCode的扩展信息*/private String myExtendMessage;public MyExtendOAuth2Exception(String msg, Throwable t) {super(msg, t);}public MyExtendOAuth2Exception(String msg, String oAuth2ErrorCode, Throwable t) {super(msg, t);log.info("自定义扩展OAuth2异常处理 MyExtendOAuth2Exception.class -> msg={},oAuth2ErrorCode={}", msg, oAuth2ErrorCode);String oAuth2ErrorMessage = oAuth2ErrorMap.get(oAuth2ErrorCode);this.myExtendMessage = oAuth2ErrorMessage != null ? oAuth2ErrorMessage : "未知异常:" + oAuth2ErrorCode;}public String getMyExtendMessage() {return myExtendMessage;}public void setMyExtendMessage(String myExtendMessage) {this.myExtendMessage = myExtendMessage;}
}

1.2 ServerErrorException类

public class ServerErrorException extends MyExtendOAuth2Exception {public ServerErrorException(String msg, Throwable t) {super(msg, t);}@Overridepublic String getOAuth2ErrorCode() {return "server_error";}@Overridepublic int getHttpErrorCode() {return HttpStatus.INTERNAL_SERVER_ERROR.value();}
}

有了这些还不够,还需要异常序列化器,源码是最好的导师,可参考 OAuth2Exception类

2、创建 MyExtendOAuth2ExceptionSerializer类

创建 MyExtendOAuth2ExceptionSerializer类继承 StdSerializer类。

@Component
public class MyExtendOAuth2ExceptionSerializer extends StdSerializer<MyExtendOAuth2Exception> {private static final Logger log = LoggerFactory.getLogger(MyExtendOAuth2ExceptionSerializer.class);public MyExtendOAuth2ExceptionSerializer() {super(MyExtendOAuth2Exception.class);}@Overridepublic void serialize(MyExtendOAuth2Exception e, JsonGenerator jGen, SerializerProvider provider) throws IOException {log.info("MyCustomOAuth2Exception返回格式序列化处理,MyExtendOAuth2ExceptionSerializer.class -> e={}", e);// 自定义返回格式内容BaseResult baseResult = new BaseResult();baseResult.setSuccess(false);baseResult.setMessage(e.getMyExtendMessage());baseResult.setDetailMessage(e.getMessage());jGen.writeObject(baseResult);}
}

3、在OAuth2配置类中配置异常翻译器

在 OAuth2配置类中配置自定义的异常翻译器。

    /*** OAuth2的主配置信息,将上面所有配置都整合注册进来* @param endpoints* @throws Exception*/@Overridepublic void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {endpoints.userDetailsService(userService) // 刷新 token, 不配置的话,刷新token是使用不了的.approvalStore(approvalStore()).authenticationManager(authenticationManager) //认证管理器.authorizationCodeServices(authorizationCodeServices()) //授权码服务.tokenStore(tokenStore()) //令牌管理服务.exceptionTranslator(new MyExtendOAuth2ResponseExceptionTranslator()) // 设置自定义的异常解析器;}

重启服务,访问,我发现部分异常走了我们自定义的异常解析器,然后调用自定义的异常序列化器。

部分异常走的还是 DefaultWebResponseExceptionTranslator吗默认的异常解析器,然后调用OAuth2ExceptionJackson2Serializer异常序列化器。

对此,认证服务这边还没有做到完全的自定义统一异常处理,后面再研究研究。

– 求知若饥,虚心若愚。

Spring Security+OAuth2自定义异常处理相关推荐

  1. Spring Security OAuth2——自定义OAuth2第三方登录(Gitee)并与UsernamePassword登录关联解决方案

    前文:Spring Security OAuth2--自定义OAuth2第三方登录(Gitee) Maven 主要 <!--Spring Security--><dependency ...

  2. Spring Security OAuth2——自定义OAuth2第三方登录(Gitee)

    官方文档 https://docs.spring.io/spring-security/site/docs/current/reference/html5/#oauth2login-custom-pr ...

  3. spring security oauth2 基于 RBAC 的自定义认证

    基于 RBAC 的自定义认证 概述 在实际开发中,我们的用户信息都是存在数据库里的,本章节基于 RBAC 模型 将用户的认证信息与数据库对接,实现真正的用户认证与授权 操作流程 继续 基于 JDBC ...

  4. Spring Security Oauth2 授权码模式下 自定义登录、授权页面

    主要说明:基于若依springcloud微服务框架的2.1版本 嫌弃缩进不舒服的,直接访问我的博客站点: http://binarydance.top//aticle_view.html?aticle ...

  5. Spring Security OAuth2 微服务认证中心自定义授权模式扩展以及常见登录认证场景下的应用实战

    本文源码地址 后端:https://gitee.com/youlaitech/youlai-mall/tree/v2.0.1 前端:https://gitee.com/youlaiorg/mall-a ...

  6. 【Spring Cloud Alibaba 实战 | 总结篇】Spring Cloud Gateway + Spring Security OAuth2 + JWT 实现微服务统一认证授权和鉴权

    一. 前言 hi,大家好~ 好久没更文了,期间主要致力于项目的功能升级和问题修复中,经过一年时间这里只贴出关键部分代码的打磨,[有来]终于迎来v2.0版本,相较于v1.x版本主要完善了OAuth2认证 ...

  7. 框架使用SpringBoot + Spring Security Oauth2 +PostMan

    框架使用SpringBoot + Spring Security Oauth2  主要完成了客户端授权  可以通过mysql数据库读取当前客户端表信息进行验证,token存储在数据库中 1.引入依赖 ...

  8. Spring Security OAuth2整合JWT

    文章目录 1. Spring Security 与 OAuth2 2. Spring Security OAuth2的配置和使用 ①:引入依赖 ②:配置 spring security ③:配置授权服 ...

  9. Spring Security OAuth2 授权失败(401)

    Spring Cloud架构中采用Spring Security OAuth2作为权限控制,关于OAuth2详细介绍可以参考 http://www.ruanyifeng.com/blog/2014/0 ...

最新文章

  1. 利用Event和MapFile进程共享信息
  2. android换肤动画,Android-换肤ThemeSkinning使用
  3. STM32 ADC 同步规则模式 ADC1与ADC2同用一个DMA
  4. Python关于装饰器的练习题
  5. NullReferenceException C#中的异常
  6. WSSv3和SharePoint2007安装指南
  7. Linux sed 替换第一次出现的字符串
  8. Activity生命周期方法的调用顺序project与測试日志
  9. random_state 参数
  10. 企业信息化基本指标构成方案(试行)上
  11. hdfs命令,hadoop基本常用命令
  12. Linux命令—vi命令详解
  13. 含重根的三阶实对称矩阵的快速对角化方法
  14. python样条插值(二)
  15. 为什么说甲骨文裁员也属无奈之举?
  16. 抖音热门技术998 修改视频md5
  17. Mark 韦氏拼音 邮政式拼音 和汉语拼音
  18. java put方式提交_java – 通过HTTP PUT请求上传文件
  19. 爬虫取中间文本_【实战No.2】1小时打造你自己的网络爬虫
  20. html语言加号点一下变成减号6,CSS3 linear-gradient线性渐变生成加号和减号的方法...

热门文章

  1. 秒杀系统架构设计与实现
  2. VSCode官方插件-Egret Coder 解决了文件代码编辑、调用命令、断点调试等问题
  3. Dvwa_XSS (Stored)
  4. Spring框架四AOP
  5. for语句嵌套执行顺序_C语言笔记 | for语句嵌套时的运行顺序
  6. 自动转flash为html5,Safari插件推荐:自动转Flash为Html5 _11684苹果网MAC资讯
  7. kafka 问题 (localhost/127.0.0.1:9092) could not be established. Broker may not be availab
  8. Java实现Excel多表头动态数据导出
  9. Qt中的控件随着窗口大小改变而改变和充满窗口
  10. 作为一名程序员,如何在周末快乐的学习?