文章目录
- OAuth2简介
- 角色
- 流程
- 客服端注册
- Client Type
- 四种授权模式
- 授权码模式
- 隐藏式
- 密码式
- 凭证式
- RefreshToken
OAuth2简介
OAuth 是一个开放授权协议标准,允许用户授权第三方应用访问他们存储在另外的服务提供者上的信息,而不需要将用户名和密码提供给第三方应用或分享他们数据的全部内容。
OAuth2 是 OAuth 协议2.0版本,不兼容1.0版本。RFC6749 文档描述了 OAuth2 协议的全部内容。本文将会基本 RFC协议文档去带大家理解OAuth2协议,如果读完以后感觉晦涩弄懂,可以结合 阮一峰大神这个博客去理解。
角色
例子:Ruby China这个网站支持使用github账号登录,当用户选择使用github登录的时候,需要用户先输入github账号密码登录github,然后询问用户是否授权rubyChina这个网站获取用户的信息。
https://ruby-china.org/account/sign_in
这就是一个第三方登录的一个例子, 下面列出的是OAuth2协议中所涉及到的几个角色以及描述。
流程
- 用户打开客户端以后,客户端要求用户给予授权。
- 用户同意给予客户端授权。
- 客户端使用上一步获得的授权,向认证服务器申请令牌。
- 认证服务器对客户端进行认证以后,确认无误,同意发放令牌。
- 客户端使用令牌,向资源服务器申请获取资源。
- 资源服务器确认令牌无误,同意向客户端开放资源。
客服端注册
如果一个第三方应用想使用第三方登录,该第三方应用的开发者需要先到 Authentication Server去申请一个 client,一般需要:
- 应用名称,
- Home Page url
- Redirect URI
- 指定client type
注册完以后可以拿到 clientId 和 client secret
参考第三方网站开发文档,选择适合的授权模式去实现 OAuth2 第三方授权登录
例如如果你想要你自己的网站支持github登录的话,首先你要去github上面注册一个client:
Client Type
官方协议解释:
OAuth defines two client types, based on their ability to authenticate securely with the authorization server (i.e., ability to maintain the confidentiality of their client credentials,根据client申请方时候有能力去维护client的credentials信息分为 confidential和public两种。
confidential:Clients capable of maintaining the confidentiality of
their credentials (e.g., client implemented on a secure server with
restricted access to the client credentials), or capable of secure
client authentication using other meanspublic: Clients incapable of maintaining the confidentiality of their
credentials (e.g., clients executing on the device used by the
resource owner, such as an installed native application or a web
browser-based application), and incapable of secure client
authentication via any other means
四种授权模式
OAuth2的核心是认证服务器向第三方颁发令牌,其根据不同的互联网应用场景,定义了一下四种允许第三方应用获取令牌的模式。分别是:
假设用户A通过浏览器访问网站:https://client.example.com,而该网站需要用户A授权,去访问用户A在网站 http://server.example.com 上面存储的个人信息。
我们以这个例子来解释下面介绍的各种模式。下面所说的客户端指的是 https://client.example.com,授权服务器以及资源服务器指的是 http://server.example.com,假设它们在同一个服务器上。
一下四种模式涉及到一些代码名称,先给大家列出来:
授权码模式
授权码(authorization code)方式,指的是第三方应用先申请一个授权码,然后再用该码获取令牌。
这种方式是最常用的流程,安全性也最高,它适用于那些有后端的 Web 应用。授权码通过前端传送,令牌则是储存在后端,而且所有与资源服务器的通信都在后端完成。这样的前后端分离,可以避免令牌泄漏。
A)用户访问客户端(第三方应用),客户端引导(重定向)用户的代理(浏览器)去到授权服务器的授权页面,这个时候客户端会在URI上附上 clientId, redirectUri, requestScope以及 local state,例如:
GET HTTP/1.1
http://server.example.com/authorize?response_type=code&client_id=s6BhdRkqt3&state=xyz&redirect_uri=https%3A%2F%2Fclient%2Eexample%2Ecom%2Fcb&scope=read
B)授权服务器要求用户A输入用户密码认证身份,并询问用户A是否同意授权
C)假设用户同意授权,授权服务器使用 redirectUri (在authorization request中redirectUri或者客户端注册是的redirectUri)将用户导向客户端,并附上一个授权码code
https://client.example.com/cb?code=SplxlOBeZQQYbYS6WxSbIA&state=xyz
D)客户端拿到授权码code以后,在后台使用授权码向授权服务器发送post请求换取accessToken令牌,并附上在(A)这个步骤请求授权码的时候的redirectUri
POST HTTP/1.1
http://server.example.com/token
grant_type=authorization_code&code=SplxlOBeZQQYbYS6WxSbIA&redirect_uri=https%3A%2F%2Fclient%2Eexample%2Ecom%2Fcb&clientId=xxx&clientSecret=xyzz
E)认证服务器认证客户端,检验授权码以及redirectUri,检验成功后发放 accesToken 以及 refreshToken 最后客户端就可以使用令牌向resourceServer请求资源了。
隐藏式
有些 Web 应用是纯前端应用,没有后端。这时就不能用上面的方式了,必须将令牌储存在前端。
OAuth2 允许直接向前端颁发令牌。这种方式没有授权码这个中间步骤,所以称为(授权码)“隐藏式”(implicit)
令牌直接存储到前端浏览器,是不太安全的。
A)用户访问客户端(第三方应用),客户端引导(重定向)用户的代理(浏览器)去到授权服务器的授权页面,这个时候客户端会在URI上附上 clientId, redirectUri, requestScope以及 local state,例如:
GET HTTP/1.1
http://server.example.com/authorize?response_type=code&client_id=s6BhdRkqt3&state=xyz&redirect_uri=https%3A%2F%2Fclient%2Eexample%2Ecom%2Fcb&scope=read
B)授权服务器要求用户A输入用户密码认证身份,并询问用户A是否同意授权
C)假设用户同意授权,授权服务器使用 redirectUri (在authorization request中redirectUri或者客户端注册是的redirectUri)将用户导向客户端,并附上令牌 accessToken
https://client.example.com/cb?#access_token=2YotnFZFEjr1zCsicMWpAA&state=xyz&token_type=example&expires_in=3600
令牌的位置URL 锚点(fragment),而不是查询字符串,好像是为了避免中间人攻击啥的 (http)
D、E、G)看网上说是使用一段脚本能够将token从(C)重定向的URI抽取出来,具体怎么时候就不是很清楚了
最后客户端就可以使用令牌向resourceServer请求资源了。
密码式
OAuth2 允许用户把用户名和密码直接告诉该应用,该应用就使用你的密码申请令牌,这种方式称为"密码式"(password) 直接提供账号密码,风险很大,所以应当是用户高度新人客户端才会使用这种方式。
客户端直接使用用户的账户密码去换取accessToken,发送的post请求:
POST
http://server.example.com/token
body: grant_type=password&username=johndoe&password=A3ddj3w&clientId=xxx
凭证式
客户端直接使用客户端凭证(client credentials)向认证服务器请求令牌,适用于没有前端的命令行应用。
同样使用post请求:
POST
http://server.example.com/token
body: grant_type=client_credentials&clientId=xxx&client_secret=SECRET
RefreshToken
client请求token的时候,服务器一般会返回一个refreshToken,一般来说accessToken的过期时间很短,当accessToken过期了之后,授权服务器允许client通过refreshToken去再次请求一个新的accessToken。所以refreshToken的过期时间肯定是要比accessToken长。
refreshToken是可选的,如果对于安全要求比较高的话,可以不用。