前文介绍了Authorization Code flow的基本内容,可以看出其拥有不错的安全机制。但是仍然存在局限,如果客户端是运行在服务器上的Web应用程序(这类客户端称为机密客户端)当然是个不错授权模式,因为很多涉及安全隐患的步骤(如AccessToken)都是通过后端通道由web服务器和授权服务器直接通信的,而不需要经过用户的浏览器或者其他的地方。虽然如此,但是Authorization Code仍然是通过前端通道传递的,如果code被泄露,就仍然存在安全隐患,典型的有Cut and pasted code attack,就是一种盗用code的攻击来获取用户的权限。因此,官方更加建议使用“Hybrid Flow”或“PKCE”从而增加安全性,但是官方更加推荐使用PKCE。

Hybrid Flow

先介绍一下ID_Token具体是什么,ID_Token就像我们的身份证或者户口本,客户端程序可以通过ID_Token获取用户的ClaimsID_Token由三个部分(头Header,体Body和签名Signature)组成并且是通过JWT形式呈现。ID_Token中包含的主要内容有issIssuing authority)、subA unique identifier for the end-user issued by the issuer)、audexpiatauth_time等。

Hybrid Flow在流程上大致是和Authorization Code flow一样的,唯一的区别是在完成用户的身份认证之后,通过authorization endpoint返回的数据不同。而这一步返回的数据是根据我们发送请求时的response_type参数决定的,这使得流程更加灵活。

Hybrid Flow根据response_type的不同,authorization endpoint返回可以分为三种情况。

  1. response_type = code + id_token ,即包含Authorization Codeidentity Code
  2. response_type = code + token ,即包含Authorization CodeAccess Code
  3. response_type = code + id_token + token,即包含Authorization Codeidentity CodeAccess Token

可以看到,code都是一定会返回的。如果ID_Tokenauthorization endpointtoken endpoint都被返回了,那么

  • iss和sub的值必须是相同的
  • 所有关于身份认证的Claims在两者中应该都包含
  • 但是authorization endpoint返回的Claim数量应该会少一点,这也是出于对隐私的考虑

如果Access Tokenauthorization endpointtoken endpoint都被返回了,那么

  • 那么两次值可能相同也可能不同
  • 这都是取决于这两个终结点的安全特性

那么到底为什么要使用hybrid flow呢?让我们的应用程序在前端通道和后端通道都可以接收到分开的token接下来进行一些简单的实践,有了前面AuthorizationCode模式代码的经验,编写Hybrid Flow也是很简单的。

授权服务器上新增HybridClient

在之前代码的基础上,新增一个授权方式是Hybridclient,这样就ok了。

Identity Server4 基础应用(四)Hybrid Flow与PKCE

创建一个Hybrid流程的MVC客户端

创建一个新的ASP.NET Core MVC程序,取名为“MVC_Hybrid”,使用Nuget添加IdentityModelOpenIdConnect的引用。

Identity Server4 基础应用(四)Hybrid Flow与PKCE

Startup.cs中进行配置,代码和Authorization Code Flow的基本一致,只是我们在配置ResponseType时需要使用Hybrid定义的三种情况之一,具体代码如下。

Identity Server4 基础应用(四)Hybrid Flow与PKCE

Identity Server4 基础应用(四)Hybrid Flow与PKCE

随后在Controller中,和前面介绍Authorization Code时一样,我们通过扩展方法在授权过程中获取的几个token,并显示到界面上

 1 public async Task<IActionResult> Index()
 2         {
 3             //我们利用拓展方法获取存下来的Access Token
 4             var accessToken = await HttpContext.GetTokenAsync(OpenIdConnectParameterNames.AccessToken);
 5             ViewBag.idToken = await HttpContext.GetTokenAsync(OpenIdConnectParameterNames.IdToken);
 6             var client = new HttpClient();
 7             //携带上AccessToken
 8             client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", accessToken);
 9             //去请求受保护的api1上的资源
10             var response = await client.GetAsync("http://localhost:5001/identity");
11             if (!response.IsSuccessStatusCode)
12             {
13                 ViewBag.Json = response.ReasonPhrase;
14             }
15             var content = await response.Content.ReadAsStringAsync();
16             ViewBag.Json = JArray.Parse(content).ToString();
17             return View();
18         }
Controller中Index

相关文章: