大家好,我是不才陈某~

这是《Spring Security 进阶》的第4篇文章,往期文章如下:

文章都是成体系的,陈某默认看到这篇文章的读者都已经看了前期的文章,之前的知识点就不再详细介绍了。

今天这篇文章主要介绍一下实际工作中使用Spring Security需要定制的一些异常信息。

文章目录如下:

案例服务搭建

此篇文章沿用上篇文章的认证、资源服务,如下:

1、认证服务oauth2-auth-server-jwt

2、资源服务oauth2-auth-resource-jwt

案例源码已经上传GitHub,关注公号:码猿技术专栏,回复关键词:9529 获取!

认证服务的异常

先来看一下正确的获取令牌的请求,以密码模式为例,如下图:

密码模式需要传递5个参数,分别是用户名密码客户端id客户端秘钥授权类型

那么问题来了:如果任意一个参数传错了,返回什么?

1、用户名、密码错误

故意输错用户名或者密码,返回信息如下:

2、授权类型错误

输入一个不存在的授权类型,返回信息如下:

3、客户端ID,秘钥错误

输入错误的客户端id或者秘钥,返回信息如下:

感觉如何?是不是很酸爽?很显然这返回的信息不适合前后端交互,别着急,下面介绍解决方案

认证服务自定义异常信息

上面列举了三种常见的异常,解决方案实际可以分为两种:

  • 用户名,密码错误异常、授权类型异常
  • 客户端ID、秘钥异常

陈某这里针对这两种异常先上解决方案,后面再从源码解释为什么这么做?

1、用户名,密码错误异常、授权类型异常

针对用户名、密码、授权类型错误的异常解决方式比较复杂,需要定制的比较多。

1、定制提示信息、响应码

这部分根据自己业务需要定制,陈某这里只是给出个例子,代码如下:

2、自定义WebResponseExceptionTranslator

需要自定义一个异常翻译器,默认的是DefaultWebResponseExceptionTranslator,此处必须重写,其中有一个需要实现的方法,如下:

ResponseEntity<T> translate(Exception e) throws Exception;

这个方法就是根据传递过来的Exception判断不同的异常返回特定的信息,这里需要判断的异常的如下:

  • UnsupportedGrantTypeException:不支持的授权类型异常
  • InvalidGrantException:用户名或者密码错误的异常

创建一个OAuthServerWebResponseExceptionTranslator实现WebResponseExceptionTranslator,代码如下:

3、认证服务配置文件中配置

需要将自定义的异常翻译器OAuthServerWebResponseExceptionTranslator在配置文件中配置,很简单,一行代码的事。

AuthorizationServerConfig配置文件指定,代码如下:

案例源码已经上传GitHub,关注公号:码猿技术专栏,回复关键词:9529 获取!

4、测试

按照上述的配置完成后,测试下用户名、密码错误、授权类型错误是否能够正确返回定制的提示信息,如下:

5、源码追踪

实践有了,总该理解一下为什么这么做吧?下面从源码的角度告诉你为什么要这么做?

我们知道获取令牌的接口为 /oauth/token,这个接口定义在TokenEndpoint#postAccessToken()(POST请求)方法中,如下图:

我们先不看其中的逻辑,平时我们写接口的异常怎么处理?

一般都是通过 @ExceptionHandler 特定的异常,然后统一的进行异常处理,是不是这样?

然后看一下上述的两种异常属于什么类型的,如下:

是不是都继承了OAuth2Exception,那么尝试在TokenEndpoint这个类中找找有没有处理OAuth2Exception这个异常的处理器,果然找到了一个 handleException() 方法,如下:

可以看到,这里的异常翻译器已经使用了我们自定义的OAuthServerWebResponseExceptionTranslator。可以看下默认的异常翻译器是啥,代码如下:

看到没,就是这个DefaultWebResponseExceptionTranslator

问题又来了:为什么在配置文件中设置了OAuthServerWebResponseExceptionTranslator就会生效呢?

