SpringSecurityOauth2系列学习(一):初认Oauth2
系列导航
SpringSecurity系列
- SpringSecurity系列学习(一):初识SpringSecurity
- SpringSecurity系列学习(二):密码验证
- SpringSecurity系列学习(三):认证流程和源码解析
- SpringSecurity系列学习(四):基于JWT的认证
- SpringSecurity系列学习(四-番外):多因子验证和TOTP
- SpringSecurity系列学习(五):授权流程和源码分析
- SpringSecurity系列学习(六):基于RBAC的授权
SpringSecurityOauth2系列
- SpringSecurityOauth2系列学习(一):初认Oauth2
- SpringSecurityOauth2系列学习(二):授权服务
- SpringSecurityOauth2系列学习(三):资源服务
- SpringSecurityOauth2系列学习(四):自定义登陆登出接口
- SpringSecurityOauth2系列学习(五):授权服务自定义异常处理
初认Oauth2
hello!我又来啦!
之前我们学习了前后端分离的SpringSecurity架构,但是在微服务满天飞的今天,前面学习的这种架构已经不适用了。
诶那么有没有一种适用于微服务的安全框架呢?
当然就是SpringSecurityOauth2了。
有的小伙伴一看,这不还是SpringSecurity嘛,后面加了一个Oauth2后缀,啥是Oauth2啊?
别急,我们先来看看Oauth2的概念
Oauth2的概念
先说OAuth,OAuth是Open Authorization的简写。
OAuth协议为用户资源的授权提供了一个安全的、开放而又简易的标准。与以往的授权方式不同之处是
OAuth的授权不会使第三方触及到用户的帐号信息(如用户名与密码),即第三方无需使用用户的用户名与
密码就可以申请获得该用户资源的授权,因此OAuth是安全的。
OAuth2.0是OAuth协议的延续版本,但不向前兼容(即完全废止了OAuth1.0)。
使用场景
假设,A网站是一个打印照片的网站,B网站是一个存储照片的网站,二者原本毫无关联。
如果一个用户想使用A网站打印自己存储在B网站的照片,那么A网站就需要使用B网站的照片资源才行。
按照传统的思考模式,我们需要A网站具有登录B网站的用户名和密码才行,但是,现在有了OAuth2,只需要A网
站获取到使用B网站照片资源的一个通行令牌即可!这个令牌无需具备操作B网站所有资源的权限,也无需永久有
效,只要满足A网站打印照片需求即可。
这么听来,是不是有点像单点登录?NONONO!千万不要混淆概念!单点登录是用户一次登录,自己可以操作其
他关联的服务资源。OAuth2则是用户给一个系统授权,可以直接操作其他系统资源的一种方式。
但SpringSecurity的OAuth2也是可以实现单点登录的!
总结一句:SpringSecurity的OAuth2可以做服务之间资源共享,也可以实现单点登录!
角色说明
- 应用(客户端):客户端就是一个要访问用户账号的应用,需要得到用户的允许后才可以得到授权
- API服务(资源服务):资源服务通常提供API用于访问用户的授权客户端访问的信息
- 授权服务:让用户批准或拒绝请求的服务,严格来说,认证不属于这个服务的职责,虽然往往认证和授权都在一个服务。当然授权服务也可以是资源服务
- 用户(资源所有者):授权客户端访问资源服务器的主体
通常,作为作为OAuth2的客户端 ,需要上传这几个字段:
Application name
:应用名HomepageURL
:主页,主要是域名Authorization callback URL
:回调地址,授权成功后返回的URL
然后系统对于每个客户端会生成Client ID
(必要,应用工具唯一标识)和Client Secret
(非必要,可以理解为应用的密码),请求授权要求客户端传入这两个值进行授权
OAuth2.0中主流的四种授权方式
为了说明四种模式先准备一张图
授权码模式(authorization code)
- 流程
说明:【A服务客户端】需要用到【B服务资源服务】中的资源
-
第一步:【A服务客户端】将用户自动导航到【B服务认证服务】,这一步用户需要提供一个回调地址,以备【B服务认证服务】返回授权码使用。
-
第二步:用户点击授权按钮表示让【A服务客户端】使用【B服务资源服务】,这一步需要用户登录B服务,也就是说用户要事先具有B服务的使用权限。
-
第三步:【B服务认证服务】生成授权码,授权码将通过第一步提供的回调地址,返回给【A服务客户端】。
注意这个授权码并非通行【B服务资源服务】的通行凭证。
-
第四步:这个时候,通常会给用户弹出一个页面,询问用户是否同意授权给A服务,用户点击同意,然后【A服务认证服务】携带上一步得到的授权码向【B服务认证服务】发送请求,获取通行凭证token。
-
第五步:【B服务认证服务】给【A服务认证服务】返回令牌token和更新令牌refresh token。
- 使用场景
授权码模式是OAuth2中最安全最完善的一种模式,应用场景最广泛,可以实现服务之间的调用,常见的微
信,QQ等第三方登录也可采用这种方式实现。
简化模式(implicit)
- 流程
说明:简化模式中没有【A服务认证服务】这一部分,全部又【A服务客户端】与B服务交互,整个过程不再有授权码,token直接暴露在浏览器。
-
第一步:【A服务客户端】将用户自动导航到【B服务认证服务】,这一步用户需要提供一个回调地址,以备【B服务认证服务】返回token使用,还会携带一个【A服务客户端】的状态标识state。
-
第二步:用户点击授权按钮表示让【A服务客户端】使用【B服务资源服务】,这一步需要用户登录B服务,也就是说用户要事先具有B服务的使用权限。
-
第三步:【B服务认证服务】生成通行令牌token,token将通过第一步提供的回调地址,返回给【A服务客户端】。
-
使用场景
适用于A服务没有服务器的情况。比如:纯手机小程序,JavaScript语言实现的网页插件等。
密码模式(resource owner password credentials)
- 流程
-
第一步:直接告诉【A服务客户端】自己的【B服务认证服务】的用户名和密码
-
第二步:【A服务客户端】携带【B服务认证服务】的用户名和密码向【B服务认证服务】发起请求获取token。
-
第三步:【B服务认证服务】给【A服务客户端】颁发token。
-
使用场景
此种模式虽然简单,但是用户将B服务的用户名和密码暴露给了A服务,需要两个服务信任度非常高才能使用。
客户端模式(client credentials)
-
流程
说明:这种模式其实已经不太属于OAuth2的范畴了。A服务完全脱离用户,以自己的身份去向B服务索取token。换言之,用户无需具备B服务的使用权也可以。完全是A服务与B服务内部的交互,与用户无关了。
-
第一步:A服务向B服务索取token。
-
第二步:B服务返回token给A服务。
-
使用场景
A服务本身需要B服务资源,与用户无关。
JWS/JWK
Oauth2中返回的token其实也有很多种类型,SpringSecurityOauth2中授权服务器如果不做配置,那么默认返回的其实只是一串UUID。
那么我们使用怎样的token呢?当然还是JWT啦!
SpringSecurityOauth2中授权服务器签发的token还是使用非对称加密进行签名,但是其内部已经实现了相关逻辑,不再需要我们自己去写逻辑,配置好之后直接使用即可。
不过这里还需要了解一个术语:JWS/JWK
JWT火爆之后,人们对于其安全性做了很多讨论,其签名也是人们讨论的话题之一。JWT支持使用不同的算法进行签名,但是没有一个统一的方式。
JWS就是JWT签名的验证数据完整性的不同的加密机制
JWK就是签名加密密钥的JSON结构。一般情况下,对于签名需要有一个不对称加密,需要有密钥对进行加密。
公钥和私钥。公钥加密需要私钥来解密,同样的私钥加密需要公钥来解密。
一般公钥和私钥放在不同的地方,私钥保存在服务器上面,而公钥保存在客户端。公钥可以公开,私钥保密。
签发token使用私钥加密,签名。token的内容是公开的,但是token的签名验证需要公钥去解密验证,没有私钥是伪造不出来私钥加密的签名的。只要验证成功,说明这个token是使用私钥签发的,是可以信任的
流程:
- 客户端请求Access Token,资源服务器返回一个使用JWS签名的一个JWT,这里的JWS包含一个Kid,其为一种加密形式的唯一标识,表示了应该使用说明方式进行加密。
- 客户端请求资源,将使用JWS签名的一个JWT传递给资源服务器,资源服务器会向授权服务器请求一个公钥,授权服务器会把JWK(即Kid对应的这种密钥结构)返回给资源服务器,之后资源服务器就使用这个公钥去验证签名,去验证权限。
加密流程
加密流程也就是创建JWT的过程
拿私钥进行非对称加密,将kid
和其kid value
写入jws的header中,然后在进行Claims
声明进行编码
然后将jws header放入签名这个字段里面去,进行编码
最后就形成了有header,有cliams,有签名的一个jwt
解密流程
拿到JWT之后,去请求一个公钥,对这个JWT进行一个解析
解析完成之后,得到签名,然后取出其中Kid
为key对应的Kid value
然后将Kid value
设置到签名当中
然后在去验证签名是否正确