理解OAuth2.0协议和授权机制
无论是自然资源还是互联网上的资源,需要控制使用权与被使用权,以保护资源的安全、合理的使用和有效的管控。
项目中,我们需要控制的是用户资源,既要保证有效用户的合理使用,又要防范非法用户的攻击。如此,如何区分有效和非法就是我们需要考虑的问题,简单点,通过账号密码来区分,能够通过检测的便是有效用户。
可当项目越来越复杂,用户还想使用第三方应用,那么不能将账号密码交给第三方吧,同时,尽管通过账号密码检测了是有效用户,那么是否就能够访问资源、使用资源呢,这也未必。
种种因素下,OAuth(Open Authorization)作为一个安全、开放且简易的标准,方便我们在项目中协调资源、控制授权,以在用户、第三方应用和用户资源三者间形成一个有效的控制机制,而无需将用户的账号密码提供给第三方应用,且还能控制第三方应用对资源的访问与使用。
An open protocol to allow secure authorizationin a simple and standard method from web, mobile and desktop applications.
认证与授权
对于第三方应用想要访问被保护的资源,应当是使用授权访问,而这前提是要经过认证环节,毕竟认证成功,但不代表就有权限访问资源。OAuth更多的是侧重于如何实现授权来控制第三方应用能够访问资源。认证协议是基于其上的Open ID Connect。尽管OAuth的流程中存在认证部分内容,但不能说OAuth就是认证协议。
- Authentication(认证) 是验证用户(是第三方应用的使用者)的身份的凭据(账号和密码),通过这个凭据,系统得以知道你就是你,也就是说系统存在你这个用户。所以,Authentication 被称为身份/用户验证。
- Authorization(授权) 发生在 Authentication(认证) 之后。其本身意思就很明了,它主要掌管我们访问系统的权限。比如有些特定资源只能具有特定的权限才能访问。比如手机浏览器想要访问底层摄像头,需要经用户允许访问才能获得授权,以此才能获取到具体资源。
OAuth历史
OAuth 的历史版本中有 OAuth 1.0 和 OAuth 2.0,其中 OAuth 1.0 在 2010 年发表为 RFC5849,OAuth 2.0 为 RFC6749,并且 OAuth 2.0 不向下兼容 OAuth 1.0,目前 OAuth 2.0 被广泛使用,因此这里不再对 OAuth 1.0 进行更多的描述。
OAuth的几种角色
Resource Owner:资源所有者,拥有资源的人,简单说就是谁在操作浏览器/App/小程序..
Resource Server:资源服务器,资源拥有者拥有的资源
Client:第三方应用,从资源所有者处获取授权,然后从资源服务器获取资源
当不考虑OAuth协议,我们常见便是如上三种,想要第三方应用可以使用资源服务器的资源,又得把自身的账号密码交付给第三方应用,带来不安全。如此再加入一个角色,以平衡控制。Authorization Server:授权服务器,提供给资源所有者完成授权并确定允许给第三方应用可以使用的资源范围与操作。由资源服务器方负责实现。
如此一来,资源所有者将账号密码提供给授权服务器来完成验证,验证有效由授权服务器来负责提供能够代表身份的标识给第三方应用,这样一来降低了账号密码泄露的可能。
OAuth的工作流程
获取授权过程:
(A) 用户(Resource Owner)访问第三方应用(Client),第三方应用(Client)向授权服务器(Authorization Server)发起请求,即第三方应用(Client)向用户(Resource Owner)请求授权。
(B)用户(Resource Owner)输入账号密码,勾选允许的资源范围,提交表单,授权服务器验证后返回授权许可给第三方应用(Client)。
(C) 第三方应用(Client)再带着授权许可请求授权服务器(Authorization Server)换取访问令牌(Access_token)。
(D) 授权服务器(Authorization Server)验证第三方应用(Client)的身份和授权许可,然后给第三方应用(Client)返回访问令牌(Access_token)。
获取资源过程:
(E) 第三方应用(Client)拿访问令牌(Access_token)去请求资源服务器(Resource Server)获取资源。
(F) 资源服务器(Resource Server)返回允许范围内的资源给第三方应用(Client)。
OAuth的几种授权方式
在增加了一个Authorization Server角色后,存在几种常用获取授权许可(资源所有者授权能够访问资源服务器内有效范围资源的凭据,供第三方应用来访问资源服务器)的方式。
- Authorization Code:授权码
- Implicit:隐式授权
- Client Credentials:客户端凭证授权
- Resource Owner Password Credentials:资源所有者密码凭证授权
如上四种模式有一个前提是第三方应用需要在授权服务器中有过登记注册过程。不应该让随便一个第三方应用需要请求资源,就跳转到授权服务器,然后让资源所有者完成授权,然后便可去访问资源。而应该是有效的第三方应用,何为有效,在授权服务器中有过登记,并返回给第三方客户端一对识别码,客户端Id和客户端密钥,由第三方应用私有保存,这个很是常见。
授权码
授权码是目前 OAuth 2.0 中最常用的。通过去授权端获取授权码,利用授权码换取 token,通过使用 token 去资源服务器获取受保护资源。
核心部分在于红色部分,细讲这几部分
1、当前访问的客户端的URL地址重定向到授权服务器的授权地址(2.0~2.1)
https://authorization-server.com/auth
?response_type=code
&client_id=29352915982374239857
&redirect_uri=https%3A%2F%2Fexample-app.com%2Fcallback
&scope=create+delete
&state=xcoiv98y2kd22vusuye3kch
参数说明:
response_type=code
必选,向授权服务器标识客户端使用授权码方式client_id
必选,应用的身份标识Id(应用先得在授权服务器中登记得到一对ClientId和Client Secret)。redirect_uri
可选,授权成功后从授权服务器重定向回client的地址。scope
可选,表示Client申请授权的范围,用一个或多个空格分隔。state
推荐,Client提供的一个字符串,服务器会原样返回给Client,以阻止CSRF攻击。
2、授权页中同意授权,重定向回Client提供的重定向地址(3.0~3.3)
https://example-app.com/redirect
?code=g0ZGZmNjVmOWIjNTk2NTk4ZTYyZGI3
&state=xcoiv98y2kd22vusuye3kch
参数说明:
state
为Client提供的字符串,授权服务器原样返回,用于Client防止CSRF攻击。code
授权服务器生成的授权码(临时的)。
3、授权码换访问令牌(4.0~4.2)
重定向回到Client后,Client获取到授权码,向授权服务器发送Post请求,以换取访问令牌。
POST
https://authorization-server.com/auth/token
Content-Type: application/x-www-form-urlencoded
参数:
grant_type=code
&code=Yzk5ZDczMzRlNDEwY
&redirect_uri=https://example-app.com/cb
&client_id=mRkZGFjM
&client_secret=xRsjfkgfgfdfvxsfg
参数说明:
grant_type=authorization_code
必选,向授权服务器标识客户端类型为授权码类型。code
必选,上一步中重定向返回的授权码。redirect_uri
必选,和第一步中提供的redirect_uri相同。client_id
必选,客户端Id,第一步中提及的应用标识Id。client_secret
必选,客户端标识Id对应密钥,用于授权服务器对客户端身份验证,并防范授权码和客户端Id暴露被直接用来换取访问令牌。
授权服务器验证客户端身份以及授权码正确及有效后返回访问令牌。
{
"access_token": "MTQ0NjJkZmQ5OTM2NDE1ZTZjNGZmZjI3",
"token_type": "bearer",
"expires_in": 3600,
"refresh_token": "IwOGYzYTlmM2YxOTQ5MGE3YmNmMDFkNTVk",
"scope": "create delete"
}
核心部分授权码流程结束,后续便是拿着访问令牌获取资源。
问题解惑
虽然整个流程算是清晰了,但还是存在一些疑惑,如下所示,也随着更多的了解也有了答案。纯属自问自答了。
1、授权码是一个临时的,用来令Client向Authorization Server换取访问令牌(Access token),那么这一次获取到访问令牌,并获取到了资源后。User再次访问同样的资源,这个授权码换取访问令牌的过程没有了,那么如何从哪里获取访问令牌以去访问资源呢?
答:除了返回Access_token还返回了一个Refresh_token。Access_token有短暂的生命周期,如果过期了就用Refresh_token去刷新或是获取一个新的Access_token。Refresh_token的生命周期更长。
2、当一次访问资源的流程结束后,下一次再访问时,如何可以不要再登录了,也就是这个流程中并没返回标识信息到User,如何判断User有效并可以不用重定向到用户登录页?
答:该部分标识用户身份属于OIDC的部分与OAuth无关,OAuth目的是访问受保护的资源。OIDC中有个ID token,一般为JWT格式,用于返回给前端,然后请求时携带着以标识用户身份。又或者是采用Cookie+Session方式来标识用户。具体点就是从AuthServer重定向到Client时已经带上了身份标识信息。
3、Refresh_token是需要Client端存起来吗,如何标识这个Client端对应是哪个User?
答:标识Client端对应的是哪个User,这部分不是OAuth2.0协议所关注的,这部分更多是OIDC协议的事情。至于Refresh_token的存储,如下方式可以。
- 如果User和Client间是Cookie和Session,那么可以通过将标识写入到Cookie中,同时本地Session中记录UserId和Refresh_token,这是可以做到的,由Cookie和Session来标识用户是谁。
- 如果是使用Jwt,那么或许是存在Cache(Memory、Redis)或是直接存在数据表中,因为已经知道了UserId以及Refresh_token的有效期,这些都可以存储起来完成对应关系。
需要注意
- Access_token和Refresh_token其核心在于,Client端请求资源的访问令牌对外隐藏,只在Client端、Authorization Server和Resource Server间使用。
- 标识这个用户身份以及如何确保用户下次访问仍是这个用户并不是OAuth2.0所关心的,OAuth2.0更多是偏向于对保护资源的有效访问,核心部分聚焦于此。
隐式授权
在授权码的环节上做了一些简化,原有返回授权码的过程变成了直接返回访问令牌。这种情况在纯前端Web应用中(如SPA、浏览器插件)常出现,没有后台,在浏览器中需要负责完成所有过程。
相比于授权码形式,少了Client Server与Authorization Server完成交换Access Token过程,而是直接将返回授权码替换成了返回Access Token(这种方式存在着一些安全问题)。除非没得选,不然不会考虑这种方式,因为提出这种方式是十年前,浏览器功能受限,现如今功能更加强大,更多是采用授权码类型加PKCE来代替隐式授权类型。
https://developer.okta.com/blog/2019/05/01/is-the-oauth-implicit-flow-dead
核心部分在于红色部分,细讲这几部分
1、当前访问的客户端的URL地址重定向到授权服务器的授权地址(2.0)
https://authorization-server.com/auth
?response_type=token
&client_id=29352915982374239857
&redirect_uri=https%3A%2F%2Fexample-app.com%2Fcallback
&scope=create+delete
&state=xcoiv98y2kd22vusuye3kch
参数说明:
response_type=token
必选,向授权服务器标识客户端使用隐式授权方式client_id
必选,应用的身份标识Id(应用先得在授权服务器中登记得到一对ClientId和Client Secret)。redirect_uri
可选,授权成功后从授权服务器重定向回client的地址。scope
可选,表示Client申请授权的范围,用一个或多个空格分隔。state
推荐,Client提供的一个字符串,服务器会原样返回给Client,以阻止CSRF攻击。
2、授权页中同意授权,重定向回Client提供的重定向地址(3.0~3.3)
https://example-app.com/redirect
#access_token=g0ZGZmNjVmOWIjNTk2NTk4ZTYyZGI3
&state=xcoiv98y2kd22vusuye3kch
&expires_in=3600
&token_type=Bearer
&scope=create
参数说明:
state
为Client提供的字符串,授权服务器原样返回,用于Client防止CSRF攻击。access_token
授权服务器生成的访问令牌(临时的)。token_type
访问令牌默认的schema。expires_in
访问令牌的过期时间(秒数)。scope
Client申请授权的范围。
问题解惑
1、这样返回的Access token生命周期很短同时又不会返回Refresh token,如果多次请求,那么就失效了得重新登录了?
答:隐式授予流程不能用于获取刷新令牌,由于基于浏览器的应用基本上都是短时的连接,仅持续加载它们的浏览器的上下文的会话长度,因此,刷新令牌的用途非常有限。与其他授予类型不同,可以假定资源所有者仍处于浏览器中,并在必要的时候可用于重新授权客户端。授权服务器仍能够应用首次使用信任(TOFU)原则从而使重新认证成为无缝的用户体验。
需要注意
重定向返回时的返回参数形式上和授权码模式有所不一样,隐式授权将token信息放在了url的hash部分(#后面),而不是作为查询字符串(?后面)。这样浏览器在访问重定向的Location指定的url时,就不会把这些数据发送到服务器。而Client可以通过读取Location头信息中获取到access_token信息。
客户端凭证授权
有些场景下,资源所有者不能直接参与,即没有与用户的交互这一过程,像后台任务,定时Job等是直接由Client Server去请求Authorization Server,这种即Machine to Machine。
核心部分在于红色部分,细讲这几部分
1、当前客户端发起Post请求授权服务器(4.1)
POST请求
https://authorization-server.com/auth
Content-Type: application/x-www-form-urlencoded
grant_type=client_credentials
scope=create+delete
携带请求头,其中Basic对应值xxx为clientid:clientsecret然后经base64编码
Authorization: Basic xxx
客户端提供以下参数请求Authorization Server:
- grant_type:必选。值固定为“client_credentials”。
- scope:可选。表示授权范围。
2、授权服务器认证Client端信息无误后返回访问令牌(4.2)
参数说明:
access_token
授权服务器生成的访问令牌(临时的)。token_type
访问令牌默认的schema。expires_in
访问令牌的过期时间(秒数)。scope
Client申请授权的范围。
资源所有者密码凭证授权
这种方式将资源所有者的身份标识信息(账号密码,提供给Client,然后Client再去授权服务器换取访问令牌),这种安全风险大,更多是完全信任Client才会使用。
核心部分在于红色部分,细讲这几部分
1、当前客户端发起Post请求授权服务器(4.1~4.2)
POST请求
https://authorization-server.com/auth
Content-Type: application/x-www-form-urlencoded
grant_type=password&username=yourname&password=123qwe
请求头,其中Basic对应值xxx为clientid:clientsecret然后经base64编码
Authorization: Basic xxx
- grant_type:必选。值固定为“password”。
- username:必选。用户登陆名。
- passward:必选。用户登陆密码。
- scope:可选。表示授权范围。
参考
https://developer.okta.com/
https://www.cnblogs.com/linianhui/p/oauth2-authorization.html#auto-id-4
2022-02-26,望技术有成后能回来看见自己的脚步
理解OAuth2.0协议和授权机制的更多相关文章
- 深入理解OAuth2.0协议
1. 引言 如果你开车去酒店赴宴,你经常会苦于找不到停车位而耽误很多时间.是否有好办法可以避免这个问题呢?有的,听说有一些豪车的车主就不担心这个问题.豪车一般配备两种钥匙:主钥匙和泊车钥匙.当你到酒店 ...
- 帮你深入理解OAuth2.0协议
1. 引言 如果你开车去酒店赴宴,你经常会苦于找不到停车位而耽误很多时间.是否有好办法可以避免这个问题呢?有的,听说有一些豪车的车主就不担心这个问题.豪车一般配备两种钥匙:主钥匙和泊车钥匙.当你到酒店 ...
- (转)帮你深入理解OAuth2.0协议
1. 引言 如果你开车去酒店赴宴,你经常会苦于找不到停车位而耽误很多时间.是否有好办法可以避免这个问题呢?有的,听说有一些豪车的车主就不担心这个问题.豪车一般配备两种钥匙:主钥匙和泊车钥匙.当你到酒店 ...
- OAuth2.0认证和授权机制讲解
第一章.OAuth2.0 介绍 OAuth认证 OAuth认证是为了做到第三方应用在未获取到用户敏感信息(如:账号密码.用户PIN等)的情况下,能让用户授权予他来访问开放平台(主要访问平台中的资源服务 ...
- 问题:OAuth2.0;结果:帮你深入理解OAuth2.0协议
1. 引言 如果你开车去酒店赴宴,你经常会苦于找不到停车位而耽误很多时间.是否有好办法可以避免这个问题呢?有的,听说有一些豪车的车主就不担心这个问题. 豪车一般配备两种钥匙:主钥匙和泊车钥匙.当你到酒 ...
- OAuth2.0 用户验证授权标准 理解
OAuth2.0是一套标准. 一.问题 这个标准解决了这样的一个问题. 允许用户让第三方应用访问该用户在某一网站上存储的私密的资源(如照片,视频,联系人列表),而无需将用户名和密码提供给第三方应用. ...
- OAuth2.0认证和授权以及单点登录
https://www.cnblogs.com/shizhiyi/p/7754721.html OAuth2.0认证和授权机制讲解 2017-10-30 15:33 by shizhiyi, 2273 ...
- 深入理解OAuth2.0 XSS CSRF CORS 原理
基于Token的WEB后台认证机制 http://www.cnblogs.com/xiekeli/p/5607107.html 深入理解OAuth2.0协议http://blog.csdn.net/s ...
- 对OAuth2.0协议的理解和测试demo
1. 什么是OAuth OAuth(开放授权)是一个开放标准,允许用户授权第三方网站访问他们存储在另外的服务提供者上的信息,而不需要将用户名和密码提供给第三方网站或分享他们数据的所有内容. OAuth ...
随机推荐
- mysql5.7安装和卸载过程
安装mysql 5.7 点击下面链接下载 mysql-5.7.27-winx64.zip 压缩文件 链接:https://pan.baidu.com/s/1CF5mmKkZkD_hxsjFOQJrzw ...
- 【记录一个问题】linux+opencv+cuvid解码1080P视频,当使用CUDA核函数的时候,必然崩溃
崩溃的信息如下: 1 OpenCV(4.1.0-dev) Error: Gpu API call (invalid configuration argument) in videoDecPostPro ...
- node.js和vue cli脚手架下载安装配置方法
一.node.js安装以及环境配置 1.下载vue.js 下载地址: https://nodejs.org/en/ 2.安装node.js 下载完成后,双击安装包开始安装.安装地址最好换成自己指定的地 ...
- listen()和accept()
1.listen()队列剖析 作用:监听端口,TCP连接中的服务器端角色 调用格式:int listen(int sockfd, int backlog); 第一个参数:创建的sockfd, 好好理解 ...
- gin框架中请求路由组的使用
1. gin框架中可以使用路由组来实现对路由的分类 package main import "github.com/gin-gonic/gin" func main() { rou ...
- MySQL数据库本地事务原理
在经典的数据库理论里,本地事务具备四大特征: 原子性 事务中的所有操作都是以原子的方式执行的,要么全部成功,要么全部失败: 一致性 事务执行前后,所有的数据都应该处于一致性状态---即要满足数据库表的 ...
- IntelliJ IDEA 热部署,修改java文件 不用重启tomcat
详情见大佬:https://www.cnblogs.com/chenweichu/articles/6838842.html
- R-B Tree
1.简介 R-B Tree,全称Red-Black Tree,又称为"红黑树",为一种自平衡二叉查找树(特殊的平衡二叉树,都是在插入和删除操作时通过特定操作保持二叉树的平衡,从而获 ...
- 使用estimatedRowHeight的优缺点
使用estimatedRowHeight的优缺点 1.优点 1> 可以降低tableView:heightForRowAtIndexPath:方法的调用频率 2> 将[计算cell高度的操 ...
- Java.lang.Math类详解
Math类位于Java.lang包中,包含用于执行基本数学运算的方法!Math类的所有执行方法都是静态方法,可以直接使用类名.方法名调用,如:Math.round() 常用的方法:Math.round ...