这个不得不看下 @EnableAuthorizationServer 这个注解了,源码如下:

注入了这个AuthorizationServerEndpointsConfiguration配置类,其中注入了AuthorizationEndpoint这个bean,如下:

将自定义的异常翻译器设置进入了AbstractEndpoint这个抽象类中,而TokenEndpoint正是继承了这个抽象类,复用了其中的异常翻译器,代码如下:

哦了,问题解决了,学东西一定要知其所以然...............

2、客户端ID、秘钥异常

这部分比较复杂,想要理解还是需要些基础的,解决这个异常的方案很多,陈某只是介绍其中一种,下面详细介绍。

1、定制提示信息、响应码

这部分根据自己业务需要定制,陈某这里只是给出个例子,代码如下:

2、自定义AuthenticationEntryPoint

这个AuthenticationEntryPoint是不是很熟悉,前面的文章已经介绍过了,此处需要自定义来返回定制的提示信息。

创建OAuthServerAuthenticationEntryPoint,实现AuthenticationEntryPoint,重写其中的方法,代码如下:

3、改造ClientCredentialsTokenEndpointFilter

ClientCredentialsTokenEndpointFilter这个过滤器的主要作用就是校验客户端的ID、秘钥,代码如下:

有几个重要的部分需要讲一下,如下:

  • 构造方法中需要传入第2步自定义的 OAuthServerAuthenticationEntryPoint
  • 重写 getAuthenticationManager() 方法返回IOC中的AuthenticationManager
  • 重写afterPropertiesSet() 方法,用于自定义认证失败、成功处理器,失败处理器中调用OAuthServerAuthenticationEntryPoint进行异常提示信息返回

4、OAuth配置文件中指定过滤器

只需要将自定义的过滤器添加到AuthorizationServerSecurityConfigurer中,代码如下:

部分是添加过滤器,其中authenticationEntryPoint使用的是第2步自定义的OAuthServerAuthenticationEntryPoint

部分一定要注意:一定要去掉这行代码,具体原因源码解释。

案例源码已经上传GitHub,关注公号:码猿技术专栏,回复关键词:9529 获取!

5、测试

直接输入错误的秘钥,结果如下:

6、源码追踪

1、OAuthServerAuthenticationEntryPoint在何时调用?

OAuthServerAuthenticationEntryPoint这个过滤器继承了 AbstractAuthenticationProcessingFilter 这个抽象类,一切的逻辑都在 doFilter() 中,陈某简化了其中的关键代码如下:

public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
throws IOException, ServletException {
try {
//调用子类的attemptAuthentication方法,获取参数并且认证
authResult = attemptAuthentication(request, response);
}
catch (InternalAuthenticationServiceException failed) {
//一旦认证异常,则调用unsuccessfulAuthentication方法,通过failureHandler处理
unsuccessfulAuthentication(request, response, failed);
return;
}
catch (AuthenticationException failed) {
//一旦认证异常,则调用unsuccessfulAuthentication方法,通过failureHandler处理
unsuccessfulAuthentication(request, response, failed);
return;
}
//认证成功,则调用successHandler处理
successfulAuthentication(request, response, chain, authResult);
}

关键代码在 unsuccessfulAuthentication() 这个方法中,代码如下:

2、自定义的过滤器如何生效的?

这个就要看 AuthorizationServerSecurityConfigurer#configure() 这个方法了,其中有一段代码如下:

这段代码就是遍历添加的过滤器将其添加到过滤器链中,在BasicAuthenticationFilter这个过滤器之前。

添加到security的过滤器链中,这个过滤器自然会生效了。

3、为什么不能加.allowFormAuthenticationForClients()?

还是在 AuthorizationServerSecurityConfigurer#configure() 这个方法中,一旦设置了 allowFormAuthenticationForClients 为true,则会创建 ClientCredentialsTokenEndpointFilter,此时自定义的自然失效了。

资源服务器的异常

从认证服务获取到令牌之后去请求资源服务的资源,这里涉及到的异常主要有两个,如下:

