Spring Security 5 OAuth2 Login

  先吐槽一下上一篇博客,现在回过头来自己看都不知道写了什么,主要是不会使用Markdown的首行缩进,看起来就很分不清。当然,主要还是内容不清楚,这一篇其实就是补充上一篇的。真实的情况是,写上一篇之前,发现Spring Security 5支持OAuth2登录,但是想弄的企业微信登录虽然在文档里说它的网页授权登录就是OAuth2登录,但实际上跟OAuth2标准差别还有点大,至少跟Spring Security里面预定义的Github、google授权登录不一样,简单地换一换client id、client secret是解决不了的,所以就回退到了Spring Security 4,然后采用了自定义登录的办法解决问题,当然,这种办法用在Spring Security 5下面也是行得通的,只不过总是感觉别扭,就是因为Spring Security 5是支持OAuth2登录的,企业微信网页授权登录就算不那么遵循标准,但是基本步骤、过程总不可能完全不同,不能简单照搬,但是或多或少利用一下Spring Security 5提供的OAuth2登录框架总比自己从过滤器干起好一点吧,关键是这一次绕开它,下一次如果要用到还是得学,所以就又回到Spring Security 5,并且努力使用它的OAuth2登录框架来实现企业微信的网页授权登录。
  首先发现一点,Spring Security 5里面的OAuth2相关的功能是新增加的,在文档里面融合得不是很好。比如说,就像自己实现相似的功能一样,应该在Security Filter Chain里面至少多出一个过滤器来实现这个功能,但是文档里面介绍Security Filter的地方,还是原来那些过滤器,所以仅仅看文档,那还是只能照着文档里面的说明和例子来做,包括文档里面关于redirect url添加自定义参数这些都照做了,都不成功。跟踪自己写的代码也没发现错在哪里,后来才想到OAuth2登录过程本来就有好几次请示/应答过程,而且是在服务、客户之间跳来跳去,搜索API发现相关的过滤器有两个,OAuth2AuthorizationRequestRedirectFilter和OAuth2LoginAuthenticationFilter一个是负责把用户访问保护资源的请求重定向到服务器,另一个负责处理用户认证。从OAuth2标准流程来看,第一步是引导用户到授权服务器的授权页面,然后带一个授权码code返回,第二步是用授权码去资源服务器换取AccessToken,第三步才是带着AccessToken去访问资源。之所以第一、二步要两次才换回一个AccessToken是因为授权服务器和资源服务器不一定是同一个,或者说可以把服务器分成两块,各干各的,也就是专业化吧,授权服务器只管验证客户身份,资源服务器主要管资源,所以用户取得授权码code之后去访问资源时,资源服务器还要去授权服务器验证code,验证通过之后换发自己的AccessToken,之后用户只要凭这个AccessToken就可以访问资源了。这个设计是非常有效的,资源服务器根本不关心用户是谁,只管授权码code是不是有效,并且换发自己的AccessToken之后,并不会每次都去验证授权码code。但是企业微信网页授权登录好像不是这么回事,AccessToken并不是用授权码换来的,客户端可以直接获得,但是AccessToken又有访问频率限制,不能每次都重新去获取。当然,这个可以想办法把以前获取得AccessToken持久化,但是不需要换AccessToken就比较麻烦,Spring Security 5或者说标准的步骤偏偏有这一步,如果顺着这条路走下去,不仅仅是查询参数多少或者一不一致的问题,直接就中访问频率的限制,肯定是行不通的。
  所以,需要搞清楚Spring Security 5 OAuth2 Login的流程,在适当的地方进行自定义。刚才提到的两个过滤器中的OAuth2LoginAuthenticationFilter就是负责第二、三步的,也就是负责换取AccessToken和获取用户身份信息的。OAuth2LoginAuthenticationFilter和UsernamePasswordAuthenticationFilter一样,也是继承自AbstractAuthenticationProcessingFilter的,它同样本身并不干具体的工作,而是将工作交给AuthenticationManager去做,OAuth2LoginAuthenticationFilter与UsernamePasswordAuthenticationFilter主要不同在于它们将从request参数获取到的值封装成不同的AuthenticationToken,OAuth2LoginAuthenticationFilter封装OAuth2LoginAuthenticationToken而UsernamePasswordAuthenticationFilter封装UsernamePasswordAuthenticationToken,然后都是交给AuthenticationManager,最后由不同的AuthenticationProvider去具体实现,每个AuthenticationProvider都supports(支持)一种AuthenticationToken,支持当前AuthenticationToken的AuthenticationProvider的
authenticate方法会被调用,成功的话返回补充完整的AuthenticationToken,如果所有支持的AuthenticationProvider都返回Null,则认证失败。这里要注意的是,如果自定义的AuthenticationProvider不返回null值,那么返回的AuthenticationToken的isAuthenticated必须要为真,否则会抛出异常,这个很容易理解。对于OAuth2LoginAuthenticationToken来说Spring Security 5提供的是OAuth2LoginAuthenticationProvider,这里显然要自定义一个差不多的类来代替它。从OAuth2LoginAuthenticationToken的构造函数来看,OAuth2LoginAuthenticationToken(ClientRegistration clientRegistration,
OAuth2AuthorizationExchange authorizationExchange)OAuth2AuthorizationExchange应该就是交换AccessToken用的,源代码没看,但是望文生义的话,应该在OAuth2LoginAuthenticationProvider里面会用到这个类的信息去交换AccessToken然后再去获取用户信息。这里假设要改变OAuth2AuthorizationExchange实现自定义的交换AccessToken的过程,就得自定义OAuth2LoginAuthenticationFilter那不如按前一篇的方法去实现还要简单些。所以,最后的办法就是自定义OAuth2LoginAuthenticationProvider在它的authenticate方法里去实现用户认证。这个办法从整过认证过程来看自定义的东西少一点,就只自定义一个AuthenticationProvider比上一篇的办法即要自定义AuthenticationFilter,又要自定义AuthenticationToken(其实可以用UsernamePasswordAuthenticationFilter但是为了功能性和以后扩展还是自定义了)还要自定义AuthenticationProvider好一些。这里说的好是从能够尽量利用Spring已有的功能的角度,因为主要目的是学习,如果说从功能上和代码的量上,上一篇的方法也差不多,可是那样就算了的话就学不到这些东西了。
  另外,需要备注的是,OAuth2LoginAuthenticationFilter并不是自然就会有,包括文档里面提到的标准的过滤器中的UsernamePasswordAuthenticationFilter也一样,都是配置了才有,配置了formlogin才会注册UsernamePasswordAuthenticationFilter,配置了oauth2Login才会有OAuth2LoginAuthenticationFilter。一开始没搞清楚,特别是使用自定义登录页时,觉得formlogin下有个loginpage,oauth2login下又有一个loginpage很啰嗦,删掉了formlogin这一部分,结果oauth2login功能调试通了,用用户名密码登录老是登录失败,自定义的登录页又写得简化了一些,没有显示详细的出错信息,害得这个问题查了好久好久……
  最后,文档里面提到了oauth2login下面可以配置tokenEndpoint(),但是没有详细的说明。这应该就是换AccessToken的那部分吧,能不能够只自定义这里就达到想要的目的了呢?继续研究

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值