1 认证(Authentication)
认证是指应用软件(身份信息使用方)通过采用某种方法来确认当前请求的用户是谁。基于密码的认证过程可以细分为三步:
(1)认证服务器(身份信息提供方)从客户端获取用户账密。
(2)认证服务器将拿到的账密与数据库中保存的账密进行比较,确认正确后,生成用户身份信息。
(3)使用方从提供方处获取用户身份信息。
- 当提供方与使用方能够共享数据库,不必跨网络和安全边界进行交互时,两个角色就合并了,完成前两步就能确认当前请求的用户是谁,所以只需考虑一个问题:【Q1】按照什么流程、格式能够安全可靠地把用户账密从客户端传递给认证服务器。
- 当提供方与使用方独立部署,必须跨网络和安全边界进行交互时,三步都完成后使用方才能确认当前请求的用户是谁,因此除了【Q1】之外,还有一个问题需要考虑:【Q2】按照什么流程、格式能够安全可靠地把用户身份信息从提供方传递给使用方。
所有能让使用方确认当前用户是谁的方法都可以统称为认证机制,应用软件根据其部署情况、所处角色、面临的问题来具体选择。
- 【Q1】的解决方案为:表单认证、HTTP Basic认证、HTTP Digest认证、HTTP Mutual认证。这些就是Web应用中常见的基于密码的身份认证机制。
- 【Q2】的解决方案为:OIDC、SAML 、WS-Federation、Windows AD。这些统称为联合身份认证机制(Federated Authentication),通常用于实现SSO、第三方登录。
2 授权(Authorization)
授权是指应用软件(资源提供方)通过采用某种方法来确认用户允许第三方应用(资源使用方)对他的保护资源做哪些操作。
由于资源提供方与资源使用方是两个独立部署的应用,必须跨网络和安全边界进行交互,则需考虑的问题是:
【Q3】按照什么流程、格式能够安全可靠地把用户授权决策从资源使用方传递给资源提供方。解决方案是OAuth 2.0。
OAuth 2.0是授权协议,它规定了一套传递用户授权决策的标准流程、格式。采用该流程的优点在于用户在享受第三方应用替自己操作的便利时,不必与其共享自己的账号密码,第三方应用使用的是一个短期有效的访问令牌,并且用户能够控制令牌权限范围,以及随时能够让令牌失效。具体流程是:用户告知授权服务器(QQ用户中心),自己允许客户机应用(某第三方在线PS应用)在其同意的权限范围内(读取某个指定相册),访问自己保存在资源服务器(QQ空间)中的数据资源(照片)。显然,授权服务器(QQ用户中心)必须先认证用户的身份,才会发放访问令牌给客户机应用(PS应用),客户机应用凭借此访问令牌就能从资源服务器(QQ空间)上访问用户数据资源(读取指定相册的照片)。
OAuth 2.0术语
Resource Owner:资源拥有者,即用户。
Client:客户机应用,用户数据资源的使用方,凭借授权凭证访问用户存储在资源服务器上的特定数据资源。
AS:Authentication Server,授权服务器,有能力对用户进行认证、发放访问令牌、并且被资源服务器信任的一方。
RS:Resource Server,资源服务器,用户数据资源的提供方。
3 OAuth 2.0与认证机制的联系
(1)OAuth 2.0规定了授权服务器必须要对用户进行认证,但它只是授权协议,它不关心授权服务器如何完成认证(【Q1】),授权服务器自行选择认证机制。授权服务器可以在表单认证、Basic、Digest、Mutual等密码认证机制中进行选择,也可以选择非密码的认证机制,还可以将两者结合起来使用。
(2)从实现的角度讲,OAuth 2.0能够作为一种联合身份认证机制来使用,但是不适合,会导致一些问题。
由于:
- OAuth 2.0授权流程比较安全可靠
- 授权服务器这个角色有能力且必须要对用户进行认证,看起来很适合作为用户身份信息的提供方
- 有时候客户机应用也需要使用用户身份信息
于是有人就想,能不能利用OAuth 2.0的授权流程把用户身份信息从授权服务器传递给客户机应用?即利用OAuth 2.0解决【Q2】,把它作为一种联合身份认证机制来使用。
能,Github就是这样做的,它利用OAuth 2.0向客户机应用同时提供用户认证、授权两项服务。但是,OAuth 2.0的设计本意是用来做授权的,直接拿来做用户认证会导致一些问题。虽然提供方(Github)能够通过自己做额外设计来解决这些问题,但是当每个提供方采用的额外设计不一致时,整个流程就不再统一规范了,这会给使用方带来不必要的负担。于是就产生了OIDC这个协议,它规定了一套标准的额外设计。
OIDC(OpenID Connect)是一个身份认证协议,它规定了一套把用户身份信息从授权服务器(身份提供方)传递给客户机应用(身份使用方)的标准流程、格式。
详解基于OIDC实现单点登录SSO、第三方登录
4 详解OAuth 2.0授权协议
核心规范标准文件:【RFC 6749】The OAuth 2.0 Authorization Framework
RFC 6749 定义了一个授权框架,允许第三方应用通过指定方式获取短期有效的访问令牌,代替用户密码,在令牌权限范围内访问资源服务器上的特定保护资源,从而用户无需与第三方应用共享用户名和密码。OAuth2.0被设计为基于HTTP使用。
OAuth 2.0提供了四种具体的授权流程: 授权码流程(authorization code),隐式许可流程(implicit),用户密码流程(resource owner password credentials)和客户机凭据流程(client credentials),同时也支持扩展。
其中,授权码流程(authorization code)最常见,安全性也最高,适用有后端的 Web 应用。授权码通过前端传送,令牌储存在后端,而且所有与资源服务器的通信都在后端完成,可以避免令牌泄漏。
OAuth 2.0术语
Redirection Endpoint:重定向接口, 由客户机应用提供。
Authorization Endpoint:授权接口,由授权服务器提供。
Token Endpoint:令牌接口,由授权服务器提供。
中文版总结
4.1 授权码流程
1、客户机应用向授权服务器的授权接口GET https://www.amazon.com/ap/oa
发送申请:
GET https://www.amazon.com/ap/oa
?response_type=code
&redirect_uri=http://localhost/oauth_callback
&scope=profile
&state=c7HBU6Sb1nAcWELJx6l2aUsQfnwIl0EW
&client_id=9c21477eb0a5e2191342
传参:
- response_type:必选,希望授权服务器采用哪种Oauth 2.0流程来响应,code代表授权码流程。
- redirect_uri:必选,授权服务器回传授权码等信息给客户机应用的重定向接口,通常要与客户机应用注册时提供的信息保持一致。
- scope:必选,申请获取的资源权限。
- client_id:必选,客户机应用向授权服务器注册的client_id。
- state:推荐,不透明字符串,当授权服务器重定向到redirect_uri时,会原样返回给客户机应用,用于防止跨站请求伪造攻击(CSRF、 XSRF)。由于授权服务器会原样返回此参数,可将state值与用户在客户机应用最后浏览的URI绑定,便于授权完成后将用户重定向回最后浏览的页面。
2、授权服务器校验收到的传参response_type、redirect_uri、scope、client_id:
(1)如果校验失败,则返回RFC 6749规定的错误响应。
(2)如果校验通过,则要求用户输入账密,对其身份进行认证(授权服务器自行选择认证机制,OAuth 2.0不指定),用户选择是否同意对客户机应用授权,并指定允许其获取哪些资源权限,此权限范围可以大于客户机应用申请的权限,授权服务器最终只会对客户机应用批准其申请的权限。
3、授权服务器签发授权码,通过浏览器重定向到客户机应用提供的重定向接口http://localhost/oauth_callback
(即步骤1的传参redirect_uri),即返回响应:
HTTP/1.1 302 Found
Location: http://localhost/oauth_callback
?code=SplxlOBeZQQYbYS6WxSbIA
&state=c7HBU6Sb1nAcWELJx6l2aUsQfnwIl0EW
传参:
- code:必选,授权服务器签发给客户机应用的授权码。
- state:如果客户机应用在步骤1中提供了state参数,则授权服务器需原样返回,客户机应用没提供就不返回此参数。
4、客户机应用使用收到的授权码在后台向授权服务器的令牌接口POST https://api.amazon.com/auth/o2/token
请求访问令牌。发出请求时,客户机应用需提供其在授权服务器注册的client_id、client_secret,从而使授权服务器能对客户机应用的身份进行认证(OAuth 2.0列举了两种认证机制:Basic认证、POST请求体传参,但是不建议使用后者,允许授权服务器自行选择其他认证机制),即发送以下请求:
POST https://api.amazon.com/auth/o2/token
Authorization: Basic czZCaGRSa3F0MzpnWDFmQmF0M2JW
Content-Type: application/x-www-form-urlencoded
grant_type=authorization_code&code=SplxlOBeZQQYbYS6WxSbIA&redirect_uri=http://localhost/oauth_callback
传参:
- grant_type:必选,客户机应用提供的凭证类型,authorization_code代表提供的是授权码。
- code:必选,授权服务器在步骤3中回传的授权码。
- redirect_uri:必选,客户机应用在步骤1中提供给授权服务器的redirect_uri。
5、授权服务器对客户机应用的身份进行认证,并校验授权码:
(1)如果校验失败,则返回RFC 6749规定的错误响应。
(2)如果校验通过,则签发access_token,即返回响应:
HTTP/1.1 200 OK
Content-Type: application/json;charset=UTF-8
Cache-Control: no-store
Pragma: no-cache
{
"access_token":"2YotnFZFEjr1zCsicMWpAA",
"token_type":"bearer",
"expires_in":3600,
"scope":"profile",
"refresh_token":"tGzv3JOkF0XG5Qx2TlKWIA"
}
传参:
- access_token:必选,授权服务器签发给客户机应用的短期有效的访问令牌。
- token_type:必选,令牌类型,详见下文access token的具体用法(Bearer token)。
- expires_in:推荐,令牌有效期,单位为秒,3600秒即1小时。
- scope:授权服务器批准的资源权限,如果与客户机应用申请的权限不同(即批准权限小于申请权限),则必须返回此参数,如果相同,可以不用返回。
- refresh_token:可选,授权服务器签发给客户机应用的较长有效期的更新令牌。由于access_token有效期很短,到期后客户机应用可以使用refresh_token向授权服务器申请新的access_token,从而避免重复找用户要授权。
6、客户机应用使用access token获取资源服务器上的特定保护资源(详见access token的具体用法)。
4.2 如何使用refresh_token换发新的access_token?
1、在授权服务器签发了refresh_token的情况下,如果客户机应用发现access_token到期,或者权限不足,可以使用refresh_token向授权服务器的令牌接口POST https://api.amazon.com/auth/o2/token
请求新的access_token。发出请求时,客户机应用同样需提供其在授权服务器注册的client_id、client_secret,从而使授权服务器能对客户机应用的身份进行认证。
POST https://api.amazon.com/auth/o2/token
Authorization: Basic czZCaGRSa3F0MzpnWDFmQmF0M2JW
Content-Type: application/x-www-form-urlencoded
grant_type=refresh_token&refresh_token=tGzv3JOkF0XG5Qx2TlKWIA
传参:
- grant_type:必选,客户机应用提供的凭证类型,refresh_token代表提供的是更新令牌。
- refresh_token:必选,授权服务器在授权码流程步骤5中回传的refresh_token。
- scope:可选,当客户机应用要申请有更多资源权限的access_token时,使用此参数。如果只是更新已过期的access_token,则不传。
2、授权服务器对客户机应用的身份进行认证,并校验refresh_token:
(1)如果校验失败,则返回RFC 6749规定的错误响应。
(2)如果校验通过,且客户机应用只是要更新已过期的access_token,则签发新的access_token,返回响应与授权码流程步骤5的响应相同。
(3)如果校验通过,且客户机应用要申请有更多资源权限的access_token,则检查其申请的权限是否在用户已指定的权限范围内(用户在授权码流程步骤2中指定的)。如果在此范围内,则签发新的access_token,返回响应与授权码流程步骤5的响应相同。如果超出了此范围,则询问用户是否同意授权,即重复授权码流程步骤2。
4.3 access token的具体用法(Bearer token)
补充规范标准文件:【RFC 6750】The OAuth 2.0 Authorization Framework: Bearer Token Usage
OAuth 2.0通过补充规范 RFC 6750 示范了一种access token的具体用法,符合这种具体用法的access token统称为Bearer token。注意:Bearer token不是一种token值的格式,而是一种规范的用法,OAuth 2.0没有规定token值的内容、格式。
1、客户机应用在访问特定保护资源时(目标URI:https://api.amazon.com/user/profile
),把access token传递给资源服务器,具体有三种传递方式(详见下文),最常用的是在请求头Authorization
字段中使用Bearer
这一关键字传递,例如:
GET https://api.amazon.com/user/profile
Authorization: Bearer 2YotnFZFEjr1zCsicMWpAA
2、资源服务器校验access token,例如检查其值是否合法、是否在有效期内、是否有权限访问目标URI等。
(1)如果校验通过,则返回保护资源作为响应。
(2)如果校验失败,则返回RFC 6750规定的错误响应。
4.4 客户机应用如何把access token传递给资源服务器?
OAuth 2.0在补充规范RFC 6750 中定义了三种传递access token的方式。
1、放在请求头中传递:在请求头Authorization
字段中使用Bearer
这一关键字传递。资源服务器必须支持这种传递方式。
GET https://api.amazon.com/user/profile
Authorization: Bearer 2YotnFZFEjr1zCsicMWpAA
2、放在表单请求体中传递:资源服务器自行选择是否支持这种传递方式。
POST https://api.amazon.com/user/profile
Content-Type: application/x-www-form-urlencoded
access_token=2YotnFZFEjr1zCsicMWpAA
3、放在URI的查询参数中传递(不建议):RFC 6750 不建议采用这种方式,如果实在无法使用前两种方式,再来考虑这种。资源服务器自行选择是否支持这种传递方式。
GET https://api.amazon.com/user/profile?access_token=2YotnFZFEjr1zCsicMWpAA
Cache-Control: no-store
4.5 access token的值是什么?
OAuth 2.0没有规定access token值的内容、格式,只要求access token对客户机应用不透明。实操中常见的格式分为两类:
1、读库令牌:access token值是一个随机生成的标识符,在数据库中存储有效期,谁颁发的,颁发给谁的,权限范围等元数据信息。校验时需先读库,优点是当授权服务器想主动撤销已签发的access token时非常方便。
2、自包含令牌(例如JWT):access token值是把上述元数据信息编码、签名、加密后得到的值,优点是校验时无需读库,缺点是当授权服务器想主动撤销已签发的access token时会比较棘手。
4.6 JWT
JSON WEB TOKEN,是目前比较流行的自包含令牌格式。其优点在于:
- 基于json的通用性,可以跨语言支持。
- 简单紧凑,字节占用小,能够放在 HTTP 报头或URI的查询参数中进行传输。
- 自包含并提供防篡改机制。
标准文件:
【RFC7519】JSON Web Token (JWT)
【RFC7515】JSON Web Signature (JWS)
【RFC7516】JSON Web Encryption (JWE)
【RFC7517】JSON Web Key (JWK)
【RFC7518】JSON Web Algorithms (JWA)
4.7 资源服务器如何校验access token?
OAuth 2.0没有规定资源服务器如何校验access token,只是说资源服务器与授权服务器之间自己协调。实操中一般采用以下方法校验:
- 对于小型Web应用:资源服务器通常与授权服务器同为一体,自然能够通过读库来校验。
- 对于大型Web应用:授权服务器和资源服务器通常是独立部署的,有三种方式校验:
1、使用读库令牌作为access token,在数据库存储层面做共享,使得资源服务器能够通过读库来校验。
2、使用读库令牌作为access token,由授权服务器提供一个令牌校验接口,资源服务器请求该接口来校验。OAuth2.0在补充规范RFC 7662 中定义了令牌校验接口(Introspection Endpoint)的相关标准。
3、使用自包含令牌作为access token,资源服务器和授权服务器双方约定好自包含令牌的结构、签名密钥、加密方法,资源服务器按照约定规则自行校验。
补充规范标准文件:【RFC 7662】OAuth 2.0 Token Introspection
4.8 客户机应用如何申请撤销token?
补充规范标准文件:【RFC 7009】OAuth 2.0 Token Revocation
OAuth 2.0在补充规范RFC 7009中定义了一个由授权服务器提供的撤销接口(Revocation Endpoint)来供客户机应用申请撤销access_token或refresh_token。
当用户在客户机应用退出登录、更换账号、注销账号,或卸载了客户机应用时,客户机应用需要通知授权服务器自己不再需要该用户的令牌,授权服务器将清除与该令牌相关的授权信息。 这样可以防止被遗弃令牌的滥用,并改善用户体验,因为失效的授权将不再出现在授权服务器展示给用户的已授权客户机应用列表中。
4.9 授权服务器如何令已签发的access token失效?
在以下情景中,授权服务器可能需要撤销客户机应用对用户资源的访问权限,即令已签发的access token失效:
- 如上述,客户机应用申请撤销某用户的令牌。
- 用户明确要求取消对某客户机应用的授权。
- 客户机应用开发人员下架了他们的应用。
- 授权服务器已确定某个客户机应用被攻击了或本身是恶意的,要禁用它。
如果使用读库令牌作为access token,授权服务器在数据库中直接删除即可。
如果使用自包含令牌作为access token,则比较棘手。当资源服务器只依赖自包含令牌提供的信息来校验access_token时(真正的无状态令牌验证机制),授权服务器无法影响资源服务器对access_token的校验,只能删除自己数据库中的refresh_token,当客户机应用使用此refresh_token申请新的access_token时,授权服务器将不予签发,而对于之前已签发的access_token,只能等待其自行到期。因此,自包含令牌的access_token通常有效期极短。
参考文档
- [认证 & 授权] 1. OAuth2授权
- [认证 & 授权] 2. OAuth2授权(续) & JWT(JSON Web Token)
- [认证 & 授权] 3. 基于OAuth2的认证(译)
- OAuth 2.0 系列文
- 【RFC 6749】The OAuth 2.0 Authorization Framework
- 【RFC 6750】The OAuth 2.0 Authorization Framework: Bearer Token Usage
- 【RFC 7662】OAuth 2.0 Token Introspection
- 【RFC 7009】OAuth 2.0 Token Revocation
- 认证与授权——单点登录协议盘点:OpenID vs OAuth2 vs SAML
- 浅谈SAML, OAuth, OpenID和SSO, JWT和Session
- BearerToken之JWT的介绍
- JSON Web Token 入门教程
- 【RFC7519】JSON Web Token (JWT)
- 【RFC7515】JSON Web Signature (JWS)
- 【RFC7516】JSON Web Encryption (JWE)
- 【RFC7517】JSON Web Key (JWK)
- 【RFC7518】JSON Web Algorithms (JWA)
- Web应用中基于密码的身份认证机制(表单认证、HTTP认证: Basic、Digest、Mutual)
- 基于OIDC实现单点登录SSO、第三方登录