Skip to main content

OAuth2介绍

分类:  Asp.net Core认证和授权 标签:  #Asp.Net core基础 #认证 #授权 #.Net 发布于: 2023-05-27 20:12:30

我们今天来学习一下QAuth2.0.

我们先来了解一些比较有趣的历史。

QAuth2.0 是基于Token的授权方式,我们前面有一篇文章大致讲述过了基于Token认证和授权的历史,为了大家更好的学习和理解QAuth2.0的出现历史必然性,我们还是旧事重提一下,为什么会有Qauth, 它到底解决了哪些问题。

对于Web应用,我们最早的时候提出的解决方案是通过Session来进行认证和授权的。做法很简单,用户提供用户名和密码(或者其他的方式,例如证书或者密钥等等各种手段)到专用的登录的节点,节点验证通过后,会在服务器端生成一个唯一的ID,同时将用户的信息(也就是我们将的ClaimPrinpcal)系列化存在服务器端(内存里,临时文件,数据库,或者分布式缓存等等)同时将这个唯一的ID返回给客户端(通过cookie或者URL或者查询字符串)但是随着应用规模的扩大,部署应用到微服务或者负载均衡后面,这样的设计会立即让存储Session的组件成为单点故障,因此我们有了使用Token保存ClaimPrincpal的手段,前面我们讲过JWT的基本概念(但是Access Token和JWT不是一回事),需要回顾的同学可以参考下面的文档再回顾一下:

这是选择JWT的历史原因,另外我们还需要考虑如下的应用场景:

  • API的认证和授权。

  • 单点登录的认证和授权。

  • 应用和应用的授权。

  • 用户和应用以及资源之间的委托授权。

需要注意的是QAuth2.0 生成的Access Token和JWT的Id_token是有区别的。

最重要的是先要强调一点:QAuth2.0 只是一个授权解决方案,但是并不是认证的解决方案。

很多人将Qauth2.0 称为一个标准,我更愿意将它称为是一个解决方案,由于这个方案解决大多数的问题,在业界内形成了大家都用它,所以它是事实上的工业标准,当然作为一个标准它也定义了很多知识点,以便大家遵守。

在这里Qauth虽然定义了主要的四大应用场景,但是Qauth 2.0 最为有用的实际上通过用户向第三方应用授权访问用户资源的应用场景,也即:用户和应用以及资源之间的委托授权。

关于Qauth 2.0 可以参考RFC:http://www.rfcreader.com/#rfc6749


我们先来看一下Qauth 2.0提供了哪些要素:

  • 授权和许可的范围(Scope)

  • 参与的角色

  • 客户端:这里实际就是需要用户授权的第三方应用,术语里面称为Client, 客户端,但是我觉得不是很好理解,直接认为是需要用户授权的第三方应用,更容易理解。

  • Token(令牌):     Qauth2.0 有两种令牌即Access Token和Refresh     Token

  • 授权服务器。

  • 授权的流程(我理解为应用的场景)

 

授权和许可的范围

这个很好理解,就是通过用户向第三方应用授权的时候,你需要指定一些范围的,但是这里的范围和我们直观的感受有一些区别,我举一个例子:一般我们直观上授权是这么理解的,例如假如我们有一个联系人管理的应用,我们可以考虑如下的授权的场景:

只允许管理员修改所有的联系人,只允许联系人的拥有者修改自己的联系人列表。

但是在你设计了一个应用API之后,例如你定义了一个EditContact的api, 将这个api命名为EditContact之后,通过Qauth 2.0 给第三方应用授权时,你是无法在Qauth 2.0 通过授权服务器对第三方应用说上述的两个规则,授权服务器仅仅能做到的是第三方应用是否能够访问这个API, 这个是不是和我们的直观感受有很大的区别?这个区别是需要谨记在心的,所有授权服务器定义的范围是在API这个级别,而不是更细致的授权规则上。那么更细致的授权规则怎么处理?例如上述的两个规则,那么就需要用户在自己的API中通过解析Access Token, 根据Access Token里的基本信息,重新形成ClaimPrinpcal, 然后再次根据ClaimPrinPcal来进行更细致的授权。

因此请谨记Qauth 2.0 定义的Scope 仅仅是在API 这个级别,是无法兼顾你的业务逻辑的,很多人开始学习Qauth 2.0的时候可能对这个非常的费解。


参与的角色

  • 资源的拥有者,也就是我们: 用户,例如我在Azure 上的Storage Blob里存放了大量的数据,那么这些数据的拥有者就是我。我就是这个用户。

  • 资源服务器:上述这个例子中Azure Storage Blob     服务就是这个资源服务器。

  • Client: 就是第三方应用,例如我自己开发帮部署在Azure Web 上的web app应用。

  • 授权服务器:例如Azure     Activty Direct (AAD) 就是一个授权服务器的例子。

