文章目录
OAuth2.0
历史由来
- 有一个第三方网站,可以将用户储存在Google的照片,冲印出来。用户为了使用该服务,必须让第三方网站读取自己储存在Google上的照片
- 传统方法是,用户将自己的Google用户名和密码,告诉第三方网站,后者就可以读取用户的照片了。这样的做法有以下几个严重的缺点
- 第三方网站为了后续的服务,会保存用户的密码,这样很不安全,第三方应用程序被破解,就会导致用户密码泄漏,以及所有被密码保护的数据泄漏
- 第三方网站拥有了用户储存在Google所有资料的权力,用户没法限制第三方网站获得授权的范围和有效期
- 用户只有修改密码,才能收回第三方网站赋予的权力
- OAuth在"客户端"与"服务提供商"之间,设置了一个授权层。“客户端"不能直接登录"服务提供商”,只能登录授权层,获取所用的令牌(token),"服务提供商"根据令牌的权限范围和有效期,向"客户端"开放用户储存的资料。
授权码模式(authorization code 最常用)
- 创建应用:A网站开发者首先去诸如淘宝开放平台创建应用,开放平台会生成一个client_id作为A网站唯一标识
- 跳转授权页:用户在A网站点击使用淘宝账号登陆时,实际上跳转至淘宝提供的授权页,会带上 client_id 和 redirect_uri 等参数
- 重定向:用户在授权页面输入淘宝用户名和密码,校验成功后跳转至A网站 redirect_uri 回调地址,地址上会拼接授权码 code
- A网拿到授权码 code 后访问授权服务器,用授权码 code 去换访问口令 access_token 和 过期更新口令 refresh_token
- refresh_token的时效性比access_token长,当access_token过期时,可以使用refresh_token换取新的access_token
// access_token 示例
{
"access_token":"2YotnFZFEjr1zCsicMWpAA",
"token_type":"example",
"expires_in":3600,
"refresh_token":"tGzv3JOkF0XG5Qx2TlKWIA",
"example_parameter":"example_value"
}
-
完成授权:后续后端请求用户的信息通过 access_token 来获取
-
先换取code,再根据 code 换取 access_token原因
- 避免 access_token 暴露:access_token 的有效期都比较长,一般为 1~2 个小时。如果泄露会对用户造成一定影响。并且就算 code 暴露,后端收到这个 code 之后,需要使用 Client Id + Client Secret + Code 去授权服务器换取用户的 access_token,而 Client Id + Client Secret 只有业务方自己知道(在诸如淘宝开放平台创建应用的时候返回的)
- code 一次有效,用后作废,就算攻击者拿到了所有信息也要和应用进行竞争
- code 有效期短,超期未使用需要重新按授权流程获取,进一步降低风险
-
使用 JWT 格式的 acess_token
- 资源服务器(提供用户资料的服务器)需要远程调用授权服务器 check_token 端点校验令牌是否有效,这样比较消耗性能。
- 如果资源服务器和授权服务器约定一个密钥对,授权服务器用秘钥加密令牌,当资源服务器接收到令牌时进行解密直接对令牌进行校验,这样可以节省远程交互。SON Web Token(JWT)可以解决该问题
-
JWT介绍
-
JWT 由三部分构成 header、payload、signature
-
头部(Header)
- 通常由令牌的类型 typ 和所使用的签名算法 alg 组成并使用 Base64URL 进行编码组成 JWT 结构的第一部分
{
"alg": "HS256", // 签名算法
"typ": "jwt" // 令牌类型
}
- 载荷(Payload)
- 除了设置过期时间 exp 和用户的 uid ,还可以添加自定义私有字段,使用 Base64URL 进行编码组成 JWT 结构的第二部分
{
"exp": Math.floor(Date.now() / 1000 + 60 * 60), // 设置有效期为 1 个小时
"uid": "188888",
// ... 自定义字段
}
- 签名(Signature)
- 在服务端设置一个私钥 secret 用于加密解密,使用 Header 指定的算法 HS256 对头部 Header 和 载荷 Payload 进行加密生成签名用于防止篡改
- 最后生成 Token
- 把 Header、Payload、Signature 拼成一个用 英文句号 分隔的字符串,即为 Token
简化模式(implicit)
- 用户在授权页面输入淘宝用户名和密码,校验成功后跳转至A网站 redirect_uri 回调地址,会直接带上 access_token 而不是授权码 code
- access_token 的位置也不同,会作为 hash 部分,因为浏览器跳转时,锚点不会发到服务器,就减少了泄漏令牌的风险。
回调地址#token=xxxxxx
密码模式(resource owner password credentials)
- 如果高度信任某个应用,也允许用户把用户名和密码,直接告诉该应用。该应用就使用你的密码,申请令牌 access_token
客户端模式(client credentials)
- 用户在客户端注册,客户端以自己的名义而不是用户的名义去向服务的提供商去做一个认证。
OpenID Connect
- OAuth2.0只有授权流程,返回令牌之后授权流程已经完成,OpenID connect 在此基础上进行了扩展,客户端能够通过认证来识别用户
- 客户端在获取到令牌之后,还需要调用资源服务器接口获取用户信息,OpenID Connect 可以在返回令牌时同时将用户是谁(ID token)
// OpenID Connect 还会返回 id_token 来证明用户身份
{
"access_token": "1fa67a8b-12a9-4862-b9ef-983d6554cd19",
"token_type": "bearer",
"refresh_token": "e0a417a9-33cc-4034-84c1-d09dc80a8c31",
"expires_in":3599,
"id_token":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJkYXRhIjpbeyJuYW1lIjoidGVzdF9uYW1lIn0seyJlbWFpbCI6InRlc3RfZW1haWwifV0sImlhdCI6MTY0NzE4MTc0NywiZXhwIjoxNjQ3MzU5OTk5LCJhdWQiOiJ0ZXN0MiIsImlzcyI6InRlc3QxIiwic3ViIjoidGVzdDMifQ.k0dknW7ff4KyMIMbBmnZtI3nf2lWN9KVKQrGwcyOGYI"
}
-
id_token
- id_token 以 JWT 格式存在并由授权服务器进行签名,其中包含了用户的一些基本信息,客户端可以解密获取,同时 OIDC 协议要求提供一个 UserInfo endpoint 端点,如果需要获取用户详细信息,那么需要通过 access_token 调用 UserInfo endpoint 端点进行查询