最近放假之后实现了一下单点登录,各种项目都需要账号管理系统,太麻烦了,导致各种项目都咕咕咕,懒得写(划掉)
OAuth 2.0 协议(本文简称 OAuth)是一种三方授权协议,目前大部分的第三方登录与授权都是基于该协议的标准或改进实现。OAuth 1.0 的标准在 2007 年发布,2.0 的标准则在 2011 年发布,其中 2.0 的标准取消所有 Token 的加密过程,并简化了授权流程,但因强制使用 HTTPS 协议,被认为安全性高于 1.0 的标准。
在了解 OAuth 前,需先了解一下各个名词的基本概念
客户端:客户端是 OAuth 的接入方,目的是请求用户储存在资源服务器上的受保护资源。可以理解成前端应用(比如 Web、应用程序、小程序等)
用户代理(UA):用户参与互联网的工具,比如浏览器、系统标识等
资源所有者:受保护资源所属实体,比如资源持有人(就是你!!),下文用户即资源所有者
授权服务器:验证资源所有者身份的服务器,就是平时大家口中的 “登录服务器”
资源服务器:托管资源的服务器,能够接收和响应持有令牌的资源访问请求,可以理解成是客户端的后端程序
访问令牌:就是我们平时常说的 Token (Access Token),在用户(资源所有者)的授权许可下授权服务器下发给客户端的一个授权凭据,客户端(或资源服务器或任何人)可携带此令牌代表资源所有者的身份访问受保护的资源
刷新令牌:就是我们平时常说的 “双 Token”(Refresh Token),作用是在于更新访问令牌。访问令牌一般的时间较短,使用刷新令牌重新换取访问令牌,可以一定程度上减少对授权服务器和资源所有者的负担
回调地址:OAuth2.0 是一类基于回调的授权协议,以 302 重定向的形式,可以一定程度上简化客户端的操作
授权范围:用户可以指定该客户端能够访问的受保护资源的范围,比如个人纳税识别号、昵称、电邮地址等
OAuth 协议已定义了 4 种授权模式(授权码模式、隐式授权模式、资源所有者密码凭证授权模式、客户端凭证授权模式),其中最具代表性的就是授权码模式,并且我也是使用这种模式授权的,所以本文就只讲解这种模式。如果需要了解其他模式,小米开放平台的文章中讲解的非常详细,可以前往观看,具体链接详见本文底部参考文献部分
授权码模式在整个授权流程上与 1.0 版本最贴近,但是整个流程还是要简化了许多,也是 OAuth2.0 中最标准,应用最广泛的授权模式。这类授权模式非常适合于具备服务端的应用,当然现在大多数 APP 都有自己的服务端,所以大部分 APP 的 OAuth 授权都可以采取授权码模式,下图为授权码各个角色之间的交互时序(这里让用户直接参与其中,省略了用户代理):
整个授权流程说明如下(具体参数释义见下文):
注意,此部分可能在实际中有所改动,具体请以 Nya Account 使用文档为准
访问 https://account.lolinya.net/ ,按照流程 登录 / 注册 账号
在应用管理中,选择创建应用,在弹出的模态框中,输入新应用的名称。该名称将会展示给用户。后续可修改。
随后点击应用列表操作栏中的查看详细按钮,配置应用的简介、重定向 URL 以及需要的权限,配置完成后需要点击保存按钮
目前程序为测试阶段,权限配置后期会逐渐增加
配置完后可在此处查看客户端 ID 和 客户端秘钥
授权码是授权流程的一个中间临时凭证,是对用户确认授权这一操作的一个暂时性的证书,其生命周期一般较短(协议建议最大不要超过10分钟),在这一有效时间周期内,客户端可以凭借该暂时性证书去授权服务器换取访问令牌
将用户 302 重定向到该地址
https://account.lolinya.net/authorize
并携带请求参数
参数名称 | 是否必须 | 描述信息 |
---|---|---|
client_id | 必须 | 客户端ID,用于标识一个客户端,在注册应用时生成(即 AppId) |
state | 推荐 | 用于维持请求和回调过程中的状态,防止CSRF攻击,服务器不对该参数做任何处理,如果客户端携带了该参数,则服务器在响应时原封不动的返回 |
redirect_uri | 可选 | 授权回调地址(默认读取在注册应用时配置的) |
scope | 可选 | 权限范围,用于对客户端的权限进行控制,如果客户端没有传递该参数,那么服务器则以该应用的所有权限代替(所有权限默认读取在注册应用时配置的) |
response_type | 可选 | 对于授权码模式 response_type=code ,默认为此项,无需单独传递 |
用户在完成授权后,将会被重定向到创建应用时指定的地址,并携带请求参数(如果用户拒绝了授权,则 code=deny
)
名称 | 描述信息 |
---|---|
code | 授权码,授权码代表用户确认授权的暂时性凭证,只能使用一次,5分钟内有效 |
state | 如果客户端传递了该参数,则原封不动返回 |
授权服务器在下发授权码之后,客户端或资源服务器,将携带刚刚下发的授权码请求以下地址
https://api.liyxi.com/node/v0/token
(中国大陆镜像服务器,很可能会变更,具体以使用文档为主)
携带请求体
名称 | 是否必须 | 描述信息 |
---|---|---|
grant_type | 必须 | 对于授权码模式 grant_type=authorization_code |
code | 必须 | 上一步骤获取的授权码 |
redirect_uri | 必须 | 授权回调地址 |
client_id | 必须 | 客户端 ID,用于标识一个客户端,等同于 appId ,在注册应用时生成 |
client_secret | 必须 | 客户端秘钥,等同于 appSecret ,在注册应用时生成 |
type | 可选 | 如果 type=info 则直接下发用户基础信息,而不是 token |
正常情况下的响应体
名称 | 描述信息 |
---|---|
access_token | 访问令牌 |
token_type | 访问令牌类型,比如 bearer,mac 等等 |
expires_in | 访问令牌的生命周期,以秒为单位,表示令牌下发后多久时间过期 |
refresh_token | 暂不开放,一般用户返回 refresh_token=null |
scope | 用户实际权限范围,如 [1,2,3,4] ,具体参见权限列表 |
如果 type=info
,则响应
名称 | 描述信息 |
---|---|
uid | 用户唯一标识符 |
nickname | 用户昵称 |
status | 用户当前在系统中的状态 |
avatar | 用户头像 url |
比如需要获取用户信息,则携带刚刚下发的令牌访问
https://api.liyxi.com/node/v0/info
(中国大陆镜像服务器,很可能会变更,具体以使用文档为主)
响应体
json
{
status: 200,
msg: "获取用户基础信息成功",
data: {}
}
data 如下
名称 | 类型 | 描述信息 |
---|---|---|
uid | String | 用户唯一标识符 |
nickname | String | 用户昵称 |
status | Number | 用户当前在系统中的状态 |
avatar | String | 用户头像 url |
在国内某短视频平台1中出现了可笑的一幕,这是一个关于 token 无感刷新的视频的评论区
令牌是由 header 、payload 、和 signature 三部分使用英文符号 “.” 连接而成的字符串(JWT)。在一般情况下,header 中存储的是此令牌的签名算法以及类型(base64 编码后),payload 中存储的是用户在使用 jwt 生成令牌时传入的数据(base64 编码后),signature 中存储的是使用前两者与特定的字符串秘钥加密后的字符串(签名),用于防篡改。对于 “长时间 token 会被破解” 这样的说法,几乎不可能,并且这也不是刷新令牌产生的根本原因。
关于令牌被劫持,在正常情况下,属于不可避免的原因或个人原因(比如在客户端或资源服务器上人为安装了病毒软件、第三者使用了漏洞)。在网络传输的过程中,TLS 拥有认证性、机密性、完整性以及重放保护,TLS 的基本工作方式是为客户端使用非对称加密与服务器进行通信,实现身份验证并协商对称加密使用的密钥,对称加密算法采用协商密钥对信息以及信息摘要进行加密通信,不同的节点之间采用的对称密钥不同,从而可以保证信息只能通信双方获取,因此令牌绝大部分情况下不会在网络传输中被劫持
关于双令牌的形式,确实可以在一定程度上增加安全性,但是在实际中更多的是用于减轻授权服务器压力。举个例子,如用户张三2正在使用扣扣3账号游玩卑微斗农民4,张三在对局中连续打出了数个对子,如果客户端的每次一请求都需要经过授权服务器,则授权服务器必将承担非常大的压力,如果授权服务器出现故障,则会影响到所有存放受保护资源的服务器上的业务。如果直接颁发长期的令牌,客户端在登录后不再与授权服务器接触,此时张三的扣扣账号涉嫌及批量点赞/批量加好友/使用第三方客户端等业务违规操作被暂时冻结,需要前往扣扣自助处理或进行资金管理5,但是张三正在游玩卑微斗农民,并没有时间搭理。此时张三的账号已经处于异常状态,存放受保护资源的服务器无法得知,如果令牌是长期的,则会一直向客户端提供服务,这非常危险。因此客户端需要定时向授权服务器获取一次资源所有者的状态,以便增加安全性,这才是刷新令牌出现的真实原因,而不完全是用于防止被盗。
https://dev.mi.com/console/doc/detail?pId=711
https://account.lolinya.net/docs/
https://datatracker.ietf.org/doc/html/rfc5849
https://datatracker.ietf.org/doc/html/rfc6749
https://datatracker.ietf.org/doc/html/rfc6750
https://datatracker.ietf.org/doc/html/draft-hammer-oauth-v2-mac-token-02