1、令牌失效

比如令牌不正确、过期,此时返回的异常提示如下:

2、权限不足

令牌的权限不足,比如 /admin 接口只允许 admin 角色访问,此时返回的异常信息如下:

资源服务自定义异常信息

下面针对上述两种异常分别定制异常提示信息,这个比认证服务定制简单。

1、令牌失效

这个比较简单,也是需要自定义AuthenticationEntryPoint。步骤如下:

1、自定义AuthenticationEntryPoint

这个和认证服务的客户端异常类似,这里不再详细说了,直接贴代码,如下:

2、OAuth配置文件中配置

这个比较简单,直接在配置文件中配置即可,代码如下:

3、测试

此时拿着失效的令牌访问资源服务,可以看到已经正常返回定制的提示信息了,如下:

源码和认证服务的类似,自己断点试试,还是很简单的。

案例源码已经上传GitHub,关注公号:码猿技术专栏,回复关键词:9529 获取!

2、权限不足

这个异常定制就更简单了,陈某在第一篇文章:实战!Spring Boot Security+JWT前后端分离架构登录认证!介绍过,下面简单的贴下代码。

1、自定义AccessDeniedHandler

代码如下:

2、OAuth配置文件中配置

和令牌失效的异常配置在同一个方法中,代码如下:

3、测试

访问 /admin 接口,此时的提示信息如下:

案例源码已经上传GitHub,关注公号:码猿技术专栏,回复关键词:9529 获取!

最后说一句(别白嫖,求关注)

陈某每一篇文章都是精心输出,已经写了3个专栏,整理成PDF,获取方式如下:

  1. 《Spring Cloud 进阶》PDF:关注公号:【码猿技术专栏】回复关键词 Spring Cloud 进阶 获取!
  2. 《Spring Boot 进阶》PDF:关注公号:【码猿技术专栏】回复关键词 Spring Boot进阶 获取!
  3. 《Mybatis 进阶》PDF:关注公号:【码猿技术专栏】回复关键词 Mybatis 进阶 获取!

如果这篇文章对你有所帮助,或者有所启发的话,帮忙点赞在看转发收藏,你的支持就是我坚持下去的最大动力!

OAuth2.0实战:认证、资源服务异常自定义!的更多相关文章

  1. OAuth2.0实战!使用JWT令牌认证!

    大家好,我是不才陈某~ 这是<Spring Security 进阶>的第3篇文章,往期文章如下: 实战!Spring Boot Security+JWT前后端分离架构登录认证! 妹子始终没 ...

  2. API代理网关和OAuth2.0授权认证框架

    API代理网关和OAuth2.0授权认证框架 https://www.cnblogs.com/bluedoctor/p/8967951.html 1,授权认证与微服务架构 1.1,由不同团队合作引发的 ...

  3. 新浪微博Oauth2.0授权认证及SDK、API的使用(Android)

    ---------------------------------------------------------------------------------------------- [版权申明 ...

  4. 没错,用三方 Github 做授权登录就是这么简单!(OAuth2.0实战)

    本文收录在个人博客:www.chengxy-nds.top,技术资源共享. 上一篇<OAuth2.0 的四种授权方式>文末说过,后续要来一波OAuth2.0实战,耽误了几天今儿终于补上了. ...

  5. 使用微服务架构思想,设计部署OAuth2.0授权认证框架

    1,授权认证与微服务架构 1.1,由不同团队合作引发的授权认证问题 去年的时候,公司开发一款新产品,但人手不够,将B/S系统的Web开发外包,外包团队使用Vue.js框架,调用我们的WebAPI,但是 ...

  6. 整合spring cloud云架构 - SSO单点登录之OAuth2.0登录认证(1)

    之前写了很多关于spring cloud的文章,今天我们对OAuth2.0的整合方式做一下笔记,首先我从网上找了一些关于OAuth2.0的一些基础知识点,帮助大家回顾一下知识点: 一.oauth中的角 ...

  7. Spring Cloud云架构 - SSO单点登录之OAuth2.0登录认证(1)

    今天我们对OAuth2.0的整合方式做一下笔记,首先我从网上找了一些关于OAuth2.0的一些基础知识点,帮助大家回顾一下知识点: 一.oauth中的角色 client:调用资源服务器API的应用 O ...

  8. OAuth2.0实战之微信授权篇

    微信开发三大坑: 微信OAuth2.0授权 微信jssdk签名 微信支付签名 本篇先搞定微信OAuth2.0授权吧! 以简书的登陆页面为例,来了解一下oauth2.0验证授权的一些背景知识: 1) 传 ...

  9. 阶段5 3.微服务项目【学成在线】_day16 Spring Security Oauth2_07-SpringSecurityOauth2研究-Oauth2授权码模式-资源服务授权测试

    下面要完成  5.6两个步骤 3.3.4 资源服务授权 3.3.4.1 资源服务授权流程 资源服务拥有要访问的受保护资源,客户端携带令牌访问资源服务,如果令牌合法则可成功访问资源服务中的资 源,如下图 ...

