概述
OAuth2机制详解已经从原理和机制层面对OAuth2进行了一些说明,想必已经能够了解到OAuth2的整体流程以及存在的意义。本篇文章将围绕OAuth2的实现方式,来看一看OAuth2这样一个概念是如何落地到日常开发中的。OAuth2规定了四种获得令牌的流程,在实际开发中,可以根据需要选择合适的方式,向第三方应用颁发令牌,实现和第三方的梦幻联动。本文将以腾讯用户授权给百度,使得腾讯用户能够使用qq号在百度登录为例,描述认证和授权过程。
在认证和授权的过程中涉及到三方:
- 服务提供方,也就是用户资源(例如好友信息、身份信息等)存储方,可以理解为腾讯方;
- 用户,也就是qq用户;
- 客户端,要访问服务提供方资源的第三方应用,可以理解为百度方。
在认证过程开始前,客户端和服务提供方是需要达成合作协议的,也就是百度需要向腾讯申请客户端标识(CLIENT_ID和CLIENT_SECRETE),告知腾讯自己是一个合法的应用。CLIENT_ID用于标识当前认证行为是来自哪一个第三方,确定当前认证行为是百度发起的而不是美团或者拼多多发起的,也可以用于信息溯源;CLIENT_SECRETE用于验证进行当前认证行为的百度是否是合法的第三方,避免被什么“白度”、“千度”冒充。
简单来说,获得令牌的流程有如下四种模式:
- 授权码模式(authorization code)
- 简化模式(implicit)
- 密码模式(resource owner password credentials)
- 客户端模式(client credentials)
授权码模式
授权码模式是最常用的流程,且安全性高,授权码获取的过程是通过前端请求完成的,而获取令牌信息的过程是在后端请求完成的,因此令牌最终是存储在后端db中的,百度向腾讯请求授权的过程也是通过这种模式实现的。该模式的具体流程如下:
- 百度提供一个第三方登录按钮,用户点击后会重定向到腾讯的授权页面。
观察url不难发现其中包含着几个重要参数:
- response_type=code
- client_id=CLIENT_ID
- redirect_uri=CALLBACK_URL
- state=123456
- scope=all
以上参数中,response_type
表示要求返回的是授权码(code),client_id
是让腾讯知道到底是哪个应用在请求授权,redirect_uri
是回调地址,state
用于让百度标识当前请求授权是哪个用户发起的,scope
表示当前授权的范围。
- 用户点击“授权并登录”后,腾讯接收到授权请求,会请求回调地址
redirect_uri
,并在请求参数中加上如下参数:
- code=AUTHORIZATION_CODE
- state=123456
其中code
是腾讯返回给百度的授权码,代表着百度可以通过这个授权码获取到最终的令牌信息,state
用于让百度对应上到底是哪个用户获得了这个授权码。
- 百度拿到授权码后,会再向腾讯发送请求,获取最终的令牌信息,请求参数包含如下几个部分:
- client_id=CLIENT_ID
- client_secret=CLIENT_SECRET
- code=AUTHORIZATION_CODE
- grant_type=authorization_code
- redirect_uri=CALLBACK_URL
client_id
和redirect_uri
无需再次解释,client_secrete
用于确认当前client的身份真伪,code
是上一步骤获取到的授权码,grant_type
代表着此次授权请求的类型,authorization_code意为此次请求的是令牌信息,还可能是refresh_token,用于刷新令牌。
腾讯收到请求后,会返回给百度最终的令牌,百度会将这个令牌信息通过state参数在db里面与用户进行绑定,完成整个授权过程,图示如下:
简化模式
在理解了授权码模式之后,简化模式就很容易理解了。授权码模式其实是通过三次握手保障了授权过程的安全性。而简化模式就略显随意了,它把请求授权码的过程给隐藏掉了,直接对令牌进行请求。
由于将授权码过程简化掉了,而授权过程必然是由前端触发的,请求令牌的过程也就转移到了前端,因此该模式的令牌是颁发给前端的,无需后端参与,这就导致该模式并不安全。通过该模式颁发的令牌,一般有效期非常短,授权过程仅存在于单次会话。
整个过程包含如下步骤:
- 百度提供一个第三方登录按钮,用户点击后会重定向到腾讯的授权页面。uri中的参数与
授权码模式
大同小异,因为不涉及到后端存储,因此不再需要state
参数,并且response_type
的类型应为token
,直接返回令牌; - 用户点击“授权并登录”按钮,腾讯接收到授权请求,会请求回调地址
redirect_uri
,并将令牌信息返回给百度,完成授权过程。
图示如下:
密码模式
密码模式的主要流程如下:
- 百度要求用户提供qq号的用户名和密码;
- 百度拿到用户名密码后,向腾讯发送请求获取令牌信息;
- 腾讯收到请求,对用户名和密码进行了验证,直接将令牌塞在response的boy中返回给百度。
根据以上流程,可以很清楚地感受到十分不安全。第三方应用获取到用户的用户名和密码后,只需要发起一次http请求即可获取到令牌,无需任何跳转。第三方应用完全可以为所欲为,甚至不需要请求令牌就能够获取用户的所有权,获取令牌的过程看似只是走个过场。因此这种模式很少使用,必须是用户高度信任的应用。
客户端模式
客户端的主要流程如下:
- 百度在后端向腾讯发起申请授权的请求,请求参数包含
client_id
和client_secrete
,用于确认发起方的身份。 - 腾讯收到请求后,验证该客户端的真实性,直接返回令牌。
其实可以看出,客户端模式和密码模式是有点相似的,都是通过一次请求获取了令牌。但是密码模式传递的参数是用户名和密码,而客户端模式传递的参数是客户端id和密钥,说明授权的主体是不一样的,通过客户端模式给出的令牌,是应用级别而不是用户级别的,可能多个用户共享同一个令牌。
其它内容
令牌使用
通过以上四种方式之一拿到令牌后,接下来在访问需要鉴权的接口时,在请求头headers里面加上Authorization
字段即可,一般鉴权相关的参数都会放在headers中进行传输。如果在服务器上,可以使用curl指令进行接口调用:
curl -H “Authorization: Bearer ACCESS_TOKEN” “https://xxx.com”
令牌更新
以上描述的都是请求令牌的过程,一般而言,令牌其实是有expire_time过期时间的,在请求过程中就会带上这个参数。超过过期时间后,该令牌就会自动失效。因此需要存在令牌刷的操作,具体的接口地址视业务而定,但是请求时所带的参数如下:
- grant_type=refresh_token
- client_id=CLIENT_ID
- client_secret=CLIENT_SECRET
- refresh_token=REFRESH_TOKEN
整体而言,令牌刷新接口的参数与请求令牌接口的参数没有很大区别。在获取令牌时,除了返回access_token以外,一般还会返回refresh_token和expire_time,refresh_token便是用于刷新令牌的token,能够延长令牌的有效期。