OAuth
OAuth(开放授权)是一个开放标准,允许用户授权第三方网站访问他们存储在另外的服务提供者上的信息,而不需要将用户名和密码提供给第三方网站或分享他们数据的所有内容。比如,现在很多APP都支持使用微信账号登录,用户授权后,这些APP后台可以获取到用户在微信绑定的手机号码和昵称信息,从而完成在APP的注册和登录,期间不需要用户提供微信的密码。
OAuth1.0
规范看这里
OAuth2.0
2.0版本比1.0版本更简单也更安全。OAuth2.0规范看这里或者rfc6749文件。
首先要先理解几个概念:
Access Token
访问令牌是用来访问受保护的资源的凭据。一个访问令牌是一个字符串,它代表发给客户端的授权。令牌代表资源所有者授予的对特定范围和访问的时间(令牌是有范围和有效期的)。
Refresh Token
Refresh Token是用于获取Access Token的凭据。刷新令牌是授权服务器发给客户端的,用于在当前访问令牌已经失效或者过期的时候获取新的访问令牌。
Scope
scope用于限制限制第三方应用可以获取哪些用户账户信息,由授权服务器自己定义。 应用程序可以请求一个或多个scope,然后在在授权页面把申请的权限范围呈现给用户,并且颁发给该应用程序的访问令牌将限于所授予的scope。
OAuth2.0授权方式
OAuth2.0定义了四种授权方式
- 授权码(authorization-code)
- 隐式(implicit)
- 密码式(password):
- 客户端凭证(client credentials)
注意,不管哪一种授权方式,第三方应用申请令牌之前,都必须先到授权服务器备案,说明自己的身份,然后会拿到两个身份识别码:客户端 ID(client ID)和客户端密钥(client secret)。相当于账号密码,这是为了防止令牌被滥用,没有备案过的第三方应用,是不会拿到令牌的。比如:微信登录第三方应用,需要先在微信开放平台注册开发者帐号,并拥有一个已审核通过的移动应用,并获得相应的 AppID 和 AppSecret。
下面举例说明这四种方式授权步骤,假设有A和B两个网站,用户使用B网站的账号信息,登录A网站。
1. 授权码方式
适用于有前端(APP或网页)的应用。第三方网站或APP先向授权服务器申请一个授权码(用户确认授权后,授权服务器将授权码返回给第三方网站),然后第三方网站或APP后台用授权码去申请访问令牌。
1.1 用户在A网站点击“使用B登录”,浏览器跳转到B网站的授权页面。
https://b.com/auth2/code?
response_type=code
&client_id=${client_id}
&redirect_uri=${callback_uri}
&scope=user_info
在上面的网址中,response_type=code,表示第三方应用要申请授权码,client_id是第三方应用在授权服务器系统备案的id,redirect_uri是第三方网站提供的回调地址,这一步scope可选。
1.2 在授权页面,用户确认授权后,B回调A网站的链接,返回授权码。
https://a.com/authorize?code=${code}
1.3 A后台接收到授权码后,用授权码向B申请access_token
https://b.com/auth2/access_token?
grant_type=authorization_code
&client_id=${client_id}
&client_secret=${client_secret}
&code=${code}
&redirect_uri=${redirect_uri}
在上面的网址中,grant_type=authorization_code,表示授权码方式,client_id和client_secret是第三方应用在授权服务器系统备案的id和密码,redirect_uri是第三方网站提供的回调地址,可选,B可以回调A,也可以直接返回访问令牌。
1.4 B回调A,或直接返回数据。
{
"access_token":"2YotnFZFEjr1zCsicMWpAA",
"expires_in":3600,
"refresh_token":"tGzv3JOkF0XG5Qx2TlKWIA",
"openId":"openId"
}
access_token是访问令牌,expires_in是访问令牌的超时时间,refresh_token用于访问令牌过期时重新向B获取访问令牌,这样可以减少用户操作授权的次数。openId是B提供的,这个用户的id,用于后续获取该用户账号信息。
1.5 A调用B的接口,用access_token和openId来获取用户账号信息等。
2. 隐式
隐式授权是为了兼顾到在浏览器中用诸如JavaScript的脚本语言实现的客户端而优化的简化授权代码流程。在隐式授权流程中,不是发给客户端一个授权码,而是直接发给客户端一个访问令牌,而且不会对客户端进行认证。隐式授权提高了一些客户端(比如基于浏览器实现的客户端)的响应能力和效率,因为它减少了获得访问令牌所需的往返次数。
2.1 用户在A网站点击“使用B登录”,浏览器跳转到B网站的授权页面。
https://b.com/auth2/access_token?
response_type=token
&client_id=${client_id}
&redirect_uri=${redirect_uri}
在上面的网址中,response_type参数为token,表示要求直接返回令牌。
2.2 用户授权后,B跳回redirect_uri指定的网址,并带上令牌,注意,这个跳转地址后面带参数必须是#。
https://a.com/callback#token=${access_token}
3. 密码式
资源所有者的密码凭据(比如,用户名和密码)可以直接作为授权许可来获取访问令牌。这个凭据只应该用在高度信任的资源所有者和客户端之间(比如,客户端是系统的一部分,或者特许的应用),并且其它授权模式不可用的时候。
3.1 A向用户要B的账号密码,然后直接用账号密码申请令牌。
https://b.com/auth2/access_token?
grant_type=password
&client_id=${client_id}
&username=${username}
&password=${password}
3.2 B直接返回json数据。
{
"access_token":"2YotnFZFEjr1zCsicMWpAA",
"expires_in":3600,
"refresh_token":"tGzv3JOkF0XG5Qx2TlKWIA",
"openId":"openId"
}
4. 客户端凭证
这种方式适用于没有前端的纯后台应用。
4.1 A直接请求B。
https://b.com/auth2/access_token?
grant_type=client_credentials
&client_id=${client_id}
&client_secret=${client_secret}
4.2 B响应json数据,不需要refresh_token。
{
"access_token":"2YotnFZFEjr1zCsicMWpAA",
"expires_in":3600,
}
5.访问令牌的刷新
访问令牌都是有时效的,第三方应用在当前访问令牌已经失效或者过期的时候主动获取新的访问令牌,以减少用户操作授权的次数,提高用户体验。
5.1 A后台主动请求B接口。
https://b.com/auth2/refresh_token?
grant_type=access_token
&client_id=${client_id}
&client_secret=${client_secret}
&refresh_token=${refresh_token}
5.2 B返回新的令牌
{
"access_token":"2YotnFZFEjr1zCsicMWpAA",
"expires_in":3600,
"refresh_token":"tGzv3JOkF0XG5Qx2TlKWIA",
}
微信登录
微信登录使用的是授权码方式,APP使用微信登录时,用户授权后,授权码直接返回给第三方APP,而不是回调第三方后台,官方文档很详细。