那么他们的交互流程是什么样子的呢?借用RFC的图来说明一下:


当我的WebApp(Client) 想访问我的Azure Storage Blob (Resource Server)里的数据的时候, 它会首先问我们要登录(A),  我们登录完成了之后(B), Web App拿到了一个code(就是Grant), 然后向AAD (Authorization Server)发送这个Grant(C ), 授权服务器校验这个Code 之后,会返回Access Token给Web App (D ),  Web App使用这个Token(E ) 访问Azure Storage Blob, 校验后,返回资源(F)

我们刚刚在范围里论述了一个很重要的问题就是仅仅通过Access Token, Azure Storage Blob 是无法知道Web App 到底应该访问谁的Blob, 它还是需要对Accesstoken进行解析,并取得ClaimPrinpcal 从而知道具体是访问谁的Blob.  这个业务逻辑的部分还是要Blob服务端自行实现。


授权的流程(应用场景)

Qauth 2.0 提供四种应用场景

  • 授权码模式(Authoration Code)

  • 简易模式(implicit)

  • 密码模式(Resource Owner     Password): 想想其实就是用户直接把用户名密码给到第三方应用。

  • 客户端模式(Client     Credentials)

  

授权码模式(granttype:authorization_code)

授权码模式是最为严谨的,也是最安全的一种,在将用户的授权委托给第三方应用的时候,我们应该使用这种模式

借用RFC的图来说明这个流程:


实际发生的步骤如下:

(A)用户访问客户端,后者将前者导向认证服务器。

(B)用户选择是否给予客户端授权。 

(C)假设用户给予授权,认证服务器将用户导向客户端事先指定的"重定向URI"(redirectionURI),同时附上一个授权码。 

(D)客户端收到授权码,附上早先的"重定向URI",向认证服务器申请令牌。这一步是在客户端的后台的服务器上完成的,对用户不可见。 

(E)认证服务器核对了授权码和重定向URI,确认无误后,向客户端发送访问令牌(accesstoken)和更新令牌(refresh token)。

下面是上面这些步骤所需要的参数。

 

A步骤中,客户端申请认证的URI,包含以下参数:

response_type:表示授权类型,必选项,此处的值固定为"code"

client_id:表示客户端的ID,必选项

redirect_uri:表示重定向URI,可选项

scope:表示申请的权限范围,可选项

state:表示客户端的当前状态,可以指定任意值,认证服务器会原封不动地返回这个值。

 

C步骤中,服务器回应客户端的URI,包含以下参数:

code:表示授权码,必选项。该码的有效期应该很短,通常设为10分钟,客户端只能使用该码一次,否则会被授权服务器拒绝。该码与客户端ID和重定向URI,是一一对应关系。

state:如果客户端的请求中包含这个参数,认证服务器的回应也必须一模一样包含这个参数。

 

D步骤中,客户端向认证服务器申请令牌的HTTP请求,包含以下参数:

 grant_type:表示使用的授权模式,必选项,此处的值固定为"authorization_code"。

code:表示上一步获得的授权码,必选项。

redirect_uri:表示重定向URI,必选项,且必须与A步骤中的该参数值保持一致。

client_id:表示客户端ID,必选项。

 

E步骤中,认证服务器发送的HTTP回复,包含以下参数:

access_token:表示访问令牌,必选项。

token_type:表示令牌类型,该值大小写不敏感,必选项,可以是bearer类型或mac类型。

expires_in:表示过期时间,单位为秒。如果省略该参数,必须其他方式设置过期时间。

refresh_token:表示更新令牌,用来获取下一次的访问令牌,可选项。

scope:表示权限范围,如果与客户端申请的范围一致,此项可省略。

 

简易模式   

授权码模式我们中有一环是需要用户授权之后,返回给应用一个授权码,然后应用使用这个授权码再次得到token, 但是简易模式中用户给予授权之后,应用直接就拿到了授权码,应用要做的仅仅是从浏览器中解析并取得token就可以了。

如下图:



密码模式(granttype:password)

密码模式是用户直接把自己的密码给到了应用,不过要求应用不得存储用户的密码,只有在及其信任的情况下才会采取这种形式,如下图:



客户端模式(granttype:clientcredentials)

客户端模式没有用户参与,理论上客户端向服务注册自己,并得到client id和secret,  直接和授权服务器通讯就可以了。



如何使用refresh token

Access Token 过期之后,可以使用refresh token对accesstoken进行更新。

客户端发出请求带以下参数就可以了。

客户端发出更新令牌的HTTP请求,包含以下参数:

  •  granttype:表示使用的授权模式,此处的值固定为"refreshtoken",必选项。
  • refresh_token:表示早前收到的更新令牌,必选项。
  • scope:需要和第一次取得Access token的范围一致。