随机推荐

  1. scrapy获取汽车之家数据

    1.创建scrapy项目 >scrapy startproject scrapy_carhome 2.找到对应接口 3.创建爬虫文件 > cd scrapy_carhome\scrapy_ ...

  2. Robot frawork关键字使用报错原因

    对比发现1或者${1}两种方式赋值输出的类型都为整形 >>> ${test1}    set variable   'www' >>> log    ${test1 ...

  3. 关于如何在MyEclipse下修改项目名包名,以及类

    1.修改项目名,右键选择properties->web->web-Context-root修改名称或者直接按F2修改.2,修改包名,右键选择Refactor->rename修改名称即 ...

  4. [atAGC022D]Shopping

    称0到$L$的方向为左,同时为了方便,可以假设$0<t_{i}\le 2L$ 当我们确定是进入店中的方向,根据这个店的位置以及购物时间,不难确定出来时火车经过0或$L$的次数,由于$0<t ...

  5. [luogu7092]计数题

    由于$\mu(i)$,因此每一个素数最多存在1次,当$k=0$答案必然为0 根据莫比乌斯和欧拉函数的积性,答案与对素数的划分无关,仅与每一个素数是否出现有关,换言之枚举素数出现的集合$P'$,答案即为 ...

  6. mysql变成类型字段varchar值更新变长或变短底层文件存储原理

    为了搞清楚MySQL对于可变长度字段值修改时,如何高效操作数据文件的机制.之前一直模糊不清,网上也搜不到现成的答案.经过多方资料搜集整理.写出此文供大家一起参阅.由于涉及众多非常底层的知识,我假设读者 ...

  7. 测试平台系列(81) 编写在线执行Redis功能

    大家好~我是米洛! 我正在从0到1打造一个开源的接口测试平台, 也在编写一套与之对应的完整教程,希望大家多多支持. 欢迎关注我的公众号测试开发坑货,获取最新文章教程! 回顾 上一节我们牛刀小试,编写了 ...

  8. 如何在Docker容器中使用Arthas

    Arthas(阿尔萨斯) 能为你做什么? Arthas 是Alibaba开源的Java诊断工具,深受开发者喜爱. 当你遇到以下类似问题而束手无策时,Arthas可以帮助你解决: 这个类从哪个 jar ...

  9. 洛谷 P6177 - Count on a tree II/【模板】树分块(树分块)

    洛谷题面传送门 好家伙,在做这道题之前我甚至不知道有个东西叫树分块 树分块,说白了就是像对序列分块一样设一个阈值 \(B\),然后在树上随机撒 \(\dfrac{n}{B}\) 个关键点,满足任意一个 ...

  10. 毕业设计之zabbix+微信企业号报警

    需要自己申请一个微信企业号 创建应用 AgentId 1000003 Secret SOI8b20G96yUVM29K02-bP5N5o6dovwSF2RrDaXHJNg 企业ID(自己再企业信息里面 ...