随着软件的不断发展,出现了更多的身份验证使用场景,除了典型的服务器与客户端之间的身份验证外还有,如服务与服务之间的(如微服务架构)、服务器与多种客户端的(如PC、移动、Web等),甚至还有需要以服务的形式开放给第三方的,身份验证这一功能已经演化为一个服务,很多大型应用中都有自己的身份验证服务器甚至集群,所以普通的身份验证方式已经不能满足需求。
在.Net领域中也有一些开源的身份验证服务器组件,如IdentityServer(
  该图出自IdentityServer:https://identityserver4.readthedocs.io/en/release/intro/big_picture.html
为了满足这些场景人们制定了一套标准协议,这个协议就是OAuth(Open Authorization,开放授权)协议,OAuth能够让第三方应用程序去访问受限制的HTTP服务。OAuth有两个版本分别是1.0和2.0,但是由于1.0版本过于复杂所以1.0版本被2.0版本替换了,并且两个版本是不兼容的。
接下来就对OAuth2.0相关的概念进行介绍:
1. OAuth2.0中的角色
  ● Resource Owner:资源拥有者,就是能够访问被限制资源的用户(注:这里的用户是个泛指,它既可以是真实用户也可以是服务程序)。
● Resource Server:资源宿主,能够接受和处理,使用访问令牌(access token)访问受保护资源的请求(如提供API的服务器)。
● Client:它泛指所有的第三方程序(无论是Web应用、桌面应用还是服务端应用),它通过资源拥有者以及它的授权来访问受保护的资源。
● Authorization Server:用来对授权成功的客户端发布令牌以及对令牌的验证授权。并对Client进行管理。
2.OAuth2.0的协议流程
  
  A. 第三方程序向资源拥有者(用户)发送授权请求,这个过程既可以通过客户端直接向用户请求,也可以通过授权服务器作为中介来完成请求。(注:对于授权请求这个概念相当于用户登录,应用程序可以直接显示一个登录页面,也可以跳转到验证服务器的统一登录页面)
B. 用户将授权相关信息“提交”给第三方程序,在OAuth中有4种不同的权限授予方式,每种方式需要的数据不同,如基于用户密码的授权方式就需要用户名和密码。
C. 第三方程序将用户的授权信息提交到授权服务器,请求一个Access Token。
D. 授权服务器验证完成用户的授权信息后,将Access Token发放到第三方程序。
E. 第三方程序携带Access Token访问被保护的资源。
F. 资源服务器验证Access Token有效后,将资源返回到第三方程序。
3. OAuth中的授权模式(即获取Access Token的方式)
  ● Authorization Code(授权码模式):该模式的核心是客户端通过一个授权码来向授权服务器申请Access Token。是一种基于重定向的授权模式,授权服务器作为用户和第三方应用(Client)的中介,当用户访问第三方应用是,第三方应用跳转到授权服务器引导用户完成身份验证,生成Authorization Code并转交到第三方应用,以便于第三方应用根据这个授权码完成后续的Access Token获取。
● Implicit(简化模式):简化模式是一种简化的授权码模式,授权码模式在首次访问第三方应用时跳转到授权服务器进行身份验证返回授权码,而简化模式在跳转到授权服务器后直接返回Access Token,这种模式减少了获取Access Token的请求次数。
● Resource Owner Password Credentials(用户密码模式):通过资源拥有者(用户)的用户名和密码来直接获取Access Token的一种方法,这种方法要求第三方应用(Client)是高度可信任的,并且其它授权方式不可用的情况下使用。
● Client Credentials(客户端模式):该模式是通过第三方应用(Client)发送一个自己的凭证到授权服务器获得Access Token,这种模式的使用要求该Client已经被授权服务器管理并限制其对被保护资源的访问范围。另外这种模式下Client应该就是一个资源拥有者(用户),如微服务程序。
4. Access Token & Refresh Token
这个很好理解,第三方应用通过Access Token去获取受保护的资源,但是Access Token是存在有效期的,一旦过期就无法使用,为了避免Access Token过期后无法使用,所以加入了Refresh Token的概念,通过刷新的方式来完成Access Token的更新。
5. Client的注册
在OAuth2.0中,所有需要访问受限资源的程序都视为第三方应用(Client),为了保证这个Client是安全的、可信任的,所以OAuth需要对Client进行管理。参考:
● Mac:
  
注:从该组件的名称可以看出,.Net对OAuth的实现实际上是基于Owin的,所以很多内容均使用Owin中相关的身份验证概念,这些内容可参考本系列与身份验证的文章。
2. 添加OAuth授权服务器
  根据上面OAuth的介绍可知,授权服务器是OAuth其中一个角色,该角色最主要的功能就是Access Token的发放以及授权,另外它还用于支持授权码模式的授权码发放以及Client的管理。
在Startup类型的Configuration方法中加入以下代码,该代码是为Owin中间件添加一个授权服务器(注:该中间件是一个Owin的身份验证中间件可参考《
其中OAuthAuthorizationServerOptions定义如下:
  
  上面的定义可以分为以下几类:
● 终结点地址:AuthorizeEndpointPath、TokenEndpointPath等,它定义了访问获取授权码以及获取Token的地址信息。
● Token提供器:AuthorizationCodeProvider、AccessTokenProvider、RefreshTokenProvider负责完成对应令牌的创建和处理功能。
● Token的“加密”与“解密”:该功能是OAuth与Owin身份验证的结合,通过AccessTokenFormat等ISecureDataFormat接口的实现可以将对应的Token转换成一个  AuthenticationTicket。可参考《
3. 为授权服务器添加终结点
上面介绍OAuth时介绍了终结点实际上就是用来获取授权码或者Access Token的,在.Net中使用Microsoft.Owin.Security.OAuth组件仅需要通过配置的形式就可以指定授权码及Token获取的终结点访问地址(注:把AllowInsecureHttp配置属性设为true,可以允许不安全的http来访问终结点,该配置仅用于开发环境):
  
完成后就可以通过浏览器访问这两个地址:
  
  
可以看到是可以访问,只不过是有错误的(注:请求地址的QueryString的参数参考文档)。
4. Client的管理与验证
Client在OAuth中指代了所有的第三方需要访问受限制资源的应用程序,授权服务器为了能够识别和验证Client所以需要完成Client的管理以及验证功能。(注:微软在Microsoft.Owin.Security.OAuth组件中仅仅提供了Client验证的接口,所以要自己实现Client数据的管理以及验证逻辑):
1). 添加Client实体以及对应的仓储(本例以内存的方式实现仓储,实际使用中至少应该保存数据库):
  
上图是Client最基础的属性(注:如果还需要对Client的访问范围进行限制,那么还应该加入一个Scope的列表,本例不再加入Scope的限制)。
2). Client的仓储:
  
3). 实现授权服务器对Client的验证:
由于授权服务器对客户端验证的接口位于OAuthAuthorizationServerProvider类型中,所以首先要继承该类型,并重载相应的验证方法:
  
  上面代码做了以下几件事:
● 尝试从Http请求header或者请求body中获取Client信息,包含Id和密码。
● 如果没有Client的Id信息,那么直接判断为不通过验证,如果有Client的密码信息则保存到Owin上下文中,供后续处理使用。
● 使用获得的ClientId在Client仓储中查询,判断是否是一个合法的Client,如不是则判断为不通过验证。
4). 验证完成后设置该Client的重定向Url(注:该方法仍旧是重载OAuthAuthorizationServerProvider类型中的方法):
  
5. 添加授权码提供器
授权码的生成是授权服务器终结点的一项功能,当使用授权码模式时,用户访问Client会被引导跳转到授权服务器完成身份验证(登录),随后又携带授权码跳转回Client,Client使
