oauth2.0–基础–01–理论
1、快递员问题
1.1、问题描述
我经常点外卖,每天都有外卖员来送餐。我必须找到一个办法,让快递员通过门禁系统,进入小区。
如果我把自己的密码,告诉快递员,他就拥有了与我同样的权限,就可以自由的出入小区,这对小区的安全造成影响。
你可以会想,每次收到外卖就改一次密码,这样就可以让外卖员拥有进入小区的权力,但是这很麻烦,因为第一,我要自己去修改密码,第二,我还得把最新的密码通知我的家人和朋友。那么有没有办法,既可以让外卖人员自由进入小区,而又不必修改密码呢。比如说一个临时凭证
1.2、需求描述
- 每次外卖人员来送餐,我希望给外卖人员一个凭证,而非密码。
- 凭证需要有个过期时间,防止外卖人员一直持有这个凭证。
- 凭证只能到A栋楼2层201房间门口,不能去其他楼,同时也不能去A栋楼的其他楼层。
1.3、设计凭证
-
门禁系统的密码输入器下面,增加一个按钮,叫做"获取授权"。快递员去申请授权。
-
申请前,我和快递员做了一个约定,每次申请的时候,需要把快递员工号发给我,同时我这里记录下这个工号。
-
他按下按钮以后,屋主(也就是我)的手机就会跳出对话框,有人正在要求授权。系统还会显示该快递员的姓名、工号和所属的快递公司。我拿出我记录的工号和系统的工号对比,只有相等,我才认为这个快递员是我授权的。
-
我确认请求属实,就点击按钮,告诉门禁系统,我同意给予他进入小区的授权。
-
门禁系统得到我的确认以后,向快递员显示一个进入小区的令牌(access token)。只在短期内(比如七天)有效。且只能去我所在的那层楼。
-
快递员向门禁系统输入令牌,进入小区。
2、oauth2.0是为了解决什么样的问题
2.1、场景
我们经常遇到这种情况,假设有个网址W1,可以将用户储存在W2的照片,冲印出来,用户U1使用该功能,必须让W1读取自己储存在W2上的照片,这个时候应该怎么办呢?
2.2、传统的做法:
将W2的账号密码告诉W1,但是这样做有以下缺陷
1. W1可能保存W2的账号密码,不安全
2. W1拥有了获取用户储存在W2所有资料的权力,用户没法限制W1获得授权的范围和有效期。
3. 用户只有修改密码,才能收回赋予W1的权力。但是这样做,会使得其他所有获得用户授权的第三方应用程序全部失效。
4. 只要有一个第三方应用程序被破解,就会导致用户密码泄漏,以及所有被密码保护的其他第三方数据泄漏。
2.3、现在的做法
使用oauth2.0 来解决上面的问题。
3、专用名词
3.1、授权过程名词
- client:第三方应用程序或者客户端,即W1
- HTTP service: 服务提供商,即W2
- Resource Owner:资源所有者,即U1
- User Agent:用户代理,本文中就是指浏览器。
- Authorization server:认证服务器
- 服务提供商处理认证的服务器。
- Resource server:资源服务器
- 服务提供商存放资源的服务器
- 一般它与认证服务器分开
- clinet Identifier:客户端标识符,即W1向W2申请的clinet Id
- redicrection ulr:重定向的url
- user authenticates:用戶 确定 授权
- authorization code:授权码
- access token:访问令牌
- optional refresh token:可选的刷新令牌
3.2、授权认证参数说明
- code:授权码
- 授权码。
- 有效期应该很短,通常设为10分钟,客户端只能使用该码一次,否则会被授权服务器拒绝。
- code与客户端ID和重定向URI,是一一对应关系。
- state:户端的当前状态
- 如果客户端的请求中包含这个参数,认证服务器的回应也必须包含这个参数。
- response_type:授权类型,具体看服务器定义
- client_id:客户端的ID
- redirect_uri:表示重定向URI
- scope:申请的权限范围
- grant_type:使用的授权模式
- access_token:访问令牌
- token_type:令牌类型,大小写不敏感,可以是bearer类型或mac类型。
- expires_in:过期时间,单位为秒。
- refresh_token:更新令牌,用来获取下一次的访问令牌。
- scope:表示权限范围,如果与客户端申请的范围一致,此项可省略。
- password:密码
- username:用户名
- granttype:授权类型,具体看服务器定义
- refresh_token:更新令牌
4、授权模式
- 授权码模式:authorization code
- 简化模式: implicit
- 密码模式: resource owner password credentials
- 客户端模式: client credentials
4.1、 授权码模式:authorization code
功能最完整、流程最严密的授权模式。
步骤如下
(A) 用户访问客户端,客户端将用户导向认证服务器。
(B) 用户选择是否给予客户端授权。
(C) 假设用户给予授权,认证服务器将用户导向客户端事先指定的"重定向URI"(redirection URI),同时附上一个授权码。
(D) 客户端收到授权码,附上早先的"重定向URI",向认证服务器申请令牌。在客户端的后台的服务器上完成的,对用户不可见。
(E) 认证服务器核对了授权码和重定向URI,确认无误后,向客户端发送访问令牌(access token)和更新令牌(refresh token)。
A步骤中,客户端申请认证的URI,包含以下参数
GET /authorize?
response_type=code&
client_id=1111&
state=2222&
redirect_uri=https://www.baidu.com
- response_type=code:授权类型,这里表示授权码模式
- client_id:客户端id为1111
- 客户端状态:2222
- redirect_uri:重定向地址为https://www.baidu.com
C步骤中,服务器回应客户端的URI,包含以下参数:
https://www.baidu.com?code=xxxxx&state=2222
- code:授权码
- https://www.baidu.com:重定向地址
D步骤中,客户端向认证服务器申请令牌的HTTP请求,包含以下参数:
...?grant_type=authorization_code&
code=xxxxx&
redirect_uri=https://www.baidu.com&
clientId=1111
- grant_type:表示使用的授权模式
E步骤中,认证服务器发送的HTTP回复,包含以下参数:
{
"access_token":"2YotnFZFEjr1zCsicMWpAA",
"token_type":"example",
"expires_in":3600,
"refresh_token":"tGzv3JOkF0XG5Qx2TlKWIA",
"example_parameter":"example_value"
}
- access_token:访问令牌
- token_type:令牌类型,大小写不敏感,可以是bearer类型或mac类型。
- expires_in:过期时间,单位为秒。
- refresh_token:更新令牌,用来获取下一次的访问令牌。
- scope:表示权限范围,如果与客户端申请的范围一致,此项可省略。
4.2、简化模式
- 不通过第三方应用程序的服务器,直接在浏览器中向认证服务器申请令牌,跳过了"授权码"这个步骤。
- 所有步骤在浏览器中完成,令牌对访问者是可见的
- 客户端不需要认证。
步骤如下
(A) 客户端将用户导向认证服务器。
(B) 用户决定是否给于客户端授权。
(C) 假设用户给予授权,认证服务器将用户导向客户端指定的"重定向URI",并在URI的Hash部分包含了访问令牌。
(D) 浏览器向资源服务器发出请求,其中不包括上一步收到的Hash值。
(E) 资源服务器返回一个网页,其中包含的代码可以获取Hash值中的令牌。
(F) 浏览器执行上一步获得的脚本,提取出令牌。
(G) 浏览器将令牌发给客户端。
A步骤中,客户端发出的HTTP请求,包含以下参数:
GET /authorize?
response_type=token&
client_id=1111&
state=2222&
redirect_uri=https://www.baidu.com
C步骤中,认证服务器回应客户端的URI,包含以下参数:
....#access_token=2YotnFZFEjr1zCsicMWpAA&
state=2222&
token_type=example&
expires_in=3600
- 参数前面是#
4.3、密码模式
- 用户向客户端提供自己的用户名和密码。客户端使用这些信息,向"服务商提供商"索要授权。
- 通常用在用户对客户端高度信任的情况下,才能考虑使用这种模式。
- 使用场景:微服务集群中单点登陆问题
步骤如下
(A) 用户向客户端提供用户名和密码。
(B) 客户端将用户名和密码发给认证服务器,向后者请求令牌。
(C) 认证服务器确认无误后,向客户端提供访问令牌。
B步骤中,客户端发出的HTTP请求,包含以下参数
POST /grant_type=password&
username=1&
password=2
- grant_type:授权类型,此处的值固定为"password",必选项。
- username:用户名,必选项。
- password:用户的密码,必选项。
- scope:权限范围,可选项。
C步骤中,认证服务器向客户端发送访问令牌,下面是一个例子。
{
"access_token":"2YotnFZFEjr1zCsicMWpAA",
"token_type":"example",
"expires_in":3600,
"refresh_token":"tGzv3JOkF0XG5Qx2TlKWIA",
"example_parameter":"example_value"
}
4.4、客户端模式
- 以客户端的名义,向"服务提供商"进行认证。
- 严格地说,客户端模式并不属于OAuth框架所要解决的问题。
- 用户直接向客户端注册,客户端以自己的名义要求"服务提供商"提供服务,其实不存在授权问题。
步骤如下
(A) 客户端向认证服务器进行身份认证,并要求一个访问令牌。
(B) 认证服务器确认无误后,向客户端提供访问令
A步骤中,客户端发出的HTTP请求,包含以下参数:
....?grant_type=client_credentials
- granttype:授权类型,此处的值固定为"clientcredentials",必选项。
- scope:权限范围,可选项。
B步骤中,认证服务器向客户端发送访问令牌,下面是一个例子。
{
"access_token":"2YotnFZFEjr1zCsicMWpAA",
"token_type":"example",
"expires_in":3600,
"example_parameter":"example_value"
}
5、更新令牌
- 如果用户访问的时候,客户端的"访问令牌"已经过期,可以使用"更新令牌"申请一个新的访问令牌。
客户端发出更新令牌的HTTP请求,包含以下参数
...?grant_type=refresh_token&
refresh_token=tGzv3JOkF0XG5Qx2TlKWIA
- granttype:授权类型,此处的值固定为"clientcredentials",必选项。
- scope:权限范围,可选项。
- refresh_token:更新令牌,必选项。