IdentityServer4中文文檔


歡迎IdentityServer4

IdentityServer4是ASP.NET Core 2的OpenID Connect和OAuth 2.0框架。

它在您的應用程序中啟用以下功能:

認證即服務

適用於所有應用程序(Web,本機,移動設備,服務)的集中登錄邏輯和工作流程。IdentityServer是OpenID Connect 的官方認證實現。

單點登錄/注銷

在多種應用程序類型上單點登錄(和退出)。

API的訪問控制

為各種類型的客戶端發出API訪問令牌,例如服務器到服務器,Web應用程序,SPA和本機/移動應用程序。

聯合網關

支持Azure Active Directory,Google,Facebook等外部身份提供商。這可以保護您的應用程序免受如何連接到這些外部提供商的詳細信息的影響。

專注於定制

最重要的部分 - IdentityServer的許多方面都可以根據您的需求進行定制。由於IdentityServer是一個框架而不是盒裝產品或SaaS,因此您可以編寫代碼以使系統適應您的方案。

成熟的開源

IdentityServer使用允許的Apache 2許可證,允許在其上構建商業產品。它也是.NET Foundation的一部分,它提供治理和法律支持。

免費和商業支持

如果您需要幫助構建或運行您的身份平台,請告知我們。我們可以通過多種方式為您提供幫助。

大圖

大多數現代應用程序或多或少看起來像這樣:

 

 

最常見的互動是:

  • 瀏覽器與Web應用程序通信

  • Web應用程序與Web API進行通信(有時是自己的,有時是代表用戶)

  • 基於瀏覽器的應用程序與Web API通信

  • 本機應用程序與Web API通信

  • 基於服務器的應用程序與Web API通信

  • Web API與Web API進行通信(有時是自己的,有時是代表用戶)

通常,每個層(前端,中間層和后端)都必須保護資源並實現身份驗證和/或授權 - 通常針對同一個用戶存儲。

將這些基本安全功能外包給安全令牌服務可防止在這些應用程序和端點之間復制該功能。

重構應用程序以支持安全令牌服務會產生以下體系結構和協議:

 

這種設計將安全問題分為兩部分:

認證

當應用程序需要知道當前用戶的身份時,需要進行身份驗證。通常,這些應用程序代表該用戶管理數據,並且需要確保該用戶只能訪問允許的數據。最常見的例子是(經典)Web應用程序 - 但是基於本機和JS的應用程序也需要身份驗證。

最常見的身份驗證協議是SAML2p,WS-Federation和OpenID Connect - SAML2p是最受歡迎和最廣泛部署的。

OpenID Connect是三者中的最新產品,但被認為是未來,因為它具有最大的現代應用潛力。它是從一開始就為移動應用場景而構建的,旨在實現API友好。

API訪問

應用程序有兩種與API通信的基本方式 - 使用應用程序標識或委派用戶的標識。有時兩種方法都需要結合起來。

OAuth2是一種協議,允許應用程序從安全令牌服務請求訪問令牌並使用它們與API通信。此委派降低了客戶端應用程序和API的復雜性,因為身份驗證和授權可以集中。

OpenID Connect和OAuth 2.0 - 更好地結合在一起

OpenID Connect和OAuth 2.0非常相似 - 事實上,OpenID Connect是OAuth 2.0之上的擴展。兩個基本的安全問題,即身份驗證和API訪問,被合並為一個協議 - 通常只需要一次往返安全令牌服務。

我們相信,OpenID Connect和OAuth 2.0的結合是在可預見的未來保護現代應用程序的最佳方法。IdentityServer4是這兩種協議的實現,經過高度優化,可以解決當今移動,本機和Web應用程序的典型安全問題。

IdentityServer4如何提供幫助

IdentityServer是一個中間件,可將符合規范的OpenID Connect和OAuth 2.0端點添加到任意ASP.NET Core應用程序中。

通常,您構建(或重用)包含登錄和注銷頁面的應用程序(並且可能同意 - 取決於您的需要),IdentityServer中間件為其添加必要的協議頭,以便客戶端應用程序可以與之通信使用那些標准協議。

托管應用程序可以像您想要的那樣復雜,但我們通常建議通過僅包含與身份驗證相關的UI來使攻擊面盡可能小。

術語

規范,文檔和對象模型使用您應該注意的某些術語。

 

IdentityServer

IdentityServer是OpenID Connect提供程序 - 它實現OpenID Connect和OAuth 2.0協議。

不同的文獻對同一個角色使用不同的術語 - 您可能還會找到安全令牌服務,身份提供者,授權服務器,IP-STS等。

但它們完全相同:一種向客戶發放安全令牌的軟件。

IdentityServer具有許多作業和功能 - 包括:

  • 保護你的資源

  • 使用本地帳戶存儲或外部身份提供程序對用戶進行身份驗證

  • 提供會話管理和單點登錄

  • 管理和驗證客戶端

  • 向客戶發放身份和訪問令牌

  • 驗證令牌

用戶

用戶是使用注冊客戶端訪問資源的人。

客戶

客戶端是從IdentityServer請求令牌的軟件 - 用於驗證用戶(請求身份令牌)或訪問資源(請求訪問令牌)。客戶端必須首先向IdentityServer注冊,然后才能請求令牌。

客戶端的示例包括Web應用程序,本機移動或桌面應用程序,SPA,服務器進程等。

資源

您希望使用IdentityServer保護資源 - 用戶的身份數據或API。

每個資源都有一個唯一的名稱 - 客戶端使用此名稱來指定他們希望訪問哪些資源。

身份數據 關於用戶的身份信息(也稱為聲明),例如姓名或電子郵件地址。

API API資源表示客戶端要調用的功能 - 通常建模為Web API,但不一定。

身份令牌

身份令牌表示身份驗證過程的結果。它至少包含用戶的標識符(稱為sub aka subject聲明)以及有關用戶如何以及何時進行身份驗證的信息。它可以包含其他身份數據。

訪問令牌

訪問令牌允許訪問API資源。客戶端請求訪問令牌並將其轉發給API。訪問令牌包含有關客戶端和用戶(如果存在)的信息。API使用該信息來授權訪問其數據。

支持的規格

IdentityServer實現以下規范:

ID連接

  • OpenID Connect Core 1.0(規范

  • OpenID Connect Discovery 1.0(規范

  • OpenID Connect會話管理1.0 - 草案28(規范

  • OpenID Connect Front-Channel Logout 1.0 - 草案02(規范

  • OpenID Connect Back-Channel Logout 1.0 - 草案04(規范

OAuth 2.0用戶

  • OAuth 2.0(RFC 6749

  • OAuth 2.0承載令牌使用(RFC 6750

  • OAuth 2.0多種響應類型(規范

  • OAuth 2.0表單后期響應模式(規范

  • OAuth 2.0令牌撤銷(RFC 7009

  • OAuth 2.0令牌自省(RFC 7662

  • 代碼交換的證明密鑰(RFC 7636

  • 用於客戶端身份驗證的JSON Web令牌(RFC 7523

打包和構建

IdentityServer由許多nuget包組成。

IdentityServer4

nuget | github上

包含核心IdentityServer對象模型,服務和中間件。僅包含對內存配置和用戶存儲的支持 - 但您可以通過配置插入對其他存儲的支持。這是其他回購和包裝的內容。

快速入門

github上

包含一個簡單的入門UI,包括登錄,注銷和同意頁面。

訪問令牌驗證處理程序

nuget | github上

用於驗證API中令牌的ASP.NET Core身份驗證處理程序。處理程序允許在同一API中支持JWT和引用令牌。

ASP.NET核心標識

nuget | github上

IdentityServer的ASP.NET核心身份集成包。該軟件包提供了一個簡單的配置API,可以為IdentityServer用戶使用ASP.NET身份管理庫。

EntityFramework核心

nuget | github上

EntityFramework IdentityServer的核心存儲實現。此程序包為IdentityServer中的配置和操作存儲提供EntityFramework實現。

開發構建

此外,我們將開發/臨時構建發布到MyGet。如果要嘗試嘗試,請將以下Feed添加到Visual Studio:

https://www.myget.org/F/identity/

支持和咨詢選項

我們為IdentityServer提供了多種免費和商業支持和咨詢選項。

免費支持

免費支持是基於社區的,並使用公共論壇

堆棧溢出

有越來越多的人使用IdentityServer來監控StackOverflow上的問題。如果時間允許,我們也會嘗試回答盡可能多的問題

您可以使用此Feed訂閱所有IdentityServer4相關問題:

https://stackoverflow.com/questions/tagged/?tagnames=identityserver4&sort=newest

IdentityServer4在提出新問題時請使用標簽

小膠質

您可以在我們的Gitter聊天室中與其他IdentityServer4用戶聊天:

https://gitter.im/IdentityServer/IdentityServer4

報告錯誤

如果您認為自己發現了錯誤或意外行為,請在Github 問題跟蹤器上打開一個問題。我們會盡快回復您。請理解我們也有日常工作,可能太忙而無法立即回復。

在發布之前還要檢查貢獻指南。

商業支持

我們正在圍繞身份和訪問控制架構進行咨詢,指導和定制軟件開發,特別是IdentityServer。請取得聯系與我們共同探討可行方案。

訓練

我們經常圍繞現代應用的身份和訪問控制進行研討會。在這里查看議程和即將公布的日期 。我們也可以在貴公司私下進行培訓。 聯系我們以請求現場培訓。

Admin UI,Identity Express和SAML2p支持

我們的合作伙伴提供了幾種商業附加產品,請訪問https://www.identityserver.com/products/

演示服務器和測試

您可以使用您喜歡的客戶端庫嘗試IdentityServer4。我們在demo.identityserver.io上有一個測試實例。在主頁面上,您可以找到有關如何配置客戶端以及如何調用API的說明。

此外,我們還有一個repo,可以運行各種IdentityServer和Web API組合(IdentityServer 3和4,ASP.NET Core和Katana)。我們使用此測試工具確保所有排列都有效。您可以通過克隆 repo來自行測試。

貢獻

我們對社區貢獻非常開放,但您應該遵循一些指導方針,以便我們可以毫不費力地處理這個問題。

如何貢獻?

最簡單的貢獻方式是打開一個問題並開始討論。然后我們可以決定是否以及如何實現功能或更改。如果您應該提交帶有代碼更改的pull請求,請從描述開始,只進行最小的更改並提供涵蓋這些更改的測試。

首先閱讀:成為一名優秀的開源公民

一般反饋和討論?

請開始討論核心回購問題跟蹤器

平台

IdentityServer是針對ASP.NET Core 2構建的,可在.NET Framework 4.6.1(及更高版本)和.NET Core 2(及更高版本)上運行。

錯誤和功能請求?

請在相應的GitHub倉庫中記錄一個新問題:

其他討論

https://gitter.im/IdentityServer/IdentityServer4

貢獻代碼和內容

在您提供任何代碼或內容之前,您需要簽署貢獻者許可協議。這是一個自動過程,將在您打開拉取請求后啟動。

注意

我們只接受開發分支的PR。

貢獻項目

如果您啟動貢獻項目(例如,支持Database X或Configuration Store Y),我們非常感謝。告訴我們,我們可以在我們的文檔中發推文和鏈接。

我們通常不想擁有這些貢獻庫,我們已經非常忙於支持核心項目。

命名約定

截至2017年10月,IdentityServer4。* nuget名稱空間保留給我們的軟件包。請使用以下命名約定:

YourProjectName.IdentityServer4

要么

IdentityServer4.Contrib.YourProjectName

設置和概述

啟動新IdentityServer項目有兩種基本方法:

  • 白手起家

  • 從Visual Studio中的ASP.NET標識模板開始

如果您從頭開始,我們提供了幾個幫助程序和內存存儲,因此您不必擔心從一開始就存在持久性。

如果您從ASP.NET身份開始,我們也提供了一種簡單的方法來集成它。

快速入門提供了各種常見IdentityServer方案的分步說明。他們從絕對的基礎開始,變得更加復雜 - 建議你按順序完成它們。

每個快速入門都有一個參考解決方案 - 您可以 在quickstarts文件夾中的IdentityServer4.Samples倉庫中找到代碼 。

基本設置

屏幕截圖顯示了Visual Studio - 但這不是必需的。

創建快速入門IdentityServer

首先創建一個新的ASP.NET Core項目。

然后選擇“清空”選項。

接下來,添加IdentityServer4 nuget包:

或者,您可以使用程序包管理器控制台通過運行以下命令來添加依賴項:

“安裝包IdentityServer4”

注意

IdentityServer構建編號1.x目標ASP.NET Core 1.1,IdentityServer構建編號2.x目標ASP.NET Core 2.0。

IdentityServer使用通常的模式為ASP.NET Core主機配置和添加服務。在ConfigureServices所需的服務中配置並添加到DI系統。在Configure中間件中添加到HTTP管道。

Startup.cs文件修改為如下所示:

public class Startup
{
   public void ConfigureServices(IServiceCollection services)
  {
       services.AddIdentityServer()
          .AddDeveloperSigningCredential();
  }

   public void Configure(IApplicationBuilder app, IHostingEnvironment env)
  {
       if (env.IsDevelopment())
      {
           app.UseDeveloperExceptionPage();
      }

       app.UseIdentityServer();
  }
}

AddIdentityServer在DI中注冊IdentityServer服務。它還為運行時狀態注冊內存存儲。這對於開發方案很有用。對於生產方案,您需要一個持久性或共享存儲,如數據庫或緩存。有關詳細信息,請參閱EntityFramework快速入門。

AddDeveloperSigningCredential擴展程序為簽名令牌創建臨時密鑰材料。同樣,這可能對入門有用,但需要替換為生產場景的一些持久性密鑰材料。有關更多信息,請參閱加密文檔

注意

IdentityServer尚未准備好啟動。我們將在以下快速入門中添加所需的服務。

修改主機

默認情況下,Visual Studio使用IIS Express來托管您的Web項目。這完全沒問題,除了您將無法看到控制台的實時日志輸出。

IdentityServer廣泛使用日志記錄,而UI中的“可見”錯誤消息或返回給客戶端是故意模糊的。

我們建議在控制台主機中運行IdentityServer。您可以通過在Visual Studio中切換啟動配置文件來完成此操作。每次啟動IdentityServer時也不需要啟動瀏覽器 - 您也可以關閉它:

 

此外,在這些快速入門的一致URL上運行IdentityServer會很有幫助。您還應該在上面的啟動配置文件對話框中配置此URL,然后使用http://localhost:5000/。在上面的屏幕截圖中,您可以看到此URL已配置。

注意

我們建議為IIS Express和自托管配置相同的端口。這樣,您可以在兩者之間切換,而無需修改客戶端中的任何配置。

要在啟動時選擇控制台主機,必須在Visual Studio的啟動菜單中選擇它:

如何運行快速入門

如上所述,每個快速入門都有一個參考解決方案 - 您可以 在quickstarts文件夾中的IdentityServer4.Samples repo中找到代碼 。

運行快速入門解決方案各個部分的最簡單方法是將啟動模式設置為“當前選擇”。右鍵單擊解決方案並選擇“設置啟動項目”:

通常,首先啟動IdentityServer,然后啟動API,然后啟動客戶端。如果您確實想要調試,只能在調試器中運行。否則Ctrl + F5是運行項目的最佳方式。

使用客戶端憑據保護

本快速入門介紹了使用IdentityServer保護API的最基本方案。

在這種情況下,我們將定義一個API和一個想要訪問它的客戶端。客戶端將在IdentityServer請求訪問令牌並使用它來獲取對API的訪問權限。

定義

范圍定義了您要保護的系統中的資源,例如API。

由於我們在本演練中使用內存配置 - 您只需創建一個類型的對象ApiResource並設置適當的屬性即可。

將文件(例如Config.cs)添加到項目中並添加以下代碼:

public static IEnumerable<ApiResource> GetApiResources()
{
   return new List<ApiResource>
  {
       new ApiResource("api1", "My API")
  };
}

定義客戶端

下一步是定義可以訪問此API的客戶端。

對於此方案,客戶端將不具有交互式用戶,並將使用IdentityServer的所謂客戶端密鑰進行身份驗證。將以下代碼添加到Config.cs文件中:

public static IEnumerable<Client> GetClients()
{
   return new List<Client>
  {
       new Client
      {
           ClientId = "client",

           // no interactive user, use the clientid/secret for authentication
           AllowedGrantTypes = GrantTypes.ClientCredentials,

           // secret for authentication
           ClientSecrets =
          {
               new Secret("secret".Sha256())
          },

           // scopes that client has access to
           AllowedScopes = { "api1" }
      }
  };
}

配置IdentityServer

要將IdentityServer配置為使用范圍和客戶端定義,您需要向ConfigureServices方法添加代碼。您可以使用方便的擴展方法 - 在封面下,這些將相關的存儲和數據添加到DI系統中:

public void ConfigureServices(IServiceCollection services)
{
   // configure identity server with in-memory stores, keys, clients and resources
   services.AddIdentityServer()
      .AddDeveloperSigningCredential()
      .AddInMemoryApiResources(Config.GetApiResources())
      .AddInMemoryClients(Config.GetClients());
}

就是這樣 - 如果您運行服務器並瀏覽瀏覽器 http://localhost:5000/.well-known/openid-configuration,您應該會看到所謂的發現文檔。客戶端和API將使用它來下載必要的配置數據。

添加

接下來,為您的解決方案添加API。

您可以使用ASP.NET Core Web API模板。同樣,我們建議您控制端口並使用與以前配置Kestrel和啟動配置文件相同的技術。本演練假定您已將API配置為運行http://localhost:5001

控制器

向API項目添加新控制器:

[Route("identity")]
[Authorize]
public class IdentityController : ControllerBase
{
  [HttpGet]
   public IActionResult Get()
  {
       return new JsonResult(from c in User.Claims select new { c.Type, c.Value });
  }
}

稍后將使用此控制器來測試授權要求,以及通過API的眼睛可視化聲明身份。

組態

最后一步是將身份驗證服務添加到DI和身份驗證中間件到管道。這些將:

  • 驗證傳入令牌以確保它來自受信任的頒發者

  • 驗證令牌是否有效用於此api(aka范圍)

將IdentityServer4.AccessTokenValidation NuGet包添加到項目中。

將Startup更新為如下所示:

public class Startup
{
   public void ConfigureServices(IServiceCollection services)
  {
       services.AddMvcCore()
          .AddAuthorization()
          .AddJsonFormatters();

       services.AddAuthentication("Bearer")
          .AddIdentityServerAuthentication(options =>
          {
               options.Authority = "http://localhost:5000";
               options.RequireHttpsMetadata = false;

               options.ApiName = "api1";
          });
  }

   public void Configure(IApplicationBuilder app)
  {
       app.UseAuthentication();

       app.UseMvc();
  }
}

AddAuthentication將身份驗證服務添加到DI並配置"Bearer"為默認方案。AddIdentityServerAuthentication將IdentityServer訪問令牌驗證處理程序添加到DI中以供身份驗證服務使用。 UseAuthentication將身份驗證中間件添加到管道中,以便在每次調用主機時自動執行身份驗證。

如果您使用瀏覽器導航到控制器(http://localhost:5001/identity),您應該獲得401狀態代碼作為回報。這意味着您的API需要憑據。

就是這樣,API現在受到IdentityServer的保護。

創建客戶端

最后一步是編寫請求訪問令牌的客戶端,然后使用此令牌訪問API。為此,請向您的解決方案添加一個控制台項目(請參閱此處的完整代碼)。

IdentityServer的令牌端點實現OAuth 2.0協議,您可以使用原始HTTP來訪問它。但是,我們有一個名為IdentityModel的客戶端庫,它將協議交互封裝在一個易於使用的API中。

將IdentityModel NuGet包添加到您的應用程序。

IdentityModel包括用於發現端點的客戶端庫。這樣您只需要知道IdentityServer的基地址 - 可以從元數據中讀取實際的端點地址:

// discover endpoints from metadata
var disco = await DiscoveryClient.GetAsync("http://localhost:5000");
if (disco.IsError)
{
  Console.WriteLine(disco.Error);
  return;
}

接下來,您可以使用TokenClient該類來請求令牌。要創建實例,您需要傳遞令牌端點地址,客戶端ID和密碼。

接下來,您可以使用該RequestClientCredentialsAsync方法為您的API請求令牌:

// request token
var tokenClient = new TokenClient(disco.TokenEndpoint, "client", "secret");
var tokenResponse = await tokenClient.RequestClientCredentialsAsync("api1");

if (tokenResponse.IsError)
{
   Console.WriteLine(tokenResponse.Error);
   return;
}

Console.WriteLine(tokenResponse.Json);

注意

將訪問令牌從控制台復制並粘貼到jwt.io以檢查原始令牌。

最后一步是調用API。

要將訪問令牌發送到API,通常使用HTTP Authorization標頭。這是使用SetBearerToken擴展方法完成的:

// call api
var client = new HttpClient();
client.SetBearerToken(tokenResponse.AccessToken);

var response = await client.GetAsync("http://localhost:5001/identity");
if (!response.IsSuccessStatusCode)
{
   Console.WriteLine(response.StatusCode);
}
else
{
   var content = await response.Content.ReadAsStringAsync();
   Console.WriteLine(JArray.Parse(content));
}

輸出應如下所示:

注意

默認情況下,訪問令牌將包含有關范圍,生命周期(nbf和exp),客戶端ID(client_id)和頒發者名稱(iss)的聲明。

進一步的實驗

本演練重點關注目前的成功之路

  • 客戶端能夠請求令牌

  • 客戶端可以使用令牌來訪問API

您現在可以嘗試激發錯誤以了解系統的行為,例如

  • 嘗試在未運行時連接到IdentityServer(不可用)

  • 嘗試使用無效的客戶端ID或機密來請求令牌

  • 嘗試在令牌請求期間請求無效范圍

  • 嘗試在API未運行時調用API(不可用)

  • 不要將令牌發送到API

  • 將API配置為需要與令牌中的范圍不同的范圍

使用密碼保護

OAuth 2.0資源所有者密碼授予允許客戶端向令牌服務發送用戶名和密碼,並獲取代表該用戶的訪問令牌。

規范建議僅對“受信任”(或遺留)應用程序使用資源所有者密碼授予。一般來說,當您想要對用戶進行身份驗證並請求訪問令牌時,通常會更好地使用其中一個交互式OpenID Connect流程。

盡管如此,這種授權類型允許我們將用戶的概念引入我們的快速啟動IdentityServer,這就是我們展示它的原因。

添加用戶

就像資源(也稱為范圍)和客戶端的內存存儲一樣,用戶也有一個。

注意

有關如何正確存儲和管理用戶帳戶的詳細信息,請查看基於ASP.NET身份的快速入門。

該類TestUser代表測試用戶及其聲明。讓我們通過在config類中添加以下代碼來創建幾個用戶:

首先將以下using語句添加到Config.cs文件中:

using IdentityServer4.Test;

public static List<TestUser> GetUsers()
{
   return new List<TestUser>
  {
       new TestUser
      {
           SubjectId = "1",
           Username = "alice",
           Password = "password"
      },
       new TestUser
      {
           SubjectId = "2",
           Username = "bob",
           Password = "password"
      }
  };
}

然后使用IdentityServer注冊測試用戶:

public void ConfigureServices(IServiceCollection services)
{
   // configure identity server with in-memory stores, keys, clients and scopes
   services.AddIdentityServer()
      .AddDeveloperSigningCredential()
      .AddInMemoryApiResources(Config.GetApiResources())
      .AddInMemoryClients(Config.GetClients())
      .AddTestUsers(Config.GetUsers());
}

AddTestUsers擴展方法做了幾件事情引擎蓋下

  • 添加對資源所有者密碼授予的支持

  • 添加對登錄UI通常使用的用戶相關服務的支持(我們將在下一個快速入門中使用它)

  • 添加對基於測試用戶的配置文件服務的支持(您將在下一個快速入門中了解更多信息)

為資源所有者密碼授予添加客戶端

您可以通過更改AllowedGrantTypes屬性來簡單地向現有客戶端添加對授權類型的支持 。如果您需要您的客戶端能夠使用絕對支持的兩種授權類型。

通常,您希望為資源所有者用例創建單獨的客戶端,將以下內容添加到客戶端配置中:

public static IEnumerable<Client> GetClients()
{
   return new List<Client>
  {
       // other clients omitted...

       // resource owner password grant client
       new Client
      {
           ClientId = "ro.client",
           AllowedGrantTypes = GrantTypes.ResourceOwnerPassword,

           ClientSecrets =
          {
               new Secret("secret".Sha256())
          },
           AllowedScopes = { "api1" }
      }
  };
}

使用密碼授權請求令牌

客戶端看起來與我們為客戶端憑據授予所做的非常相似。主要區別在於客戶端會以某種方式收集用戶的密碼,並在令牌請求期間將其發送到令牌服務。

IdentityModel再次TokenClient可以在這里提供幫助:

// request token
var tokenClient = new TokenClient(disco.TokenEndpoint, "ro.client", "secret");
var tokenResponse = await tokenClient.RequestResourceOwnerPasswordAsync("alice", "password", "api1");

if (tokenResponse.IsError)
{
   Console.WriteLine(tokenResponse.Error);
   return;
}

Console.WriteLine(tokenResponse.Json);
Console.WriteLine("\n\n");

將令牌發送到身份API端點時,您會注意到與客戶端憑據授權相比有一個小但重要的區別。訪問令牌現在將包含sub唯一標識用戶的聲明。通過在調用API之后檢查內容變量可以看到這個“子”聲明,並且控制器應用程序也會在屏幕上顯示該聲明。

聲明的存在(或不存在)sub允許API區分代表客戶的呼叫和代表用戶的呼叫。

使用OpenID Connect添加用戶認證

在本快速入門中,我們希望通過OpenID Connect協議為我們的IdentityServer添加對交互式用戶身份驗證的支持。

一旦到位,我們將創建一個將使用IdentityServer進行身份驗證的MVC應用程序。

添加

OpenID Connect所需的所有協議支持已內置於IdentityServer中。您需要為登錄,注銷,同意和錯誤提供必要的UI部件。

雖然外觀和精確的工作流程在每個IdentityServer實現中可能總是不同,但我們提供了一個基於MVC的示例UI,您可以將其用作起點。

可以在快速入門UI存儲庫中找到此UI 。您可以克隆或下載此repo,並將控制器,視圖,模型和CSS放入IdentityServer Web應用程序中。

或者,您可以從與IdentityServer Web應用程序相同的目錄中的命令行運行此命令,以自動執行下載:

iex ((New-Object System.Net.WebClient).DownloadString('https://raw.githubusercontent.com/IdentityServer/IdentityServer4.Quickstart.UI/release/get.ps1'))

對於Unix / Linux:

\curl -L https://raw.githubusercontent.com/IdentityServer/IdentityServer4.Quickstart.UI/release/get.sh | bash

添加MVC UI資產后,您還需要在DI系統和管道中將MVC添加到托管應用程序。ConfigureServices使用AddMvc擴展方法添加MVC :

public void ConfigureServices(IServiceCollection services)
{
   services.AddMvc();

   // configure identity server with in-memory stores, keys, clients and scopes
   services.AddIdentityServer()
      .AddDeveloperSigningCredential()
      .AddInMemoryApiResources(Config.GetApiResources())
      .AddInMemoryClients(Config.GetClients())
      .AddTestUsers(Config.GetUsers());
}

Configure使用UseMvc擴展方法將MVC添加為管道中的最后一個中間件:

public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
   if (env.IsDevelopment())
  {
       app.UseDeveloperExceptionPage();
  }

   app.UseIdentityServer();

   app.UseStaticFiles();
   app.UseMvcWithDefaultRoute();
}

有關詳細信息,請參閱快速入門UI 的自述文件

注意

releaseUI repo 的分支具有與最新穩定版本匹配的UI。該dev分支與IdentityServer4的當前開發版本一起使用。如果您正在尋找特定版本的UI - 請檢查標簽。

花一些時間檢查控制器和模型,您越了解它們,就越容易進行未來的修改。大多數代碼使用“功能文件夾”樣式存在於“Quickstart”文件夾中。如果此樣式不適合您,請隨意以您想要的任何方式組織代碼。

創建MVC客戶端

接下來,您將向您的解決方案添加MVC應用程序。使用ASP.NET Core“Web應用程序”(即MVC)模板。不要在向導中配置“身份驗證”設置 - 您將在此快速入門中手動執行此操作。創建項目后,將應用程序配置為使用端口5002(有關如何執行此操作的說明,請參閱概述部分)。

要為ID連接的認證支持添加到了MVC應用程序,添加以下內容ConfigureServicesStartup

public void ConfigureServices(IServiceCollection services)
{
   services.AddMvc();

   JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear();

   services.AddAuthentication(options =>
      {
           options.DefaultScheme = "Cookies";
           options.DefaultChallengeScheme = "oidc";
      })
      .AddCookie("Cookies")
      .AddOpenIdConnect("oidc", options =>
      {
           options.SignInScheme = "Cookies";

           options.Authority = "http://localhost:5000";
           options.RequireHttpsMetadata = false;

           options.ClientId = "mvc";
           options.SaveTokens = true;
      });
}

AddAuthentication將身份驗證服務添加到DI。作為主裝置來驗證用戶(通過我們使用一個cookie "Cookies"DefaultScheme)。我們設置為DefaultChallengeSchemeto,"oidc"因為當我們需要用戶登錄時,我們將使用OpenID Connect方案。

然后AddCookie,我們使用添加可以處理cookie的處理程序。

最后,AddOpenIdConnect用於配置執行OpenID Connect協議的處理程序。這Authority表明我們信任IdentityServer。然后我們通過ClientId。識別這個客戶。 SignInScheme用於在OpenID Connect協議完成后使用cookie處理程序發出cookie。並且SaveTokens用於在cookie中保留來自IdentityServer的令牌(因為稍后將需要它們)。

同樣,我們已經關閉了JWT聲明類型映射,以允許眾所周知的聲明(例如“sub”和“idp”)流暢地通過:

JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear();

然后要確保認證服務執行對每個請求,加入UseAuthenticationConfigureStartup

public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
   if (env.IsDevelopment())
  {
       app.UseDeveloperExceptionPage();
  }
   else
  {
       app.UseExceptionHandler("/Home/Error");
  }

   app.UseAuthentication();

   app.UseStaticFiles();
   app.UseMvcWithDefaultRoute();
}

應該在管道中的MVC之前添加認證中間件。

最后一步是觸發身份驗證握手。為此,請轉到主控制器並添加[Authorize]其中一個操作。同時修改該操作的視圖以顯示用戶的聲明,例如:

<dl>
   @foreach (var claim in User.Claims)
  {
       <dt>@claim.Type</dt>
       <dd>@claim.Value</dd>
  }
</dl>

如果您現在使用瀏覽器導航到該控制器,將嘗試重定向到IdentityServer - 這將導致錯誤,因為MVC客戶端尚未注冊。

添加對OpenID Connect標識范圍的支持

與OAuth 2.0類似,OpenID Connect也使用范圍概念。同樣,范圍代表您想要保護的內容以及客戶想要訪問的內容。與OAuth相比,OIDC中的范圍不代表API,而是代表用戶ID,名稱或電子郵件地址等身份數據。

通過添加新助手(in )來創建對象集合,添加對標准openid(subject id)和profile(名字,姓氏等)范圍的支持:Config.cs``IdentityResource

public static IEnumerable<IdentityResource> GetIdentityResources()
{
  return new List<IdentityResource>
  {
      new IdentityResources.OpenId(),
      new IdentityResources.Profile(),
  };
}

注意

所有標准范圍及其相應的聲明都可以在OpenID Connect 規范中找到

然后,您需要將這些標識資源添加到IdentityServer配置中Startup.cs。使用AddInMemoryIdentityResources您調用的擴展方法AddIdentityServer()

public void ConfigureServices(IServiceCollection services)
{
  services.AddMvc();

  // configure identity server with in-memory stores, keys, clients and scopes
  services.AddIdentityServer()
      .AddDeveloperSigningCredential()
      .AddInMemoryIdentityResources(Config.GetIdentityResources())
      .AddInMemoryApiResources(Config.GetApiResources())
      .AddInMemoryClients(Config.GetClients())
      .AddTestUsers(Config.GetUsers());
}

為OpenID Connect隱式流添加客戶端

最后一步是將MVC客戶端的新配置條目添加到IdentityServer。

基於OpenID Connect的客戶端與我們目前添加的OAuth 2.0客戶端非常相似。但由於OIDC中的流程始終是交互式的,因此我們需要在配置中添加一些重定向URL。

將以下內容添加到客戶端配置中:

public static IEnumerable<Client> GetClients()
{
  return new List<Client>
  {
      // other clients omitted...

      // OpenID Connect implicit flow client (MVC)
      new Client
      {
          ClientId = "mvc",
          ClientName = "MVC Client",
          AllowedGrantTypes = GrantTypes.Implicit,

          // where to redirect to after login
          RedirectUris = { "http://localhost:5002/signin-oidc" },

          // where to redirect to after logout
          PostLogoutRedirectUris = { "http://localhost:5002/signout-callback-oidc" },

          AllowedScopes = new List<string>
          {
              IdentityServerConstants.StandardScopes.OpenId,
              IdentityServerConstants.StandardScopes.Profile
          }
      }
  };
}

測試客戶端

現在終於應該為新的MVC客戶端做好一切准備。

通過導航到受保護的控制器操作來觸發身份驗證握手。您應該會看到重定向到IdentityServer的登錄頁面。

成功登錄后,將向用戶顯示同意屏幕。在這里,用戶可以決定是否要將他的身份信息發布到客戶端應用程序。

注意

可以使用RequireConsent客戶端對象上的屬性基於每個客戶端關閉同意。

..最后,瀏覽器重定向回客戶端應用程序,顯示用戶的聲明。

注意

在開發期間,您有時可能會看到一個異常,指出無法驗證令牌。這是因為簽名密鑰材料是在運行中創建的,並且僅保留在內存中。當客戶端和IdentityServer不同步時會發生此異常。只需在客戶端重復操作,下次元數據趕上時,一切都應該再次正常工作。

添加注銷

最后一步是向MVC客戶端添加注銷。

使用IdentityServer等身份驗證服務,僅清除本地應用程序cookie是不夠的。此外,您還需要向IdentityServer進行往返以清除中央單點登錄會話。

確切的協議步驟在OpenID Connect中間件中實現,只需將以下代碼添加到某個控制器即可觸發注銷:

public async Task Logout()
{
   await HttpContext.SignOutAsync("Cookies");
   await HttpContext.SignOutAsync("oidc");
}

這將清除本地cookie,然后重定向到IdentityServer。IdentityServer將清除其cookie,然后為用戶提供返回MVC應用程序的鏈接。

進一步的實驗

如上所述,OpenID Connect中間件默認要求配置文件范圍。此范圍還包括名稱網站等聲明

讓我們將這些聲明添加到用戶,以便IdentityServer可以將它們放入身份標記:

public static List<TestUser> GetUsers()
{
   return new List<TestUser>
  {
       new TestUser
      {
           SubjectId = "1",
           Username = "alice",
           Password = "password",

           Claims = new []
          {
               new Claim("name", "Alice"),
               new Claim("website", "https://alice.com")
          }
      },
       new TestUser
      {
           SubjectId = "2",
           Username = "bob",
           Password = "password",

           Claims = new []
          {
               new Claim("name", "Bob"),
               new Claim("website", "https://bob.com")
          }
      }
  };
}

下次進行身份驗證時,您的聲明頁面現在會顯示其他聲明。

隨意添加更多聲明 - 以及更多范圍。在Scope對ID連接中間件屬性可以在其中配置的作用域認證期間將發送到IdentityServer。

值得注意的是,對令牌聲明的檢索是一個可擴展性點 - IProfileService。由於我們正在使用AddTestUsersTestUserProfileService默認使用。您可以在此處檢查源代碼 以查看其工作原理。

添加對外部認證的支持

接下來,我們將添加對外部認證的支持。這非常簡單,因為您真正需要的是ASP.NET Core兼容的身份驗證處理程序。

ASP.NET Core本身支持Google,Facebook,Twitter,Microsoft Account和OpenID Connect。此外,你可以找到很多其他的認證供應商實現在這里

添加Google支持

要使用Google進行身份驗證,首先需要向他們注冊。這是在他們的開發者控制台完成的。通過將/ signin-google路徑添加到您的基地址(例如http:// localhost:5000 / signin-google),創建一個新項目,啟用Google+ API並配置您本地IdentityServer的回調地址。

如果您在端口5000上運行 - 您只需使用下面代碼段中的客戶端ID / secret,因為這是我們預先注冊的。

首先將Google身份驗證處理程序添加到DI。這是通過添加該代碼段完成ConfigureServicesStartup

public void ConfigureServices(IServiceCollection services)
{
   services.AddMvc();

   // configure identity server with in-memory stores, keys, clients and scopes
   services.AddIdentityServer()
      .AddDeveloperSigningCredential()
      .AddInMemoryIdentityResources(Config.GetIdentityResources())
      .AddInMemoryApiResources(Config.GetApiResources())
      .AddInMemoryClients(Config.GetClients())
      .AddTestUsers(Config.GetUsers());

   services.AddAuthentication()
      .AddGoogle("Google", options =>
      {
           options.SignInScheme = IdentityServerConstants.ExternalCookieAuthenticationScheme;

           options.ClientId = "434483408261-55tc8n0cs4ff1fe21ea8df2o443v2iuc.apps.googleusercontent.com";
           options.ClientSecret = "3gcoTrEDPPJ0ukn_aYYT6PWo";
      });
}

默認情況下,IdentityServer專門為外部身份驗證的結果配置cookie處理程序(使用基於常量的方案IdentityServerConstants.ExternalCookieAuthenticationScheme)。然后,Google處理程序的配置使用該cookie處理程序。為了更好地理解如何完成此操作,請參閱Quickstart文件夾AccountController下的類。

現在運行MVC客戶端並嘗試進行身份驗證 - 您將在登錄頁面上看到一個Google按鈕:

身份驗證后,您可以看到聲明現在來自Google數據。

進一步的實驗

您可以添加其他外部提供程序。我們有一個雲托管的IdentityServer4 演示版,您可以使用OpenID Connect進行集成。

將OpenId Connect處理程序添加到DI:

services.AddAuthentication()
  .AddGoogle("Google", options =>
  {
       options.SignInScheme = IdentityServerConstants.ExternalCookieAuthenticationScheme;

       options.ClientId = "434483408261-55tc8n0cs4ff1fe21ea8df2o443v2iuc.apps.googleusercontent.com";
       options.ClientSecret = "3gcoTrEDPPJ0ukn_aYYT6PWo";
  })
  .AddOpenIdConnect("oidc", "OpenID Connect", options =>
  {
       options.SignInScheme = IdentityServerConstants.ExternalCookieAuthenticationScheme;
       options.SignOutScheme = IdentityServerConstants.SignoutScheme;

       options.Authority = "https://demo.identityserver.io/";
       options.ClientId = "implicit";

       options.TokenValidationParameters = new TokenValidationParameters
      {
           NameClaimType = "name",
           RoleClaimType = "role"
      };
  });

現在,用戶應該能夠使用雲托管的演示標識提供程序。

注意

快速入門UI自動配置外部用戶。當外部用戶首次登錄時,將創建新的本地用戶,並且所有外部聲明都將復制並與新用戶關聯。你處理這種情況的方式完全取決於你。也許你想首先展示一些注冊用戶界面。可以在此處找到默認快速入門的源代碼。可以在此處找到執行自動配置的控制器。

切換到混合流並添加API訪問

在之前的快速入門中,我們探討了API訪問和用戶身份驗證。現在我們想把這兩個部分放在一起。

OpenID Connect和OAuth 2.0組合的優點在於,您可以使用單個協議和使用令牌服務進行單次交換來實現這兩者。

在之前的快速入門中,我們使用了OpenID Connect隱式流程。在隱式流程中,所有令牌都通過瀏覽器傳輸,這對於身份令牌來說是完全正確的。現在我們還想要一個訪問令牌。

訪問令牌比身份令牌更敏感,如果不需要,我們不希望將它們暴露給“外部”世界。OpenID Connect包含一個名為“混合流”的流程,它為我們提供了兩全其美的優勢,身份令牌通過瀏覽器渠道傳輸,因此客戶端可以在進行任何更多工作之前對其進行驗證。如果驗證成功,客戶端會打開令牌服務的反向通道以檢索訪問令牌。

修改客戶端配置

沒有太多必要的修改。首先,我們希望允許客戶端使用混合流,此外我們還希望客戶端允許執行不在用戶上下文中的服務器到服務器API調用(這與我們的客戶端憑證快速啟動非常相似)。這是使用該AllowedGrantTypes屬性表示的。

接下來我們需要添加一個客戶端密鑰。這將用於檢索反向通道上的訪問令牌。

最后,我們還讓客戶端訪問offline_access范圍 - 這允許請求刷新令牌以實現長期存在的API訪問:

new Client
{
   ClientId = "mvc",
   ClientName = "MVC Client",
   AllowedGrantTypes = GrantTypes.HybridAndClientCredentials,

   ClientSecrets =
  {
       new Secret("secret".Sha256())
  },

   RedirectUris           = { "http://localhost:5002/signin-oidc" },
   PostLogoutRedirectUris = { "http://localhost:5002/signout-callback-oidc" },

   AllowedScopes =
  {
       IdentityServerConstants.StandardScopes.OpenId,
       IdentityServerConstants.StandardScopes.Profile,
       "api1"
  },
   AllowOfflineAccess = true
};

修改MVC客戶端

MVC客戶端的修改也很少 - ASP.NET Core OpenID Connect處理程序內置了對混合流的支持,因此我們只需要更改一些配置值。

我們配置ClientSecret匹配IdentityServer的秘密。添加offline_accessapi1范圍,並設置ResponseType為(這基本上意味着“使用混合流”)code id_token

.AddOpenIdConnect("oidc", options =>
{
   options.SignInScheme = "Cookies";

   options.Authority = "http://localhost:5000";
   options.RequireHttpsMetadata = false;

   options.ClientId = "mvc";
   options.ClientSecret = "secret";
   options.ResponseType = "code id_token";

   options.SaveTokens = true;
   options.GetClaimsFromUserInfoEndpoint = true;

   options.Scope.Add("api1");
   options.Scope.Add("offline_access");
});

當您運行MVC客戶端時,除了現在同意屏幕要求您提供額外的API和脫機訪問范圍之外,沒有太大的區別。

使用訪問令牌

OpenID Connect中間件會自動為您保存令牌(在我們的案例中為身份,訪問和刷新)。這就是SaveTokens設置的作用。

從技術上講,令牌存儲在cookie的屬性部分中。訪問它們的最簡單方法是使用Microsoft.AspNetCore.Authentication命名空間中的擴展方法。

例如,在您的聲明視圖中:

<dt>access token</dt>
<dd>@await ViewContext.HttpContext.GetTokenAsync("access_token")</dd>

<dt>refresh token</dt>
<dd>@await ViewContext.HttpContext.GetTokenAsync("refresh_token")</dd>

要使用訪問令牌訪問API,您需要做的就是檢索令牌,並在HttpClient上設置它:

public async Task<IActionResult> CallApiUsingUserAccessToken()
{
   var accessToken = await HttpContext.GetTokenAsync("access_token");

   var client = new HttpClient();
   client.SetBearerToken(accessToken);
   var content = await client.GetStringAsync("http://localhost:5001/identity");

   ViewBag.Json = JArray.Parse(content).ToString();
   return View("json");
}

使用ASP.NET核心身份

IdentityServer旨在提供靈活性,其中一部分允許您為用戶及其數據(包括密碼)使用您想要的任何數據庫。如果您從一個新的用戶數據庫開始,那么ASP.NET Identity是您可以選擇的一個選項。本快速入門展示了如何將Identity Identity與IdentityServer一起使用。

本快速入門假設您已經完成了所有之前的快速入門。本快速入門使用ASP.NET標識的方法是從Visual Studio中的ASP.NET標識模板創建一個新項目。這個新項目將取代我們在之前的快速入門中從頭開始構建的先前IdentityServer項目。此解決方案中的所有其他項目(針對客戶端和API)將保持不變。

ASP.NET身份的新項目

第一步是為您的解決方案添加ASP.NET Identity的新項目。鑒於ASP.NET Identity需要大量代碼,因此使用Visual Studio中的模板是有意義的。您最終將刪除IdentityServer的舊項目(假設您正在關注其他快速入門),但是您需要遷移幾個項目(或者按照之前的快速入門中的描述從頭開始重寫)。

首先創建一個新的“ASP.NET核心Web應用程序”項目。

然后選擇“Web應用程序模板(模型 - 視圖 - 控制器)”選項。

然后單擊“更改身份驗證”按鈕,並選擇“個人用戶帳戶”(這意味着使用ASP.NET身份):

最后,您的新項目對話框應該如下所示。完成后,單擊“確定”以創建項目。

修改主機

不要忘記修改托管(如此處所述)以在端口5000上運行。這很重要,因此現有客戶端和api項目將繼續工作。

添加IdentityServer包

添加IdentityServer4.AspNetIdentityNuGet包。這取決於IdentityServer4包,因此會自動添加為傳遞依賴項。

范圍和客戶端配置

盡管這是IdentityServer的新項目,但我們仍需要與之前的快速入門相同的范圍和客戶端配置。將用於以前快速入門的配置類(在Config.cs中)復制到此新項目中。

必要的配置更改(暫時)是禁用MVC客戶端的同意。我們還沒有復制先前IdentityServer項目的同意代碼,所以現在對MVC客戶端進行一次修改並設置RequireConsent=false

new Client
{
  ClientId = "mvc",
  ClientName = "MVC Client",
  AllowedGrantTypes = GrantTypes.HybridAndClientCredentials,

  RequireConsent = false,

  ClientSecrets =
  {
      new Secret("secret".Sha256())
  },

  RedirectUris           = { "http://localhost:5002/signin-oidc" },
  PostLogoutRedirectUris = { "http://localhost:5002/signout-callback-oidc" },

  AllowedScopes =
  {
      IdentityServerConstants.StandardScopes.OpenId,
      IdentityServerConstants.StandardScopes.Profile,
      "api1"
  },
  AllowOfflineAccess = true
}

配置IdentityServer

和以前一樣,IdentityServer需要在Startup.cs中ConfigureServices和in Configure中配置。

ConfigureServices

這顯示了為ASP.NET Identity生成的模板代碼,以及IdentityServer所需的附加內容(最后)。在之前的快速入門中,AddTestUsers擴展方法用於注冊用戶,但在這種情況下,我們將該擴展方法替換AddAspNetIdentity為使用ASP.NET Identity用戶。該AddAspNetIdentity擴展方法需要一個通用的參數,它是你的ASP.NET身份用戶類型(同一個在需要AddIdentity從模板方法)。

public void ConfigureServices(IServiceCollection services)
{
  services.AddDbContext<ApplicationDbContext>(options =>
      options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));

  services.AddIdentity<ApplicationUser, IdentityRole>()
      .AddEntityFrameworkStores<ApplicationDbContext>()
      .AddDefaultTokenProviders();

  // Add application services.
  services.AddTransient<IEmailSender, EmailSender>();

  services.AddMvc();

  // configure identity server with in-memory stores, keys, clients and scopes
  services.AddIdentityServer()
      .AddDeveloperSigningCredential()
      .AddInMemoryPersistedGrants()
      .AddInMemoryIdentityResources(Config.GetIdentityResources())
      .AddInMemoryApiResources(Config.GetApiResources())
      .AddInMemoryClients(Config.GetClients())
      .AddAspNetIdentity<ApplicationUser>();
}

注意

在使用ASP.NET標識時,在DI系統中 ASP.NET標識之后注冊IdentityServer非常重要,因為IdentityServer會從ASP.NET標識覆蓋某些配置。

配置

這顯示了為ASP.NET Identity生成的模板代碼,以及UseIdentityServer替換調用的調用UseAuthentication

public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
  if (env.IsDevelopment())
  {
      app.UseDeveloperExceptionPage();
      app.UseBrowserLink();
      app.UseDatabaseErrorPage();
  }
  else
  {
      app.UseExceptionHandler("/Home/Error");
  }

  app.UseStaticFiles();

  // app.UseAuthentication(); // not needed, since UseIdentityServer adds the authentication middleware
  app.UseIdentityServer();

  app.UseMvc(routes =>
  {
      routes.MapRoute(
          name: "default",
          template: "{controller=Home}/{action=Index}/{id?}");
  });
}

創建用戶數據庫

鑒於這是一個新的ASP.NET Identity項目,您將需要創建數據庫。您可以通過從項目目錄運行命令提示符並運行來執行此操作,如下所示:dotnet ef database update -c ApplicationDbContext

創建用戶

此時,您應該能夠運行項目並在數據庫中創建/注冊用戶。啟動應用程序,然后從主頁單擊“注冊”鏈接:

在注冊頁面上創建一個新的用戶帳戶:

現在您擁有了一個用戶帳戶,您應該能夠登錄,使用客戶端並調用API。

使用MVC客戶端登錄

啟動MVC客戶端應用程序,您應該能夠單擊“安全”鏈接以登錄。

您應該被重定向到ASP.NET Identity登錄頁面。使用新創建的用戶登錄:

登錄后,您應該跳過同意頁面(根據我們上面做出的更改),並立即重定向回MVC客戶端應用程序,在該應用程序中應列出您的用戶聲明。

您還應該能夠單擊“使用應用程序標識調用API”來代表用戶調用API:

現在,您已使用ASP.NET Identity中的用戶登錄。

下一步是什么?

IdentityServer的先前快速入門項目提供了同意頁面,錯誤頁面和注銷頁面。這些缺失部分的代碼可以簡單地從之前的快速入門項目復制到此項目中。完成后,您最終可以刪除/刪除舊的IdentityServer項目。此外,一旦完成此操作,請不要忘記RequireConsent=true在MVC客戶端配置上重新啟用該標志。

此快速入門示例代碼已經為您完成了這些步驟,因此您可以快速開始使用所有這些功能。請享用!

添加JavaScript客戶端

本快速入門將展示如何構建JavaScript客戶端應用程序。用戶將登錄IdentityServer,使用IdentityServer發出的訪問令牌調用Web API,並注銷IdentityServer。

JavaScript客戶端的新項目

為JavaScript應用程序創建一個新項目。它可以只是一個空的Web項目,也可以是一個空的ASP.NET Core應用程序。此快速入門將使用空的ASP.NET Core應用程序。

創建一個新的ASP.NET Core Web應用程序:

選擇“空”模板:

單擊“確定”按鈕以創建項目。

修改主機

修改托管(如此處所述)以在端口5003上運行。

添加靜態文件中間件

鑒於該項目主要用於客戶端,我們需要ASP.NET Core來提供構成我們應用程序的靜態HTML和JavaScript文件。靜態文件中間件旨在實現此目的。

在方法中注冊Startup.cs中的靜態文件中間件Configure

public void Configure(IApplicationBuilder app)
{
  app.UseDefaultFiles();
  app.UseStaticFiles();
}

此中間件現在將從應用程序的〜/ wwwroot文件夾中提供靜態文件。這是我們將放置HTML和JavaScript文件的地方。

參考OIDC客戶端

在MVC項目中,我們使用庫來處理OpenID Connect協議。在這個項目中,我們需要一個類似的庫,除了一個在JavaScript中運行並且設計為在瀏覽器中運行的庫。該OIDC客戶端庫就是這樣一個圖書館。它可以通過NPMBower以及從github 直接下載

NPM

如果要使用NPM下載oidc-client,請按照以下步驟操作:

將新的NPM包文件添加到項目中並將其命名為package.json:

在的package.json一個補充dependencyoidc-client

"dependencies": {
"oidc-client": "1.4.1"
}

保存此文件后,Visual Studio應自動將這些包還原到名為node_modules的文件夾中:

在〜/ node_modules / oidc-client / dist文件夾中找到名為oidc-client.js的文件,並將其復制到應用程序的〜/ wwwroot文件夾中。有更復雜的方法將NPM包復制到〜/ wwwroot,但這些技術超出了本快速入門的范圍。

添加HTML和JavaScript文件

接下來是將您的HTML和JavaScript文件添加到〜/ wwwroot。我們將有兩個HTML文件和一個特定於應用程序的JavaScript文件(除了oidc-client.js庫)。在〜/ wwwroot中,添加一個名為index.html和callback.html的HTML文件,並添加一個名為app.js的JavaScript文件。

的index.html

這將是我們的應用程序中的主頁。它將只包含用於登錄,注銷和調用Web API的按鈕的HTML。它還將包含<script>標記以包含我們的兩個JavaScript文件。它還包含<pre>用於向用戶顯示消息的用途。

它應該如下所示:

<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8" />
  <title></title>
</head>
<body>
  <button id="login">Login</button>
  <button id="api">Call API</button>
  <button id="logout">Logout</button>

  <pre id="results"></pre>

  <script src="oidc-client.js"></script>
  <script src="app.js"></script>
</body>
</html>

app.js

這將包含我們的應用程序的主要代碼。第一件事是添加一個幫助函數來將消息記錄到<pre>

function log() {
  document.getElementById('results').innerText = '';

  Array.prototype.forEach.call(arguments, function (msg) {
      if (msg instanceof Error) {
          msg = "Error: " + msg.message;
      }
      else if (typeof msg !== 'string') {
          msg = JSON.stringify(msg, null, 2);
      }
      document.getElementById('results').innerHTML += msg + '\r\n';
  });
}

接下來,添加代碼以將“click”事件處理程序注冊到三個按鈕:

document.getElementById("login").addEventListener("click", login, false);
document.getElementById("api").addEventListener("click", api, false);
document.getElementById("logout").addEventListener("click", logout, false);

接下來,我們可以使用UserManager類的OIDC客戶端庫來管理ID連接協議。它需要MVC Client中必需的類似配置(盡管具有不同的值)。添加此代碼以配置和實例化UserManager

var config = {
  authority: "http://localhost:5000",
  client_id: "js",
  redirect_uri: "http://localhost:5003/callback.html",
  response_type: "id_token token",
  scope:"openid profile api1",
  post_logout_redirect_uri : "http://localhost:5003/index.html",
};
var mgr = new Oidc.UserManager(config);

接下來,UserManager提供getUserAPI以了解用戶是否登錄到JavaScript應用程序。它使用JavaScript Promise以異步方式返回結果。返回的User對象具有profile包含用戶聲明的屬性。添加此代碼以檢測用戶是否已登錄JavaScript應用程序:

mgr.getUser().then(function (user) {
  if (user) {
      log("User logged in", user.profile);
  }
  else {
      log("User not logged in");
  }
});

接下來,我們要實現的loginapilogout功能。在UserManager提供了signinRedirect登錄用戶,並且signoutRedirect以注銷用戶。User我們在上面的代碼中獲得的對象還具有access_token可用於通過Web API進行身份驗證的屬性。在access_token將被傳遞給通過網絡API 授權與頭承載方案。添加此代碼以在我們的應用程序中實現這三個功能:

function login() {
  mgr.signinRedirect();
}

function api() {
  mgr.getUser().then(function (user) {
      var url = "http://localhost:5001/identity";

      var xhr = new XMLHttpRequest();
      xhr.open("GET", url);
      xhr.onload = function () {
          log(xhr.status, JSON.parse(xhr.responseText));
      }
      xhr.setRequestHeader("Authorization", "Bearer " + user.access_token);
      xhr.send();
  });
}

function logout() {
  mgr.signoutRedirect();
}

callback.html

redirect_uri一旦用戶登錄IdentityServer,此HTML文件就是指定的頁面。它將完成與IdentityServer的OpenID Connect協議登錄握手。這個代碼全部由UserManager我們之前使用的類提供。登錄完成后,我們可以將用戶重定向回主index.html頁面。添加此代碼以完成登錄過程:

<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8" />
  <title></title>
</head>
<body>
  <script src="oidc-client.js"></script>
  <script>
      new Oidc.UserManager().signinRedirectCallback().then(function () {
          window.location = "index.html";
      }).catch(function (e) {
          console.error(e);
      });
  </script>
</body>
</html>

客戶注冊加入IdentityServer的JavaScript客戶端

既然客戶端應用程序已經准備就緒,我們需要在IdentityServer中為這個新的JavaScript客戶端定義一個配置條目。在IdentityServer項目中找到客戶端配置(在Config.cs中)。將新客戶端添加到我們的新JavaScript應用程序的列表中。它應具有下面列出的配置:

// JavaScript Client
new Client
{
  ClientId = "js",
  ClientName = "JavaScript Client",
  AllowedGrantTypes = GrantTypes.Implicit,
  AllowAccessTokensViaBrowser = true,

  RedirectUris =           { "http://localhost:5003/callback.html" },
  PostLogoutRedirectUris = { "http://localhost:5003/index.html" },
  AllowedCorsOrigins =     { "http://localhost:5003" },

  AllowedScopes =
  {
      IdentityServerConstants.StandardScopes.OpenId,
      IdentityServerConstants.StandardScopes.Profile,
      "api1"
  }
}

讓Ajax調用與CORS網絡API

最后一點配置是在Web API項目中配置CORS。這將允許從http:// localhost:5003到http:// localhost:5001進行Ajax調用。

配置CORS

ConfigureServices在Startup.cs中將CORS服務添加到依賴注入系統:

public void ConfigureServices(IServiceCollection services)
{
  services.AddMvcCore()
      .AddAuthorization()
      .AddJsonFormatters();

  services.AddAuthentication("Bearer")
      .AddIdentityServerAuthentication(options =>
      {
          options.Authority = "http://localhost:5000";
          options.RequireHttpsMetadata = false;

          options.ApiName = "api1";
      });

  services.AddCors(options =>
  {
      // this defines a CORS policy called "default"
      options.AddPolicy("default", policy =>
      {
          policy.WithOrigins("http://localhost:5003")
              .AllowAnyHeader()
              .AllowAnyMethod();
      });
  });
}

將CORS中間件添加到管道中Configure

public void Configure(IApplicationBuilder app)
{
  app.UseCors("default");

  app.UseAuthentication();

  app.UseMvc();
}

運行JavaScript應用程序

現在您應該能夠運行JavaScript客戶端應用程序:

單擊“登錄”按鈕以對用戶進行簽名。一旦用戶返回到JavaScript應用程序,您應該看到他們的個人資料信息:

然后單擊“API”按鈕以調用Web API:

最后點擊“退出”以簽署用戶。

您現在可以開始使用IdentityServer進行登錄,注銷和驗證對Web API的調用的JavaScript客戶端應用程序。

使用EntityFramework Core進行配置和操作數據

IdentityServer旨在實現可擴展性,其中一個可擴展點是用於IdentityServer所需數據的存儲機制。本快速入門展示了如何配置IdentityServer以使用EntityFramework(EF)作為此數據的存儲機制(而不是使用我們迄今為止使用的內存中實現)。

注意

除了手動配置EF支持外,還有一個IdentityServer模板可用於創建具有EF支持的新項目。使用創建它。有關更多信息,請參見此處dotnet new is4ef

IdentityServer4.EntityFramework

我們正在向數據庫移動兩種類型的數據。第一個是配置數據(資源和客戶端)。第二個是IdentityServer在使用時產生的操作數據(令牌,代碼和同意)。這些存儲使用接口建模,我們在IdentityServer4.EntityFramework Nuget包中提供這些接口的EF實現。

通過添加IdentityServer項目的IdentityServer4.EntityFramework Nuget包的引用開始。

使用的SqlServer

鑒於EF的靈活性,您可以使用任何EF支持的數據庫。對於本快速入門,我們將使用Visual Studio附帶的SqlServer的LocalDb版本。

數據庫架構更改和使用EF遷移

該IdentityServer4.EntityFramework包中包含從IdentityServer的模型映射實體類。作為IdentityServer的車型變化,所以會在實體類IdentityServer4.EntityFramework。當您使用IdentityServer4.EntityFramework並隨着時間的推移升級時,您將負責自己的數據庫架構以及實體類更改時該架構所需的更改。管理這些更改的一種方法是使用EF遷移,此快速入門將顯示如何完成此操作。如果遷移不是您的首選項,那么您可以以任何您認為合適的方式管理架構更改。

注意

為IdentityServer4.EntityFramework中的實體維護SqlServer的SQL腳本。他們就在這里

用於遷移的EF工具

除了使用EF遷移跟蹤架構更改之外,我們還將使用它在數據庫中創建初始架構。這需要使用EF Core工具(此處有更多詳細信息)。我們現在將添加它們,不幸的是,這必須通過手動編輯.csproj文件來完成。要通過右鍵單擊項目來編輯.csproj,然后選擇“編輯projectname.csproj”:

注意

根據您為IdentityServer主機創建初始項目的方式,您可能已在csproj文件中配置了這些工具。如果是,您可以跳到下一部分。

然后在結尾</ Project>元素之前添加以下代碼段:

<ItemGroup>
<DotNetCliToolReference Include="Microsoft.EntityFrameworkCore.Tools.DotNet" Version="2.0.0" />
</ItemGroup>

它應該看起來像這樣:

保存並關閉文件。要測試您是否正確安裝了這些工具,可以在與項目相同的目錄中打開命令shell並運行dotnet ef。它應該如下所示:

配置商店

接下來的步驟是,以取代當前呼叫AddInMemoryClientsAddInMemoryIdentityResourcesAddInMemoryApiResourcesConfigureServices在方法Startup.cs。我們將使用以下代碼替換它們:

const string connectionString = @"Data Source=(LocalDb)\MSSQLLocalDB;database=IdentityServer4.Quickstart.EntityFramework-2.0.0;trusted_connection=yes;";
var migrationsAssembly = typeof(Startup).GetTypeInfo().Assembly.GetName().Name;

// configure identity server with in-memory stores, keys, clients and scopes
services.AddIdentityServer()
  .AddDeveloperSigningCredential()
  .AddTestUsers(Config.GetUsers())
  // this adds the config data from DB (clients, resources)
  .AddConfigurationStore(options =>
  {
      options.ConfigureDbContext = builder =>
          builder.UseSqlServer(connectionString,
              sql => sql.MigrationsAssembly(migrationsAssembly));
  })
  // this adds the operational data from DB (codes, tokens, consents)
  .AddOperationalStore(options =>
  {
      options.ConfigureDbContext = builder =>
          builder.UseSqlServer(connectionString,
              sql => sql.MigrationsAssembly(migrationsAssembly));

      // this enables automatic token cleanup. this is optional.
      options.EnableTokenCleanup = true;
      options.TokenCleanupInterval = 30;
  });

您可能需要將這些命名空間添加到文件中:

using Microsoft.EntityFrameworkCore;
using System.Reflection;

上面的代碼是對連接字符串進行硬編碼,如果您願意,可以隨意更改。此外,調用AddConfigurationStoreAddOperationalStore注冊EF支持的商店實現。

傳遞給這些API的“構建器”回調函數是EF機制,允許您為這兩個存儲中的每一個配置DbContextOptionsBuilderfor DbContext。這就是我們的DbContext類可以使用您要使用的數據庫提供程序進行配置的方式。在這種情況下,通過調用UseSqlServer我們正在使用SqlServer。您也可以看出,這是提供連接字符串的位置。

“options”回調函數用於UseSqlServer配置定義EF遷移的程序集。EF需要使用遷移來定義數據庫的模式。

注意

托管應用程序負責定義這些遷移,因為它們特定於您的數據庫和提供程序。

我們接下來會添加遷移。

添加遷移

要創建遷移,請在IdentityServer項目目錄中打開命令提示符。在命令提示符下運行以下兩個命令:

dotnet ef migrations add InitialIdentityServerPersistedGrantDbMigration -c PersistedGrantDbContext -o Data/Migrations/IdentityServer/PersistedGrantDb
dotnet ef migrations add InitialIdentityServerConfigurationDbMigration -c ConfigurationDbContext -o Data/Migrations/IdentityServer/ConfigurationDb

它應該看起來像這樣:

您現在應該在項目中看到〜/ Data / Migrations / IdentityServer文件夾。其中包含新創建的遷移的代碼。

注意

如果您的數據庫項目是一個單獨的類庫,並修復了錯誤“無法創建類型的對象”<您的名字> DbContext'。將“IDesignTimeDbContextFactory”的實現添加到項目中,或者參閱https://go.microsoft.com/fwlink/?linkid=851728以獲取在設計時支持的其他模式。通過添加IDesignTimeDbContextFactory的實現,您還需要PersistedGrantDbContext和ConfigurationDbContext的工廠實現。

初始化數據庫

現在我們已經進行了遷移,我們可以編寫代碼來從遷移中創建數據庫。我們還將使用我們在之前的快速入門中定義的內存配置數據來為數據庫設定種子。

在Startup.cs中添加此方法以幫助初始化數據庫:

private void InitializeDatabase(IApplicationBuilder app)
{
  using (var serviceScope = app.ApplicationServices.GetService<IServiceScopeFactory>().CreateScope())
  {
      serviceScope.ServiceProvider.GetRequiredService<PersistedGrantDbContext>().Database.Migrate();

      var context = serviceScope.ServiceProvider.GetRequiredService<ConfigurationDbContext>();
      context.Database.Migrate();
      if (!context.Clients.Any())
      {
          foreach (var client in Config.GetClients())
          {
              context.Clients.Add(client.ToEntity());
          }
          context.SaveChanges();
      }

      if (!context.IdentityResources.Any())
      {
          foreach (var resource in Config.GetIdentityResources())
          {
              context.IdentityResources.Add(resource.ToEntity());
          }
          context.SaveChanges();
      }

      if (!context.ApiResources.Any())
      {
          foreach (var resource in Config.GetApiResources())
          {
              context.ApiResources.Add(resource.ToEntity());
          }
          context.SaveChanges();
      }
  }
}

然后我們可以從Configure方法中調用它:

public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
  // this will do the initial DB population
  InitializeDatabase(app);

  // the rest of the code that was already here
  // ...
}

現在,如果運行IdentityServer項目,則應創建數據庫並使用快速入門配置數據進行種子設定。您應該能夠使用SQL Server Management Studio或Visual Studio來連接和檢查數據。

注意

上面的InitializeDatabase輔助API可以方便地為數據庫設定種子,但是這種方法並不適合每次運行應用程序時執行。填充數據庫后,請考慮刪除對API的調用。

運行客戶端應用程序

您現在應該能夠運行任何現有的客戶端應用程序並登錄,獲取令牌並調用API - 所有這些都基於數據庫配置。

注意

本節中的代碼仍然依賴於Config.cs及其虛構用戶Alice和Bob。如果您的用戶列表很簡短且靜態,則調整后的Config.cs版本可能就足夠了,但您可能希望在數據庫中動態管理更大且更流暢的用戶列表。ASP.NET Identity是一個需要考慮的選項,下一節的快速入門列出了此解決方案的示例實現。

社區快速入門和樣本

IdentityServer組織不維護這些示例。IdentityServer組織愉快地鏈接到社區樣本,但不能對樣本做出任何保證。請直接與作者聯系。

各種ASP.NET核心安全樣本

https://github.com/leastprivilege/AspNetCoreSecuritySamples

IdentityServer4 EF和ASP.NET身份

示例結合了EF和ASP.NET Identity快速入門(#6和#8)。

共同托管IdentityServer4和

此示例顯示如何在與保護API的IdentityServer相同的主機中托管API。

https://github.com/brockallen/IdentityServerAndApi

MongoDB的IdentityServer4示例

  • IdentityServer4-mongo:與Quickstart#8 EntityFramework配置類似,但使用MongoDB配置數據。

  • IdentityServer4-mongo-AspIdentity:更詳細的示例基於使用ASP.NET Identity進行身份管理,使用MongoDB作為配置數據

https://github.com/souzartn/IdentityServer4.Samples.Mongo

從Facebook,Google和Twitter交換外部令牌

  • 顯示如何使用擴展授權將外部身份驗證令牌交換到身份服務器訪問令牌

https://github.com/waqaskhan540/IdentityServerExternalAuth

IdentityServer4 Quickstart UI的ASP.NET Core MVC RazorPages模板

基於Razor Pages的QuickStart示例Martin Fletcher提供

.NET Core和ASP.NET Core“平台”場景

  • 顯示可信“內部”應用程序和“外部”應用程序與.NET Core 2.0和ASP.NET Core 2.0應用程序的交互

https://github.com/BenjaminAbt/Samples.AspNetCore-IdentityServer4

固定節點API與來自使用JWKS IdentityServer4令牌

  • 演示如何使用IdentityServer4中的JWKS端點和RS256算法保護節點(Express)API。

  • 使用更高質量的生產就緒模塊提供IdentityServer4.Samples中NodeJsApi樣本的替代方案。

https://github.com/lyphtec/idsvr4-node-jwks

啟動

IdentityServer是中間件和服務的組合。所有配置都在您的啟動類中完成。

配置服務

您可以通過調用以下方法將IdentityServer服務添加到DI系統:

public void ConfigureServices(IServiceCollection services)
{
  var builder = services.AddIdentityServer();
}

您可以選擇將選項傳入此調用。有關選項的詳細信息,請參見此

這將返回一個構建器對象,該構建器對象又有許多方便的方法來連接其他服務。

密鑰材料

    • AddSigningCredential

      添加簽名密鑰服務,該服務為各種令牌創建/驗證服務提供指定的密鑰材料。您可以從證書存儲中傳入證書的a X509Certificate2,a SigningCredential或引用。

    • AddDeveloperSigningCredential

      在啟動時創建臨時密鑰材料。當您沒有要使用的證書時,這僅適用於dev。生成的密鑰將保留到文件系統,以便在服務器重新啟動之間保持穩定(可以通過傳遞禁用false)。這解決了客戶端/ api元數據緩存在開發期間不同步時的問題。

    • AddValidationKey

      添加用於驗證令牌的密鑰。它們將由內部令牌驗證器使用,並將顯示在發現文檔中。您可以從證書存儲中傳入證書的a X509Certificate2,a SigningCredential或引用。這對於關鍵翻轉場景非常有用。

內存配置存儲

各種“內存中”配置API允許從內存中的配置對象列表配置IdentityServer。這些“內存中”集合可以在宿主應用程序中進行硬編碼,也可以從配置文件或數據庫動態加載。但是,通過設計,這些集合僅在托管應用程序啟動時創建。

使用這些配置API的目的是在原型設計,開發和/或測試時使用,在這種情況下,不需要在運行時為配置數據動態查詢數據庫。如果配置很少更改,則此配置樣式也可能適用於生產方案,或者如果必須更改值,則要求重新啟動應用程序並不方便。

    • AddInMemoryClients

      基於配置對象的內存中集合的注冊IClientStoreICorsPolicyService實現Client

    • AddInMemoryIdentityResources

      IResourceStore根據IdentityResource配置對象的內存中集合注冊實現。

    • AddInMemoryApiResources

      IResourceStore根據ApiResource配置對象的內存中集合注冊實現。

測試商店

TestUser級車型的用戶,他們的憑據,並在IdentityServer索賠。使用TestUser“內存”商店是因為它適用於原型設計,開發和/或測試。采用TestUser在生產中不推薦使用。

    • AddTestUsers

      TestUserStore基於TestUser對象集合的注冊。 TestUserStore由默認的快速入門UI使用。還注冊IProfileService和的實現IResourceOwnerPasswordValidator

附加服務

    • AddExtensionGrantValidator

      添加IExtensionGrantValidator實現以用於擴展授權。

    • AddSecretParser

      添加ISecretParser用於解析客戶端或API資源憑據的實現。

    • AddSecretValidator

      添加ISecretValidator用於針對憑證存儲驗證客戶端或API資源憑證的實現。

    • AddResourceOwnerValidator

      添加IResourceOwnerPasswordValidator用於驗證資源所有者密碼憑據授予類型的用戶憑據的實現。

    • AddProfileService

      添加IProfileService用於連接到自定義用戶配置文件存儲的實現。的DefaultProfileService類提供了依賴於驗證cookie作為權利要求中的用於在令牌發行的唯一來源的默認實現。

    • AddAuthorizeInteractionResponseGenerator

      添加IAuthorizeInteractionResponseGenerator實現以在授權端點處定制邏輯,以便在必須向用戶顯示錯誤,登錄,同意或任何其他自定義頁面的UI時。本AuthorizeInteractionResponseGenerator類提供了一個默認的實現,因此考慮從這個現有的類派生如果需要增強現有的行為。

    • AddCustomAuthorizeRequestValidator

      添加ICustomAuthorizeRequestValidator實現以在授權端點處自定義請求參數驗證。

    • AddCustomTokenRequestValidator

      添加ICustomTokenRequestValidator實現以在令牌端點處自定義請求參數驗證。

    • AddRedirectUriValidator

      添加IRedirectUriValidator實現以自定義重定向URI驗證。

    • AddAppAuthRedirectUriValidator

      添加符合重定向URI驗證器的“AppAuth”(適用於Native Apps的OAuth 2.0)(進行嚴格驗證,但也允許帶有隨機端口的http://127.0.0.1)。

    • AddJwtBearerClientAuthentication

      使用JWT承載斷言添加對客戶端身份驗證的支持。

緩存

IdentityServer經常使用客戶端和資源配置數據。如果從數據庫或其他外部存儲加載此數據,則頻繁重新加載相同數據可能會很昂貴。

    • AddInMemoryCaching

      要使用下面描述的任何緩存,ICache<T>必須在DI中注冊實現。此API注冊了ICache<T>基於ASP.NET Core的默認內存實現MemoryCache

    • AddClientStoreCache

      注冊IClientStore裝飾器實現,該實現將維護Client配置對象的內存緩存。緩存持續時間可在Caching配置選項上配置IdentityServerOptions

    • AddResourceStoreCache

      注冊一個IResourceStore裝飾器實現,它將維護內存緩存IdentityResourceApiResource配置對象。緩存持續時間可在Caching配置選項上配置IdentityServerOptions

    • AddCorsPolicyCache

      注冊ICorsPolicyService裝飾器實現,該實現將維護CORS策略服務評估結果的內存緩存。緩存持續時間可在Caching配置選項上配置IdentityServerOptions

可以進一步自定義緩存:

默認緩存依賴於ICache<T>實現。如果要自定義特定配置對象的緩存行為,可以在依賴項注入系統中替換此實現。

ICache<T>本身的默認實現依賴於.NET提供的IMemoryCache接口(和MemoryCache實現)。如果要自定義內存中緩存行為,可以替換IMemoryCache依賴項注入系統中的實現。

配置管道

您需要通過調用以下方法將IdentityServer添加到管道:

public void Configure(IApplicationBuilder app)
{
  app.UseIdentityServer();
}

注意

UseIdentityServer包括打電話UseAuthentication,因此沒有必要同時使用。

中間件沒有其他配置。

請注意,訂單在管道中很重要。例如,您需要在實現登錄屏幕的UI框架之前添加IdentitySever。

定義資源

您通常在系統中定義的第一件事是您要保護的資源。這可能是您的用戶的身份信息,如個人資料數據或電子郵件地址,或訪問API。

注意

您可以使用C#對象模型定義資源 - 或從數據存儲加載它們。的實施IResourceStore與這些低級別的細節交易。對於本文檔,我們使用內存中實現。

定義身份資源

身份資源是用戶的用戶ID,名稱或電子郵件地址等數據。標識資源具有唯一名稱,您可以為其分配任意聲明類型。然后,這些聲明將包含在用戶的身份令牌中。客戶端將使用該scope參數來請求訪問標識資源。

OpenID Connect規范指定了幾個標准身份資源。最低要求是,您為用戶發送唯一ID提供支持 - 也稱為主題ID。這是通過公開名為的標准身份資源來完成的openid

public static IEnumerable<IdentityResource> GetIdentityResources()
{
  return new List<IdentityResource>
  {
      new IdentityResources.OpenId()
  };
}

該IdentityResources類支持的規范(OpenID的,電子郵件,個人資料,電話和地址)中定義的所有范圍。如果您想全部支持它們,可以將它們添加到支持的身份資源列表中:

public static IEnumerable<IdentityResource> GetIdentityResources()
{
  return new List<IdentityResource>
  {
      new IdentityResources.OpenId(),
      new IdentityResources.Email(),
      new IdentityResources.Profile(),
      new IdentityResources.Phone(),
      new IdentityResources.Address()
  };
}

定義自定義標識資源

您還可以定義自定義標識資源。創建一個新的IdentityResource類,為其命名,並可選擇顯示名稱和描述,並定義在請求此資源時應將哪些用戶聲明包含在身份令牌中:

public static IEnumerable<IdentityResource> GetIdentityResources()
{
  var customProfile = new IdentityResource(
      name: "custom.profile",
      displayName: "Custom profile",
      claimTypes: new[] { "name", "email", "status" });

  return new List<IdentityResource>
  {
      new IdentityResources.OpenId(),
      new IdentityResources.Profile(),
      customProfile
  };
}

有關身份資源設置的更多信息,請參閱參考部分。

定義API資源

要允許客戶端請求API的訪問令牌,您需要定義API資源,例如:

要獲取API的訪問權限,您還需要將它們注冊為范圍。這次范圍類型是Resource類型:

public static IEnumerable<ApiResource> GetApis()
{
  return new[]
  {
      // simple API with a single scope (in this case the scope name is the same as the api name)
      new ApiResource("api1", "Some API 1"),

      // expanded version if more control is needed
      new ApiResource
      {
          Name = "api2",

          // secret for using introspection endpoint
          ApiSecrets =
          {
              new Secret("secret".Sha256())
          },

          // include the following using claims in access token (in addition to subject id)
          UserClaims = { JwtClaimTypes.Name, JwtClaimTypes.Email },

          // this API defines two scopes
          Scopes =
          {
              new Scope()
              {
                  Name = "api2.full_access",
                  DisplayName = "Full access to API 2",
              },
              new Scope
              {
                  Name = "api2.read_only",
                  DisplayName = "Read only access to API 2"
              }
          }
      }
  };
}

有關API資源設置的更多信息,請參閱參考部分。

注意

由資源定義的用戶聲明由IProfileService擴展點加載。

定義客戶端

客戶端表示可以從您的身份服務器請求令牌的應用程序。

詳細信息各不相同,但您通常會為客戶端定義以下常用設置:

  • 唯一的客戶ID

  • 如果需要的秘密

  • 允許與令牌服務的交互(稱為授權類型)

  • 發送身份和/或訪問令牌的網絡位置(稱為重定向URI)

  • 允許客戶端訪問的范圍列表(也稱為資源)

注意

在運行時,通過實現來檢索客戶端IClientStore。這允許從任意數據源(如配置文件或數據庫)加載它們。對於本文檔,我們將使用客戶端存儲的內存版本。您可以ConfigureServices通過AddInMemoryClientsextensions方法連接內存存儲。

定義服務器到服務器通信的客戶端

在這種情況下,沒有交互式用戶 - 服務(也稱為客戶端)想要與API(aka范圍)進行通信:

public class Clients
{
  public static IEnumerable<Client> Get()
  {
      return new List<Client>
      {
          new Client
          {
              ClientId = "service.client",
              ClientSecrets = { new Secret("secret".Sha256()) },

              AllowedGrantTypes = GrantTypes.ClientCredentials,
              AllowedScopes = { "api1", "api2.read_only" }
          }
      };
  }
}

定義基於瀏覽器的JavaScript客戶端(例如SPA)以進行用戶身份驗證和委派訪問以及

此客戶端使用所謂的隱式流來從JavaScript請求身份和訪問令牌:

var jsClient = new Client
{
  ClientId = "js",
  ClientName = "JavaScript Client",
  ClientUri = "http://identityserver.io",

  AllowedGrantTypes = GrantTypes.Implicit,
  AllowAccessTokensViaBrowser = true,

  RedirectUris =           { "http://localhost:7017/index.html" },
  PostLogoutRedirectUris = { "http://localhost:7017/index.html" },
  AllowedCorsOrigins =     { "http://localhost:7017" },

  AllowedScopes =
  {
      IdentityServerConstants.StandardScopes.OpenId,
      IdentityServerConstants.StandardScopes.Profile,
      IdentityServerConstants.StandardScopes.Email,

      "api1", "api2.read_only"
  }
};

 

定義服務器端Web應用程序(例如MVC)以進行使用身份驗證和委托API訪問

交互式服務器端(或本機桌面/移動)應用程序使用混合流。此流程為您提供最佳安全性,因為訪問令牌僅通過反向通道調用傳輸(並允許您訪問刷新令牌):

var mvcClient = new Client
{
  ClientId = "mvc",
  ClientName = "MVC Client",
  ClientUri = "http://identityserver.io",

  AllowedGrantTypes = GrantTypes.Hybrid,
  AllowOfflineAccess = true,
  ClientSecrets = { new Secret("secret".Sha256()) },

  RedirectUris =           { "http://localhost:21402/signin-oidc" },
  PostLogoutRedirectUris = { "http://localhost:21402/" },
  FrontChannelLogoutUri = "http://localhost:21402/signout-oidc",

  AllowedScopes =
  {
      IdentityServerConstants.StandardScopes.OpenId,
      IdentityServerConstants.StandardScopes.Profile,
      IdentityServerConstants.StandardScopes.Email,

      "api1", "api2.read_only"
  },
};

登錄

為了使IdentityServer能夠代表用戶發出令牌,該用戶必須登錄IdentityServer。

Cookie認證

使用由ASP.NET Core中的cookie身份驗證處理程序管理的cookie來跟蹤身份驗證

IdentityServer注冊了兩個cookie處理程序(一個用於身份驗證會話,另一個用於臨時外部cookie)。默認情況下使用它們,如果要手動引用它們,可以從IdentityServerConstants類(DefaultCookieAuthenticationSchemeExternalCookieAuthenticationScheme)中獲取它們的名稱。

我們只公開這些cookie的基本設置(到期和滑動),如果您需要更多控制,您可以注冊自己的cookie處理程序。IdentityServer使用與使用ASP.NET Core 時DefaultAuthenticateScheme配置的cookie處理程序相匹配的cookie處理程序。AuthenticationOptions``AddAuthentication

覆蓋cookie處理程序配置

如果您希望使用自己的cookie身份驗證處理程序,則必須自己配置它。這必須ConfigureServices在DI(with AddIdentityServer)中注冊IdentityServer之后完成。例如:

services.AddIdentityServer()
  .AddInMemoryClients(Clients.Get())
  .AddInMemoryIdentityResources(Resources.GetIdentityResources())
  .AddInMemoryApiResources(Resources.GetApiResources())
  .AddDeveloperSigningCredential()
  .AddTestUsers(TestUsers.Users);

services.AddAuthentication("MyCookie")
  .AddCookie("MyCookie", options =>
  {
      options.ExpireTimeSpan = ...;
  });

注意

IdentityServer在內部調用兩個AddAuthenticationAddCookie使用自定義方案(通過常量IdentityServerConstants.DefaultCookieAuthenticationScheme),因此要覆蓋它們,您必須在之后進行相同的調用AddIdentityServer

登錄用戶界面和身份管理系統

IdentityServer不為用戶身份驗證提供任何用戶界面或用戶數據庫。這些是您希望自己提供或發展的東西。

如果您需要基本UI的起點(登錄,注銷,同意和管理授權),您可以使用我們的快速入門UI

快速入門UI針對內存數據庫對用戶進行身份驗證。您可以通過訪問真實用戶存儲來替換這些位。我們有使用ASP.NET Identity的示例。

登錄工作流程

當IdentityServer在授權端點收到請求且未對用戶進行身份驗證時,將將用戶重定向到已配置的登錄頁面。您必須通過選項上的UserInteraction設置(默認為)通知IdentityServer登錄頁面的路徑。將傳遞一個參數,通知您的登錄頁面,登錄完成后應重定向用戶。/account/login``returnUrl

注意

通過參數注意開放重定向攻擊returnUrl。您應該驗證returnUrl引用的是眾所周知的位置。請參閱API 的交互服務以驗證returnUrl參數。

登錄上下文

在您的登錄頁面上,您可能需要有關請求上下文的信息,以便自定義登錄體驗(例如客戶端,提示參數,IdP提示或其他內容)。這可以通過交互服務GetAuthorizationContextAsync上的API獲得。

發行cookie和聲明

HttpContextASP.NET Core 上有與身份驗證相關的擴展方法,用於發出身份驗證cookie並對用戶進行簽名。使用的身份驗證方案必須與您正在使用的cookie處理程序匹配(請參見上文)。

當您簽署用戶時,您必須至少發出sub索賠和name索賠。IdentityServer還提供了一些SignInAsync擴展方法HttpContext,使其更加方便。

您還可以選擇發出idp聲明(針對身份提供者名稱),amr聲明(針對所使用的身份驗證方法)和/或auth_time聲明(針對用戶身份驗證的紀元時間)。如果您不提供這些,則IdentityServer將提供默認值。

使用外部身份提供商登錄

ASP.NET Core有一種靈活的方式來處理外部身份驗證。這涉及幾個步驟。

注意

如果您使用的是ASP.NET標識,則會隱藏許多基礎技術細節。建議您還閱讀Microsoft 文檔並執行ASP.NET Identity 快速入門

為外部提供者添加身份驗證處理程序

與外部提供者通信所需的協議實現封裝在身份驗證處理程序中。一些提供商使用專有協議(例如Facebook等社交提供商),有些提供商使用標准協議,例如OpenID Connect,WS-Federation或SAML2p。

有關添加外部身份驗證和配置它的分步說明,請參閱此快速入門

cookies的作用

調用外部身份驗證處理程序的一個選項SignInScheme,例如:

services.AddAuthentication()
  .AddGoogle("Google", options =>
  {
      options.SignInScheme = "scheme of cookie handler to use";

      options.ClientId = "...";
      options.ClientSecret = "...";
  })

登錄方案指定將臨時存儲外部認證結果的cookie處理程序的名稱,例如由外部提供者發送的聲明。這是必要的,因為在完成外部身份驗證過程之前通常會涉及一些重定向。

鑒於這是一種常見做法,IdentityServer專門為此外部提供程序工作流注冊cookie處理程序。該方案通過IdentityServerConstants.ExternalCookieAuthenticationScheme常數表示。如果您要使用我們的外部cookie處理程序,那么對於SignInScheme上面的內容,您將賦值為IdentityServerConstants.ExternalCookieAuthenticationScheme常量:

services.AddAuthentication()
  .AddGoogle("Google", options =>
  {
      options.SignInScheme = IdentityServerConstants.ExternalCookieAuthenticationScheme;

      options.ClientId = "...";
      options.ClientSecret = "...";
  })

您也可以注冊自己的自定義cookie處理程序,如下所示:

services.AddAuthentication()
  .AddCookie("YourCustomScheme")
  .AddGoogle("Google", options =>
  {
      options.SignInScheme = "YourCustomScheme";

      options.ClientId = "...";
      options.ClientSecret = "...";
  })

注意

對於特殊情況,您還可以將外部cookie機制短路並將外部用戶直接轉發到主cookie處理程序。這通常涉及處理外部處理程序上的事件,以確保您從外部標識源執行正確的聲明轉換。

觸發認證處理程序

您可以通過(或使用MVC )ChallengeAsync上的擴展方法調用外部認證處理程序。HttpContext``ChallengeResult

您通常希望將一些選項傳遞給挑戰操作,例如回調頁面的路徑和簿記提供者的名稱,例如:

var callbackUrl = Url.Action("ExternalLoginCallback");

var props = new AuthenticationProperties
{
  RedirectUri = callbackUrl,
  Items =
  {
      { "scheme", provider },
      { "returnUrl", returnUrl }
  }
};

return Challenge(provider, props);

處理回調並簽署用戶

在回調頁面上,您的典型任務是:

  • 檢查外部提供商返回的身份。

  • 決定如何處理該用戶。如果這是新用戶或返回用戶,則可能會有所不同。

  • 新用戶在被允許之前可能需要額外的步驟和UI。

  • 可能會創建一個鏈接到外部提供程序的新內部用戶帳戶。

  • 存儲您要保留的外部聲明。

  • 刪除臨時cookie

  • 登錄用戶

檢查外部身份

// read external identity from the temporary cookie
var result = await HttpContext.AuthenticateAsync(IdentityServerConstants.ExternalCookieAuthenticationScheme);
if (result?.Succeeded != true)
{
  throw new Exception("External authentication error");
}

// retrieve claims of the external user
var externalUser = result.Principal;
if (externalUser == null)
{
  throw new Exception("External authentication error");
}

// retrieve claims of the external user
var claims = externalUser.Claims.ToList();

// try to determine the unique id of the external user - the most common claim type for that are the sub claim and the NameIdentifier
// depending on the external provider, some other claim type might be used
var userIdClaim = claims.FirstOrDefault(x => x.Type == JwtClaimTypes.Subject);
if (userIdClaim == null)
{
  userIdClaim = claims.FirstOrDefault(x => x.Type == ClaimTypes.NameIdentifier);
}
if (userIdClaim == null)
{
  throw new Exception("Unknown userid");
}

var externalUserId = userIdClaim.Value;
var externalProvider = userIdClaim.Issuer;

// use externalProvider and externalUserId to find your user, or provision a new user

清理和登錄

// issue authentication cookie for user
await HttpContext.SignInAsync(user.SubjectId, user.Username, provider, props, additionalClaims.ToArray());

// delete temporary cookie used during external authentication
await HttpContext.SignOutAsync(IdentityServerConstants.ExternalCookieAuthenticationScheme);

// validate return URL and redirect back to authorization endpoint or a local page
if (_interaction.IsValidReturnUrl(returnUrl) || Url.IsLocalUrl(returnUrl))
{
  return Redirect(returnUrl);
}

return Redirect("~/");

國家,URL長度和ISecureDataFormat

重定向到外部提供程序以進行登錄時,必須經常從客戶端應用程序進行往返狀態。這意味着在離開客戶端之前捕獲狀態並保留狀態,直到用戶返回到客戶端應用程序。許多協議(包括OpenID Connect)允許將某種狀態作為參數傳遞作為請求的一部分,並且身份提供者將在響應上返回該狀態。ASP.NET Core提供的OpenID Connect身份驗證處理程序利用了協議的這一功能,這就是它實現上述returnUrl功能的方式。

在請求參數中存儲狀態的問題是請求URL可能變得太大(超過2000個字符的公共限制)。OpenID Connect身份驗證處理程序確實提供了一個可擴展點,用於在服務器中而不是在請求URL中存儲狀態。您可以通過ISecureDataFormat<AuthenticationProperties>OpenIdConnectOptions上實現和配置它來自行實現

幸運的是,IdentityServer為您提供了一個實現,由IDistributedCacheDI容器中注冊的實現(例如標准MemoryDistributedCache)支持。要使用IdentityServer提供的安全數據格式實現,只需在配置DI時調用AddOidcStateDataFormatterCache擴展方法IServiceCollection。如果未傳遞任何參數,則配置的所有OpenID Connect處理程序將使用IdentityServer提供的安全數據格式實現:

public void ConfigureServices(IServiceCollection services)
{
  // configures the OpenIdConnect handlers to persist the state parameter into the server-side IDistributedCache.
  services.AddOidcStateDataFormatterCache();

  services.AddAuthentication()
      .AddOpenIdConnect("demoidsrv", "IdentityServer", options =>
      {
          // ...
      })
      .AddOpenIdConnect("aad", "Azure AD", options =>
      {
          // ...
      })
      .AddOpenIdConnect("adfs", "ADFS", options =>
      {
          // ...
      });
}

如果只配置特定方案,則將這些方案作為參數傳遞:

public void ConfigureServices(IServiceCollection services)
{
  // configures the OpenIdConnect handlers to persist the state parameter into the server-side IDistributedCache.
  services.AddOidcStateDataFormatterCache("aad", "demoidsrv");

  services.AddAuthentication()
      .AddOpenIdConnect("demoidsrv", "IdentityServer", options =>
      {
          // ...
      })
      .AddOpenIdConnect("aad", "Azure AD", options =>
      {
          // ...
      })
      .AddOpenIdConnect("adfs", "ADFS", options =>
      {
          // ...
      });
}

Windows身份驗證

在支持的平台上,您可以使用IdentityServer對使用Windows身份驗證的用戶進行身份驗證(例如,針對Active Directory)。當前使用以下命令托管IdentityServer時,Windows身份驗證可用:

  • 使用IIS和IIS集成包在Windows上使用Kestrel

  • Windows上的HTTP.sys服務器

在這兩種情況下,使用該方案的ChallengeAsyncAPI 都會觸發Windows身份驗證。我們的快速入門UI中的帳戶控制器實現了必要的邏輯。HttpContext``"Windows"

使用紅隼

使用Kestrel時,必須運行“后面”IIS並使用IIS集成:

var host = new WebHostBuilder()
  .UseKestrel()
  .UseUrls("http://localhost:5000")
  .UseContentRoot(Directory.GetCurrentDirectory())
  .UseIISIntegration()
  .UseStartup<Startup>()
  .Build();

使用該WebHost.CreateDefaultBuilder方法設置時,會自動配置紅隼WebHostBuilder

IIS(或IIS Express)中的虛擬目錄也必須啟用Windows並啟用匿名身份驗證。

IIS集成層將Windows身份驗證處理程序配置為DI,可以通過身份驗證服務調用。通常在IdentityServer中,建議禁用此自動行為。這是在ConfigureServices

services.Configure<IISOptions>(iis =>
{
  iis.AuthenticationDisplayName = "Windows";
  iis.AutomaticAuthentication = false;
});

注意

默認情況下,顯示名稱為空,Windows身份驗證按鈕不會顯示在快速入門UI中。如果依賴於自動發現外部提供程序,則需要設置顯示名稱。

退出

注銷IdentityServer就像刪除身份驗證cookie一樣簡單,但是為了完成聯合注銷,我們必須考慮將用戶從客戶端應用程序(甚至可能是上游身份提供商)中簽名。

刪除認證

要刪除身份驗證cookie,只需使用SignOutAsync擴展方法即可HttpContext。您將需要傳遞使用的方案(IdentityServerConstants.DefaultCookieAuthenticationScheme除非您已更改,否則提供此方案):

await HttpContext.SignOutAsync(IdentityServerConstants.DefaultCookieAuthenticationScheme);

或者您可以使用IdentityServer提供的便捷擴展方法:

await HttpContext.SignOutAsync();

注意

通常,您應該提示用戶注銷(意味着需要POST),否則攻擊者可能會鏈接到您的注銷頁面,導致用戶自動注銷。

通知客戶端用戶已注銷

作為退出流程的一部分,您需要確保客戶端應用程序被告知用戶已退出。IdentityServer支持服務器端客戶端的前端通道規范(例如MVC),服務器端客戶端的反向通道 規范(例如MVC),以及基於瀏覽器的JavaScript客戶端的會話管理規范(例如SPA,React,Angular)等)。

前端服務器端客戶端

要通過前端通道規范從服務器端客戶端應用程序注銷用戶,IdentityServer中的“已注銷”頁面必須呈現<iframe>以通知客戶端用戶已注銷。希望收到通知的客戶端必須FrontChannelLogoutUri設置配置值。IdentityServer跟蹤用戶已登錄的客戶端,並提供GetLogoutContextAsyncIIdentityServerInteractionService詳細信息)上調用的API 。此API返回一個LogoutRequest對象,該對象具有SignOutIFrameUrl您已注銷的頁面必須呈現為的屬性<iframe>

反向通道服務器端客戶端

要通過反向通道規范從服務器端客戶端應用程序注銷用戶SignOutIFrameUrl,IdentityServer中的端點將自動觸發服務器到服務器調用,將簽名的注銷請求傳遞給客戶端。這意味着即使如果沒有前面通道的客戶端中,“退出”,在IdentityServer頁仍必須渲染<iframe>SignOutIFrameUrl如上所述。希望收到通知的客戶端必須BackChannelLogoutUri設置配置值。

基於瀏覽器的JavaScript客戶端

鑒於會話管理規范的設計方式,IdentityServer中沒有什么特別的,您需要做的是通知這些客戶端用戶已注銷。但是,客戶端必須對check_session_iframe執行監視,這是由oidc-client JavaScript庫實現的

由客戶端應用程序啟動的注銷

如果客戶端應用程序啟動了注銷,則客戶端首先將用戶重定向到結束會話端點。在結束會話端點處的處理可能需要通過重定向到注銷頁面來維護一些臨時狀態(例如,客戶端的注銷后重定向uri)。此狀態可能對注銷頁面有用,並且狀態的標識符通過logoutId參數傳遞到注銷頁面。

GetLogoutContextAsync上的API 交互服務可以用來加載狀態。感興趣的ShowSignoutPromptShowSignoutPrompt指示注銷請求是否已經過身份驗證,因此不會提示用戶注銷是安全的。

默認情況下,此狀態作為通過logoutId值傳遞的受保護數據結構進行管理。如果您希望在結束會話端點和注銷頁面之間使用其他一些持久性,那么您可以IMessageStore<LogoutMessage>在DI中實現並注冊實現。

退出外部身份提供商

當用戶注銷 IdentityServer,並且他們使用外部身份提供程序登錄時,可能會將其重定向到也注銷外部提供程序。並非所有外部提供商都支持注銷,因為它取決於它們支持的協議和功能。

要檢測是否必須將用戶重定向到外部身份提供程序以進行注銷通常是通過使用idp在IdentityServer中發布到cookie中的聲明來完成的。設置到此聲明中的值是AuthenticationScheme相應的身份驗證中間件。在簽出時,咨詢此索賠以了解是否需要外部簽出。

由於正常注銷工作流程已經需要清理和狀態管理,因此將用戶重定向到外部身份提供商是有問題的。然后,在IdentityServer完成正常注銷和清理過程的唯一方法是從外部身份提供程序請求在注銷后將用戶重定向回IdentityServer。並非所有外部提供商都支持退出后重定向,因為它取決於它們支持的協議和功能。

然后,簽出時的工作流程將撤消IdentityServer的身份驗證cookie,然后重定向到請求退出后重定向的外部提供程序。退出后重定向應保持此處描述的必要簽出狀態(即logoutId參數值)。要在外部提供程序注銷后重定向回IdentityServer,RedirectUri應該AuthenticationProperties在使用ASP.NET Core的SignOutAsyncAPI 時使用,例如:

[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Logout(LogoutInputModel model)
{
  // build a model so the logged out page knows what to display
  var vm = await _account.BuildLoggedOutViewModelAsync(model.LogoutId);

  var user = HttpContext.User;
  if (user?.Identity.IsAuthenticated == true)
  {
      // delete local authentication cookie
      await HttpContext.SignOutAsync();

      // raise the logout event
      await _events.RaiseAsync(new UserLogoutSuccessEvent(user.GetSubjectId(), user.GetName()));
  }

  // check if we need to trigger sign-out at an upstream identity provider
  if (vm.TriggerExternalSignout)
  {
      // build a return URL so the upstream provider will redirect back
      // to us after the user has logged out. this allows us to then
      // complete our single sign-out processing.
      string url = Url.Action("Logout", new { logoutId = vm.LogoutId });

      // this triggers a redirect to the external provider for sign-out
      return SignOut(new AuthenticationProperties { RedirectUri = url }, vm.ExternalAuthenticationScheme);
  }

  return View("LoggedOut", vm);
}

一旦用戶退出外部提供程序然后重定向回來,IdentityServer的正常注銷處理應該執行,這涉及處理logoutId和執行所有必要的清理。

聯合注銷

聯合注銷是指用戶使用外部身份提供程序登錄IdentityServer,然后用戶通過IdentityServer未知的工作流程注銷該外部身份提供程序的情況。當用戶注銷時,對IdentityServer進行通知將非常有用,這樣它就可以將用戶從IdentityServer和使用IdentityServer的所有應用程序簽名。

並非所有外部身份提供商都支持聯合注銷,但那些提供的機制將提供通知客戶端用戶已注銷的機制。此通知通常以<iframe>來自外部身份提供商的“已注銷”頁面的請求的形式出現。然后IdentityServer必須通知其所有客戶(如討論這里),也通常在一個請求的形式<iframe>從內外部身份提供的<iframe>

使聯合注銷成為特殊情況(與正常注銷相比)的原因是聯合注銷請求不是IdentityServer中的正常注銷端點。實際上,每個外部IdentityProvider都將在IdentityServer主機中具有不同的端點。這是因為每個外部身份提供者可能使用不同的協議,並且每個中間件都在不同的端點上進行偵聽。

所有這些因素的凈效果是沒有像正常注銷工作流那樣呈現“注銷”頁面,這意味着我們缺少對IdentityServer客戶端的注銷通知。我們必須為每個聯合注銷端點添加代碼,以呈現必要的通知以實現聯合注銷。

幸運的是,IdentityServer已經包含此代碼。當請求進入IdentityServer和調用外部認證提供商處理,IdentityServer檢測,如果這些聯合signout要求,如果他們是它會自動呈現相同<iframe>這里描述signout。簡而言之,自動支持聯合注銷。

聯合網關

通用架構是所謂的聯合網關。在這種方法中,IdentityServer充當一個或多個外部身份提供者的網關。

 

 

該架構具有以下優點

  • 您的應用程序只需要了解一個令牌服務(網關),並且不受有關連接到外部提供程序的所有詳細信息的影響。這也意味着您可以添加或更改這些外部提供程序,而無需更新您的應用程序。

  • 您控制網關(而不是某些外部服務提供商) - 這意味着您可以對其進行任何更改,並保護您的應用程序免受外部提供程序可能對其自己的服務所做的更改。

  • 大多數外部提供商僅支持一組固定的聲明和聲明類型 - 在中間具有網關允許對提供商的響應進行后處理以轉換/添加/修改特定於域的身份信息。

  • 某些提供商不支持訪問令牌(例如社交提供商) - 因為網關知道您的API,它可以根據外部身份發出訪問令牌。

  • 某些提供商按您連接的應用程序數收費。網關充當外部提供程序的單個應用程序。在內部,您可以根據需要連接任意數量的應用程序。

  • 一些提供商使用專有協議或對標准協議進行專有修改 - 通過網關,您只需要處理一個地方。

  • 強制每個身份驗證(內部或外部)通過一個地方為身份映射提供極大的靈活性,為您的所有應用程序提供穩定的身份並處理新的需求

換句話說 - 擁有聯合網關可以讓您對身份基礎架構進行大量控制。由於您的用戶身份是您最重要的資產之一,我們建議您控制網關。

實施

我們的快速入門UI使用了以下一些功能。另請參閱外部身份驗證快速入門和有關外部提供程序的文檔。

  • 您可以通過向IdentityServer應用程序添加身份驗證處理程序來添加對外部身份提供程序的支持。

  • 你可以通過調用程序查詢這些外部供應商IAuthenticationSchemeProvider。這允許基於已注冊的外部提供程序動態呈現您的登錄頁面。

  • 我們的客戶端配置模型允許基於每個客戶端限制可用的提供者(使用該IdentityProviderRestrictions屬性)。

  • 您還可以使用EnableLocalLogin客戶端上的屬性告訴您的UI是否應該呈現用戶名/密碼輸入。

  • 我們的快速啟動UI漏斗通過一個回調所有外部認證調用(見ExternalLoginCallbackAccountController類)。這允許單點進行后處理。

同意

在授權請求期間,如果IdentityServer需要用戶同意,則瀏覽器將被重定向到同意頁面。

同意用於允許最終用戶授予客戶端對資源(身份API)的訪問權限。這通常僅對第三方客戶端是必需的,並且可以在客戶端設置上按客戶端啟用/禁用。

同意頁

為了讓用戶同意,托管應用程序必須提供同意頁面。該快速入門UI有一個批准頁面的基本實現。

同意頁面通常呈現當前用戶的顯示名稱,請求訪問的客戶端的顯示名稱,客戶端的徽標,有關客戶端的更多信息的鏈接以及客戶端請求訪問的資源列表。允許用戶表明他們的同意應該被“記住”也是很常見的,因此將來不會再次提示同一客戶。

一旦用戶提供了同意,同意頁面必須通知IdentityServer同意,然后必須將瀏覽器重定向回授權端點。

授權上下文

IdentityServer將returnUrl參數(可在用戶交互選項上配置)傳遞到包含授權請求參數的同意頁面。這些參數提供了同意頁面的上下文,可以在交互服務的幫助下閱讀。該GetAuthorizationContextAsyncAPI將返回的實例AuthorizationRequest

可以使用IClientStoreIResourceStore接口獲取有關客戶端或資源的其他詳細信息。

通知IdentityServer同意結果

GrantConsentAsync對API 的交互服務允許同意頁面知情同意的結果(這也可能拒絕客戶端訪問)的IdentityServer。

IdentityServer將暫時保留同意的結果。這種持久性默認使用cookie,因為它只需要持續足夠長的時間來將結果傳回給授權端點。這種臨時持久性與用於“記住我的同意”功能的持久性不同(並且授權端點持續“記住我對用戶的同意”)。如果您希望在同意頁面和授權重定向之間使用其他一些持久性,那么您可以IMessageStore<ConsentResponse>在DI中實現並注冊實現。

將用戶返回到授權端點

一旦同意頁面通知IdentityServer結果,就可以將用戶重定向回returnUrl。您的同意頁面應通過驗證returnUrl是否有效來防止打開重定向。這可以通過調用來完成IsValidReturnUrl交互服務。此外,如果GetAuthorizationContextAsync返回非null結果,那么您還可以信任returnUrl有效。

保護

IdentityServer 默認以JWT(JSON Web令牌)格式發出訪問令牌。

今天的每個相關平台都支持驗證JWT令牌,這里可以找到一個很好的JWT庫列表。熱門圖書館例如:

保護基於ASP.NET核心的API只需在DI中配置JWT承載認證處理程序,並將認證中間件添加到管道:

public class Startup
{
  public void ConfigureServices(IServiceCollection services)
  {
      services.AddMvc();

      services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
          .AddJwtBearer(options =>
          {
              // base-address of your identityserver
              options.Authority = "https://demo.identityserver.io";

              // name of the API resource
              options.Audience = "api1";
          });
  }

  public void Configure(IApplicationBuilder app, ILoggerFactory loggerFactory)
  {
      app.UseAuthentication();
      app.UseMvc();
  }
}

IdentityServer身份驗證處理程序

我們的身份驗證處理程序與上面的處理程序具有相同的用途(實際上它在內部使用Microsoft JWT庫),但添加了一些其他功能:

  • 支持JWT和參考令牌

  • 用於引用標記的可擴展緩存

  • 統一配置模型

  • 范圍驗證

對於最簡單的情況,我們的處理程序配置看起來非常類似於上面的代碼段:

public class Startup
{
  public void ConfigureServices(IServiceCollection services)
  {
      services.AddMvc();

      services.AddAuthentication(IdentityServerAuthenticationDefaults.AuthenticationScheme)
          .AddIdentityServerAuthentication(options =>
          {
              // base-address of your identityserver
              options.Authority = "https://demo.identityserver.io";

              // name of the API resource
              options.ApiName = "api1";
          });
  }

  public void Configure(IApplicationBuilder app, ILoggerFactory loggerFactory)
  {
      app.UseAuthentication();
      app.UseMvc();
  }
}

你可以從nugetgithub獲得包。

支持引用標記

如果傳入令牌不是JWT,我們的中間件將聯系發現文檔中的內省端點以驗證令牌。由於內省端點需要身份驗證,因此您需要提供已配置的API密鑰,例如:

.AddIdentityServerAuthentication(options =>
{
  // base-address of your identityserver
  options.Authority = "https://demo.identityserver.io";

  // name of the API resource
  options.ApiName = "api1";
  options.ApiSecret = "secret";
})

通常,您不希望為每個傳入請求執行到內省端點的往返。中間件有一個內置緩存,您可以像這樣啟用:

.AddIdentityServerAuthentication(options =>
{
  // base-address of your identityserver
  options.Authority = "https://demo.identityserver.io";

  // name of the API resource
  options.ApiName = "api1";
  options.ApiSecret = "secret";

  options.EnableCaching = true;
  options.CacheDuration = TimeSpan.FromMinutes(10); // that's the default
})

處理程序將使用在DI容器中注冊的任何IDistributedCache實現(例如標准的MemoryDistributedCache)。

驗證范圍

所述ApiName屬性檢查該令牌具有匹配觀眾(或短aud),如權利要求。

在IdentityServer中,您還可以將API細分為多個范圍。如果需要該粒度,可以使用ASP.NET Core授權策略系統來檢查范圍。

制定全球政策

services
  .AddMvcCore(options =>
  {
      // require scope1 or scope2
      var policy = ScopePolicy.Create("scope1", "scope2");
      options.Filters.Add(new AuthorizeFilter(policy));
  })
  .AddJsonFormatters()
  .AddAuthorization();

制定范圍政策

services.AddAuthorization(options =>
{
  options.AddPolicy("myPolicy", builder =>
  {
      // require scope1
      builder.RequireScope("scope1");
      // and require scope2 or scope3
      builder.RequireScope("scope2", "scope3");
  });
});

部署

您的身份服務器只是一個標准的ASP.NET核心應用程序,包括IdentityServer中間件。首先閱讀有關發布和部署的官方Microsoft 文檔

典型架構

通常,您將設計IdentityServer部署以實現高可用性:

 

IdentityServer本身是無狀態的,不需要服務器關聯 - 但是有些數據需要在實例之間共享。

配置數據

這通常包括:

  • 資源

  • 客戶

  • 啟動配置,例如密鑰材料,外部提供商設置等......

存儲數據的方式取決於您的環境。在配置數據很少更改的情況下,我們建議使用內存存儲和代碼或配置文件。

在高度動態的環境(例如Saas)中,我們建議使用數據庫或配置服務動態加載配置。

IdentityServer支持開箱即用的代碼配置和配置文件(請參閱此處)。對於數據庫,我們為基於Entity Framework Core的數據庫提供支持。

您還可以通過實現IResourceStore和構建自己的配置存儲IClientStore

密鑰材料

另一個重要的啟動配置是您的主要材料,請參閱此處以獲取有關密鑰材料和加密的更多詳細信息。

運營數據

對於某些操作,IdentityServer需要持久性存儲來保持狀態,這包括:

  • 發布授權碼

  • 發出引用和刷新令牌

  • 存儲同意

您可以使用傳統數據庫存儲操作數據,也可以使用具有Redis等持久性功能的緩存。上面提到的EF Core實現也支持運營數據。

您還可以通過實現實現對自己的自定義存儲機制的支持IPersistedGrantStore- 默認情況下IdentityServer會注入內存中的版本。

ASP.NET核心數據保護

ASP.NET Core本身需要共享密鑰材料來保護cookie,狀態字符串等敏感數據。請參閱此處的官方文檔。

您可以重復使用上述持久性存儲之一,也可以使用像共享文件這樣的簡單文件。

記錄

IdentityServer使用ASP.NET Core提供的標准日志記錄工具。Microsoft 文檔有一個很好的介紹和內置日志記錄提供程序的說明。

我們大致遵循Microsoft使用日志級別的指導原則:

  • Trace僅供開發人員解決問題的信息。這些消息可能包含敏感的應用程序數據(如令牌),不應在生產環境中啟用。

  • Debug遵循內部流程並理解為何做出某些決定。在開發和調試過程中具有短期實用性。

  • Information用於跟蹤應用程序的一般流程。這些日志通常具有一些長期價值。

  • Warning對於應用程序流中的異常或意外事件。這些可能包括錯誤或其他不會導致應用程序停止但可能需要調查的條件。

  • Error對於無法處理的錯誤和異常。示例:協議請求的驗證失敗。

  • Critical對於需要立即關注的故障。示例:缺少商店實施,無效的密鑰材料......

Serilog的設置

我們個人非常喜歡Serilog。試試看。

ASP.NET Core2.0+

對於以下配置,您需要Serilog.AspNetCoreSerilog.Sinks.Console包:

public class Program
{
  public static void Main(string[] args)
  {
      Console.Title = "IdentityServer4";

      Log.Logger = new LoggerConfiguration()
          .MinimumLevel.Debug()
          .MinimumLevel.Override("Microsoft", LogEventLevel.Warning)
          .MinimumLevel.Override("System", LogEventLevel.Warning)
          .MinimumLevel.Override("Microsoft.AspNetCore.Authentication", LogEventLevel.Information)
          .Enrich.FromLogContext()
          .WriteTo.Console(outputTemplate: "[{Timestamp:HH:mm:ss} {Level}] {SourceContext}{NewLine}{Message:lj}{NewLine}{Exception}{NewLine}", theme: AnsiConsoleTheme.Literate)
          .CreateLogger();

      BuildWebHost(args).Run();
  }

  public static IWebHost BuildWebHost(string[] args)
  {
      return WebHost.CreateDefaultBuilder(args)
              .UseStartup<Startup>()
              .UseSerilog()
              .Build();
  }
}

.NET

對於以下配置,您需要Serilog.Extensions.LoggingSerilog.Sinks.Console包:

public class Program
{
  public static void Main(string[] args)
  {
      Console.Title = "IdentityServer4";

      Log.Logger = new LoggerConfiguration()
          .MinimumLevel.Debug()
          .MinimumLevel.Override("Microsoft", LogEventLevel.Warning)
          .MinimumLevel.Override("System", LogEventLevel.Warning)
          .MinimumLevel.Override("Microsoft.AspNetCore.Authentication", LogEventLevel.Information)
          .Enrich.FromLogContext()
          .WriteTo.Console(outputTemplate: "[{Timestamp:HH:mm:ss} {Level}] {SourceContext}{NewLine}{Message:lj}{NewLine}{Exception}{NewLine}", theme: AnsiConsoleTheme.Literate)
          .CreateLogger();

      BuildWebHost(args).Run();
  }

  public static IWebHost BuildWebHost(string[] args)
  {
      return WebHost.CreateDefaultBuilder(args)
              .UseStartup<Startup>()
              .ConfigureLogging(builder =>
              {
                  builder.ClearProviders();
                  builder.AddSerilog();
              })
              .Build();
  }
}

事件

日志記錄是更低級別的“printf”樣式 - 事件代表有關IdentityServer中某些操作的更高級別信息。事件是結構化數據,包括事件ID,成功/失敗信息,類別和詳細信息。這使得查詢和分析它們變得容易,並提取可用於進一步處理的有用信息。

活動適用於ELKSeqSplunk等活動商店。

發出事件

默認情況下不會啟用事件 - 但可以在ConfigureServices方法中進行全局配置,例如:

services.AddIdentityServer(options =>
{
  options.Events.RaiseSuccessEvents = true;
  options.Events.RaiseFailureEvents = true;
  options.Events.RaiseErrorEvents = true;
});

要發出事件,請使用IEventServiceDI容器並調用RaiseAsync方法,例如:

public async Task<IActionResult> Login(LoginInputModel model)
{
  if (_users.ValidateCredentials(model.Username, model.Password))
  {
      // issue authentication cookie with subject ID and username
      var user = _users.FindByUsername(model.Username);
      await _events.RaiseAsync(new UserLoginSuccessEvent(user.Username, user.SubjectId, user.Username));
  }
  else
  {
      await _events.RaiseAsync(new UserLoginFailureEvent(model.Username, "invalid credentials"));
  }
}

自定義接收器

我們的默認事件接收器只是將事件類序列化為JSON並將其轉發到ASP.NET Core日志系統。如果要連接到自定義事件存儲,請實現該IEventSink接口並將其注冊到DI。

以下示例使用Seq發出事件:

 public class SeqEventSink : IEventSink
{
  private readonly Logger _log;

  public SeqEventSink()
  {
      _log = new LoggerConfiguration()
          .WriteTo.Seq("http://localhost:5341")
          .CreateLogger();
  }

  public Task PersistAsync(Event evt)
  {
      if (evt.EventType == EventTypes.Success ||
          evt.EventType == EventTypes.Information)
      {
          _log.Information("{Name} ({Id}), Details: {@details}",
              evt.Name,
              evt.Id,
              evt);
      }
      else
      {
          _log.Error("{Name} ({Id}), Details: {@details}",
              evt.Name,
              evt.Id,
              evt);
      }

      return Task.CompletedTask;
  }
}

Serilog.Sinks.Seq包添加到主機以使上述代碼有效。

內置事件

IdentityServer中定義了以下事件:

  • ApiAuthenticationFailureEventApiAuthenticationSuccessEvent

    獲取內省端點上成功/失敗的API身份驗證。

  • ClientAuthenticationSuccessEventClientAuthenticationFailureEvent

    獲取在令牌端點處成功/失敗的客戶端身份驗證。

  • TokenIssuedSuccessEventTokenIssuedFailureEvent

    獲取成功/失敗嘗試請求身份令牌,訪問令牌,刷新令牌和授權碼。

  • TokenIntrospectionSuccessEventTokenIntrospectionFailureEvent

    獲取成功的令牌內省請求。

  • TokenRevokedSuccessEvent

    獲取成功的令牌吊銷請求。

  • UserLoginSuccessEventUserLoginFailureEvent

    成功/失敗用戶登錄的快速入門UI引發。

  • UserLogoutSuccessEvent

    獲取成功的注銷請求。

  • ConsentGrantedEventConsentDeniedEvent

    在同意UI中引發。

  • UnhandledExceptionEvent

    獲取未處理的異常。

自定義事件

您可以創建自己的事件並通過我們的基礎架構發出它們。

您需要從我們的基Event類派生,該基類注入活動ID,時間戳等上下文信息。您的派生類可以添加特定於事件上下文的任意數據字段:

public class UserLoginFailureEvent : Event
{
  public UserLoginFailureEvent(string username, string error)
      : base(EventCategories.Authentication,
              "User Login Failure",
              EventTypes.Failure,
              EventIds.UserLoginFailure,
              error)
  {
      Username = username;
  }

  public string Username { get; set; }
}

密碼學,密鑰和

IdentityServer依賴於幾個加密機制來完成其工作。

令牌簽名和驗證

IdentityServer需要非對稱密鑰對來簽署和驗證JWT。此密鑰對可以是證書/私鑰組合或原始RSA密鑰。無論如何,它必須支持帶有SHA256的RSA。

加載簽名密鑰和相應的驗證部分是通過ISigningCredentialStore和的實現來完成的IValidationKeysStore。如果要自定義加載密鑰,可以實現這些接口並將其注冊到DI。

DI構建器擴展有幾種方便的方法來設置簽名和驗證密鑰 - 請參閱此處

簽名密鑰翻轉

雖然一次只能使用一個簽名密鑰,但您可以將多個驗證密鑰發布到發現文檔。這對於密鑰翻轉很有用。

翻轉通常如下所示:

  1. 您請求/創建新的密鑰材料

  2. 除了當前的驗證密鑰之外,還要發布新的驗證密鑰。您可以使用AddValidationKeys構建器擴展方法。

  3. 所有客戶端和API現在都有機會在下次更新發現文檔的本地副本時了解新密鑰

  4. 在一定時間(例如24小時)之后,所有客戶端和API現在應該接受舊密鑰材料和新密鑰材料

  5. 只要你願意,就可以保留舊的密鑰材料,也許你有需要驗證的長壽命令牌

  6. 當舊密鑰材料不再使用時,將其退出

  7. 所有客戶端和API將在下次更新發現文檔的本地副本時“忘記”舊密鑰

這要求客戶端和API使用發現文檔,並且還具有定期刷新其配置的功能。

數據保護

ASP.NET Core中的Cookie身份驗證(或MVC中的防偽)使用ASP.NET Core數據保護功能。根據您的部署方案,這可能需要其他配置。有關更多信息,請參閱Microsoft 文檔

HTTPS

我們不強制使用HTTPS,但對於生產,它必須與IdentityServer進行每次交互。

授予類型

授權類型是指定客戶端如何與IdentityServer交互的方式。OpenID Connect和OAuth 2規范定義了以下授權類型:

  • 含蓄

  • 授權碼

  • 混合動力

  • 客戶憑證

  • 資源所有者密碼

  • 刷新令牌

  • 延期撥款

您可以通過配置上的AllowedGrantTypes屬性指定客戶端可以使用的授權類型Client

可以將客戶端配置為使用多個授權類型(例如,用於以用戶為中心的操作的混合和用於服務器到服務器通信的客戶端憑證)。的GrantTypes類可以用來從典型交付式的組合,以挑選:

Client.AllowedGrantTypes = GrantTypes.HybridAndClientCredentials;

您也可以手動指定授權類型列表:

Client.AllowedGrantTypes =
{
  GrantType.Hybrid,
  GrantType.ClientCredentials,
  "my_custom_grant_type"
};

如果要通過瀏覽器通道傳輸訪問令牌,還需要在客戶端配置上明確允許:

Client.AllowAccessTokensViaBrowser = true;

注意

出於安全原因,並非所有授權類型組合都是允許的。請參閱下面的更多細節。

對於其余部分,簡要描述了授權類型,以及何時使用它們。還建議您另外閱讀相應的規格以更好地理解差異。

客戶端憑證

這是最簡單的授權類型,用於服務器到服務器通信 - 始終代表客戶端而不是用戶請求令牌。

使用此授權類型,您可以向令牌端點發送令牌請求,並獲取代表客戶端的訪問令牌。客戶端通常必須使用其客戶端ID和密鑰對令牌端點進行身份驗證。

有關如何使用它的示例,請參閱“ 客戶端憑據快速入門 ”。

資源所有者密碼

資源所有者密碼授予類型允許通過將用戶的名稱和密碼發送到令牌端點來代表用戶請求令牌。這就是所謂的“非交互式”身份驗證,通常不推薦使用。

某些遺留或第一方集成方案可能有原因,其中此授權類型很有用,但一般建議使用隱式或混合的交互式流來代替用戶身份驗證。

有關如何使用它的示例,請參閱資源所有者密碼快速入門。您還需要提供用戶名/密碼驗證的代碼,可以通過實現IResourceOwnerPasswordValidator接口來提供。您可以在此處找到有關此界面的更多信息。

隱含

隱式授權類型針對基於瀏覽器的應用程序進行了優化。僅用於用戶身份驗證(服務器端和JavaScript應用程序),或身份驗證和訪問令牌請求(JavaScript應用程序)。

在隱式流程中,所有令牌都通過瀏覽器傳輸,因此不允許使用刷新令牌等高級功能。

快速入門顯示了服務端的web應用程序的認證,而 這個顯示的JavaScript。

授權碼

授權代碼流最初由OAuth 2指定,並提供了一種在反向通道上檢索令牌而不是瀏覽器前端通道的方法。它還支持客戶端身份驗證。

雖然這種授權類型本身是受支持的,但通常建議您將其與身份令牌結合使用,將其轉換為所謂的混合流。混合流程為您提供重要的額外功能,如簽名協議響應。

混合

混合流是隱式和授權代碼流的組合 - 它使用多種授權類型的組合,最典型的是。code id_token

在混合流中,身份令牌通過瀏覽器通道傳輸,並包含簽名協議響應以及其他工件(如授權代碼)的簽名。這減輕了許多適用於瀏覽器通道的攻擊。成功驗證響應后,反向通道用於檢索訪問和刷新令牌。

這是希望檢索訪問令牌(也可能是刷新令牌)的本機應用程序的推薦流程,用於服務器端Web應用程序和本機桌面/移動應用程序。

有關將混合流與MVC一起使用的更多信息,請參閱快速入門。

刷新令牌

刷新令牌允許獲得對API的長期訪問。

您通常希望盡可能縮短訪問令牌的生命周期,但同時不希望通過對IdentityServer進行前端通道往返來請求新的令牌一次又一次地打擾用戶。

刷新令牌允許在沒有用戶交互的情況下請求新的訪問令牌。每次客戶端刷新令牌時,都需要對IdentityServer進行(經過身份驗證的)反向通道調用。這允許檢查刷新令牌是否仍然有效,或者在此期間是否已被撤銷。

混合,授權代碼和資源所有者密碼流支持刷新令牌。要請求刷新令牌,客戶端需要offline_access在令牌請求中包含范圍(並且必須被授權請求該范圍)。

擴展授予

擴展授權允許使用新的授權類型擴展令牌端點。有關詳細信息,請參閱

不兼容的授權類型

禁止使用某些授權類型組合:

  • 混合隱式和授權代碼或混合將允許從更安全的基於代碼的流的降級攻擊到隱式。

  • 同樣存在允許授權代碼和混合代碼的問題

秘密

在某些情況下,客戶端需要使用身份服務器進行身份驗證,例如

  • 在令牌端點請求令牌的機密應用程序(也稱為客戶端)

  • API在內省端點驗證引用令牌

為此,您可以將秘密列表分配給客戶端或API資源。

秘密解析和驗證是身份服務器中的可擴展點,開箱即用它支持共享機密以及通過基本身份驗證頭或POST主體傳輸共享機密。

創建共享秘密

以下代碼設置散列共享密鑰:

var secret = new Secret("secret".Sha256());

現在可以將此秘密分配給a Client或an ApiResource。請注意,它們不僅支持單個秘密,還支持多個秘密。這對於秘密翻轉和輪換非常有用:

var client = new Client
{
  ClientId = "client",
  ClientSecrets = new List<Secret> { secret },

  AllowedGrantTypes = GrantTypes.ClientCredentials,
  AllowedScopes = new List<string>
  {
      "api1", "api2"
  }
};

實際上,您還可以為秘密分配說明和到期日期。該描述將用於記錄,以及強制執行秘密生存期的到期日期:

var secret = new Secret(
  "secret".Sha256(),
  "2016 secret",
  new DateTime(2016, 12, 31));

使用共享密鑰進行身份驗證

您可以將客戶端ID /機密組合作為POST正文的一部分發送:

POST /connect/token

client_id=client1&
client_secret=secret&
...

..或作為基本身份驗證標頭:

POST /connect/token

Authorization: Basic xxxxx

...

您可以使用以下C#代碼手動創建基本身份驗證標頭:

var credentials = string.Format("{0}:{1}", clientId, clientSecret);
var headerValue = Convert.ToBase64String(Encoding.UTF8.GetBytes(credentials));

var client = new HttpClient();
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic", headerValue);

IdentityModel庫有一個叫做輔助類TokenClientIntrospectionClient封裝認證和協議消息。

超越共享秘密

還有其他技術來驗證客戶端,例如基於公鑰/私鑰加密。IdentityServer包括對私鑰JWT客戶機密鑰的支持(請參閱RFC 7523)。

秘密可擴展性通常包含三件事:

  • 一個秘密的定義

  • 一個知道如何從傳入請求中提取秘密的秘密解析器

  • 一個秘密驗證器,知道如何根據定義驗證解析的秘密

秘密解析器和驗證器是ISecretParserISecretValidator接口的實現。要使它們可用於IdentityServer,您需要將它們注冊到DI容器,例如:

builder.AddSecretParser<ClientAssertionSecretParser>()
builder.AddSecretValidator<PrivateKeyJwtSecretValidator>()

我們的默認私鑰JWT秘密驗證器期望完整(葉)證書作為秘密定義的base64。然后,此證書將用於驗證自簽名JWT上的簽名,例如:

var client = new Client
{
  ClientId = "client.jwt",
  ClientSecrets =
  {
      new Secret
      {
          Type = IdentityServerConstants.SecretTypes.X509CertificateBase64,
          Value = "MIIDATCCAe2gAwIBAgIQoHUYAquk9rBJcq8W+F0FAzAJBgUrDgMCHQUAMBIxEDAOBgNVBAMTB0RldlJvb3QwHhcNMTAwMTIwMjMwMDAwWhcNMjAwMTIwMjMwMDAwWjARMQ8wDQYDVQQDEwZDbGllbnQwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDSaY4x1eXqjHF1iXQcF3pbFrIbmNw19w/IdOQxbavmuPbhY7jX0IORu/GQiHjmhqWt8F4G7KGLhXLC1j7rXdDmxXRyVJBZBTEaSYukuX7zGeUXscdpgODLQVay/0hUGz54aDZPAhtBHaYbog+yH10sCXgV1Mxtzx3dGelA6pPwiAmXwFxjJ1HGsS/hdbt+vgXhdlzud3ZSfyI/TJAnFeKxsmbJUyqMfoBl1zFKG4MOvgHhBjekp+r8gYNGknMYu9JDFr1ue0wylaw9UwG8ZXAkYmYbn2wN/CpJl3gJgX42/9g87uLvtVAmz5L+rZQTlS1ibv54ScR2lcRpGQiQav/LAgMBAAGjXDBaMBMGA1UdJQQMMAoGCCsGAQUFBwMCMEMGA1UdAQQ8MDqAENIWANpX5DZ3bX3WvoDfy0GhFDASMRAwDgYDVQQDEwdEZXZSb290ghAsWTt7E82DjU1E1p427Qj2MAkGBSsOAwIdBQADggEBADLje0qbqGVPaZHINLn+WSM2czZk0b5NG80btp7arjgDYoWBIe2TSOkkApTRhLPfmZTsaiI3Ro/64q+Dk3z3Kt7w+grHqu5nYhsn7xQFAQUf3y2KcJnRdIEk0jrLM4vgIzYdXsoC6YO+9QnlkNqcN36Y8IpSVSTda6gRKvGXiAhu42e2Qey/WNMFOL+YzMXGt/nDHL/qRKsuXBOarIb++43DV3YnxGTx22llhOnPpuZ9/gnNY7KLjODaiEciKhaKqt/b57mTEz4jTF4kIg6BP03MUfDXeVlM1Qf1jB43G2QQ19n5lUiqTpmQkcfLfyci2uBZ8BkOhXr3Vk9HIk/xBXQ="
      }
  },

  AllowedGrantTypes = GrantTypes.ClientCredentials,
  AllowedScopes = { "api1", "api2" }
};

您可以實現自己的秘密驗證器(或擴展我們的秘密驗證器)來實現例如鏈信任驗證。

擴展撥款

OAuth 2.0定義了令牌端點的標准授權類型,例如passwordauthorization_coderefresh_token。擴展授權是一種添加對非標准令牌頒發方案(如令牌轉換,委派或自定義憑據)的支持的方法。

您可以通過實現IExtensionGrantValidator接口添加對其他授權類型的支持:

public interface IExtensionGrantValidator
{
  /// <summary>
  /// Handles the custom grant request.
  /// </summary>
  /// <param name="request">The validation context.</param>
  Task ValidateAsync(ExtensionGrantValidationContext context);

  /// <summary>
  /// Returns the grant type this validator can deal with
  /// </summary>
  /// <value>
  /// The type of the grant.
  /// </value>
  string GrantType { get; }
}

ExtensionGrantValidationContext對象使您可以訪問:

  • 傳入令牌請求 - 眾所周知的驗證值,以及任何自定義值(通過Raw集合)

  • 結果 - 錯誤或成功

  • 自定義響應參數

要注冊擴展授權,請將其添加到DI:

builder.AddExtensionGrantValidator<MyExtensionsGrantValidator>();

示例:使用擴展授權的簡單委派

想象一下以下場景 - 前端客戶端使用通過交互流(例如混合流)獲取的令牌調用中間層API。此中間層API(API 1)現在希望代表交互式用戶調用后端API(API 2):

 

換句話說,中間層API(API 1)需要包含用戶身份的訪問令牌,但需要具有后端API(API 2)的范圍。

注意

您可能聽說過窮人代表團這一術語,前端的訪問令牌只是轉發到后端。這有一些缺點,例如API 2現在必須接受API 1范圍,這將允許用戶直接調用API 2。此外 - 您可能希望在令牌中添加一些特定於委托的聲明,例如,呼叫路徑是通過API 1的事實。

實施擴展授權

前端會將令牌發送到API 1,現在需要在IdentityServer上使用API 2的新令牌交換此令牌。

在線上,對交換的令牌服務的調用可能如下所示:

POST /connect/token

grant_type=delegation&
scope=api2&
token=...&
client_id=api1.client
client_secret=secret

擴展授權驗證程序的工作是通過驗證傳入令牌並返回表示新令牌的結果來處理該請求:

public class DelegationGrantValidator : IExtensionGrantValidator
{
  private readonly ITokenValidator _validator;

  public DelegationGrantValidator(ITokenValidator validator)
  {
      _validator = validator;
  }

  public string GrantType => "delegation";

  public async Task ValidateAsync(ExtensionGrantValidationContext context)
  {
      var userToken = context.Request.Raw.Get("token");

      if (string.IsNullOrEmpty(userToken))
      {
          context.Result = new GrantValidationResult(TokenRequestErrors.InvalidGrant);
          return;
      }

      var result = await _validator.ValidateAccessTokenAsync(userToken);
      if (result.IsError)
      {
          context.Result = new GrantValidationResult(TokenRequestErrors.InvalidGrant);
          return;
      }

      // get user's identity
      var sub = result.Claims.FirstOrDefault(c => c.Type == "sub").Value;

      context.Result = new GrantValidationResult(sub, GrantType);
      return;
  }
}

不要忘記在DI上注冊驗證器。

注冊委托客戶端

您需要在IdentityServer中進行客戶端注冊,以允許客戶端使用此新的擴展授權,例如:

var client = new client
{
  ClientId = "api1.client",
  ClientSecrets = new List<Secret>
  {
      new Secret("secret".Sha256())
  },

  AllowedGrantTypes = { "delegation" },

  AllowedScopes = new List<string>
  {
      "api2"
  }
}

調用令牌端點

在API 1中,您現在可以自己構建HTTP有效內容,或使用IdentityModel幫助程序庫:

public async Task<TokenResponse> DelegateAsync(string userToken)
{
  var payload = new
  {
      token = userToken
  };

  // create token client
  var client = new TokenClient(disco.TokenEndpoint, "api1.client", "secret");

  // send custom grant to token endpoint, return response
  return await client.RequestCustomGrantAsync("delegation", "api2", payload);
}

現在TokenResponse.AccessToken將包含委托訪問令牌。

資源所有者密碼驗證

如果要使用OAuth 2.0資源所有者密碼憑據授權(aka password),則需要實現並注冊IResourceOwnerPasswordValidator接口:

public interface IResourceOwnerPasswordValidator
{
  /// <summary>
  /// Validates the resource owner password credential
  /// </summary>
  /// <param name="context">The context.</param>
  Task ValidateAsync(ResourceOwnerPasswordValidationContext context);
}

在上下文中,您將找到已解析的協議參數,如UserNamePassword,以及原始請求,如果您想查看其他輸入數據。

然后,您的工作是實施密碼驗證並相應地設置Result上下文。請參閱GrantValidationResult文檔

刷新令牌

由於訪問令牌的生命周期有限,因此刷新令牌允許在沒有用戶交互的情況下請求新的訪問令牌。

以下流程支持刷新令牌:授權代碼,混合和資源所有者密碼憑據流。需要明確授權客戶端通過設置AllowOfflineAccess來請求刷新令牌true

其他客戶端設置

  • AbsoluteRefreshTokenLifetime

    刷新令牌的最長生命周期,以秒為單位。默認為2592000秒/ 30天。零允許刷新令牌,當僅在SlidingRefreshTokenLifetime傳遞后使用時過期。RefreshTokenExpiration = Sliding

  • SlidingRefreshTokenLifetime

    刷新令牌的生命周期以秒為單位。默認為1296000秒/ 15天

  • RefreshTokenUsage

    ReUse 刷新令牌時刷新令牌句柄將保持不變OneTime 刷新令牌時將更新刷新令牌句柄

  • RefreshTokenExpiration

    Absolute 刷新令牌將在固定時間點到期(由AbsoluteRefreshTokenLifetime指定)Sliding刷新令牌時,將刷新刷新令牌的生命周期(按SlidingRefreshTokenLifetime中指定的數量)。生命周期不會超過AbsoluteRefreshTokenLifetime。

  • UpdateAccessTokenClaimsOnRefresh

    獲取或設置一個值,該值指示是否應在刷新令牌請求上更新訪問令牌(及其聲明)。

參考令牌

訪問令牌可以有兩種形式 - 自包含或參考。

JWT令牌將是一個自包含的訪問令牌 - 它是一個帶有聲明和過期的受保護數據結構。一旦API了解了密鑰材料,它就可以驗證自包含的令牌,而無需與發行者進行通信。這使得JWT難以撤銷。它們將一直有效,直到它們過期。

使用引用令牌時 - IdentityServer會將令牌的內容存儲在數據存儲中,並且只會將此令牌的唯一標識符發回給客戶端。接收此引用的API必須打開與IdentityServer的反向通道通信以驗證令牌。

 

您可以使用以下設置切換客戶端的令牌類型:

client.AccessTokenType = AccessTokenType.Reference;

IdentityServer提供了OAuth 2.0內省規范的實現,該規范允許API取消引用令牌。您可以使用我們的專用內省中間件 或使用身份服務器身份驗證中間件,它可以驗證JWT和引用令牌。

內省端點需要身份驗證 - 因為內省端點的客戶端是API,您可以在以下位置配置秘密ApiResource

var api = new ApiResource("api1")
{
  ApiSecrets = { new Secret("secret".Sha256()) }
}

有關如何為API配置IdentityServer身份驗證中間件的詳細信息,請參閱此處

CORS

IdentityServer中的許多端點將通過基於JavaScript的客戶端的Ajax調用進行訪問。鑒於IdentityServer最有可能托管在與這些客戶端不同的源上,這意味着需要配置跨源資源共享(CORS)。

基於客戶端的CORS配置

配置CORS的一種方法是AllowedCorsOrigins客戶端配置上使用該集合。只需將客戶端的原點添加到集合中,IdentityServer中的默認配置將查詢這些值以允許來自源的跨源調用。

注意

配置CORS時,請務必使用原點(不是URL)。例如:https://foo:123/是一個URL,而是https://foo:123一個原點。

如果您使用我們提供的“內存中”或基於EF的客戶端配置,則將使用此默認CORS實現。如果您定義自己的IClientStore,那么您將需要實現自己的自定義CORS策略服務(見下文)。

自定義Cors策略服務

IdentityServer允許托管應用程序實現ICorsPolicyService完全控制CORS策略。

要實現的單一方法是:。如果允許原點則返回,否則返回。Task<bool> IsOriginAllowedAsync(string origin)``true``false

實現后,只需在DI中注冊實現,然后IdentityServer將使用您的自定義實現。

DefaultCorsPolicyService

如果您只是希望硬編碼一組允許的原點,那么ICorsPolicyService可以使用一個預先構建的實現調用DefaultCorsPolicyService。這將被配置為DI單身,並以其硬編碼的AllowedOrigins收集,或設置標志AllowAll,以true允許所有的起源。例如,在ConfigureServices

var cors = new DefaultCorsPolicyService(_loggerFactory.CreateLogger<DefaultCorsPolicyService>())
{
  AllowedOrigins = { "https://foo", "https://bar" }
};
services.AddSingleton<ICorsPolicyService>(cors);

注意

AllowAll謹慎使用。

將IdentityServer的CORS策略與ASP.NET Core的CORS策略混合

IdentityServer使用ASP.NET Core的CORS中間件來提供其CORS實現。托管IdentityServer的應用程序可能還需要CORS用於自己的自定義端點。通常,兩者應該在同一個應用程序中一起工作。

您的代碼應使用ASP.NET Core中記錄的CORS功能,而不考慮IdentityServer。這意味着您應該定義策略並正常注冊中間件。如果您的應用程序定義了策略ConfigureServices,那么這些策略應該繼續在您使用它們的相同位置(在您配置CORS中間件的位置或在EnableCors控制器代碼中使用MVC 屬性的位置)。相反,如果您使用CORS中間件(通過策略構建器回調)定義內聯策略,那么它也應該繼續正常工作。

在您使用ASP.NET Core CORS服務和IdentityServer之間可能存在沖突的一種情況是,如果您決定創建自定義ICorsPolicyProvider。鑒於ASP.NET Core的CORS服務和中間件的設計,IdentityServer實現了自己的自定義ICorsPolicyProvider並將其注冊到DI系統中。幸運的是,IdentityServer實現旨在使用裝飾器模式來包裝ICorsPolicyProvider已在DI中注冊的任何現有模式 。這意味着你也可以實現ICorsPolicyProvider,但它只需要在DI中的IdentityServer之前注冊(例如,在ConfigureServices)。

發現

可以在https://baseaddress/.well-known/openid-configuration找到發現文檔。它包含有關IdentityServer的端點,密鑰材料和功能的信息。

默認情況下,所有信息都包含在發現文檔中,但通過使用配置選項,您可以隱藏各個部分,例如:

services.AddIdentityServer(options =>
{
  options.Discovery.ShowIdentityScopes = false;
  options.Discovery.ShowApiScopes = false;
  options.Discovery.ShowClaims = false;
  options.Discovery.ShowExtensionGrantTypes = false;
});

擴展發現

您可以向發現文檔添加自定義條目,例如:

services.AddIdentityServer(options =>
{
  options.Discovery.CustomEntries.Add("my_setting", "foo");
  options.Discovery.CustomEntries.Add("my_complex_setting",
      new
      {
          foo = "foo",
          bar = "bar"
      });
});

當您添加以〜開頭的自定義值時,它將擴展到IdentityServer基址以下的絕對路徑,例如:

options.Discovery.CustomEntries.Add("my_custom_endpoint", "~/custom");

如果要完全控制發現(和jwks)文檔的呈現,可以實現IDiscoveryResponseGenerator 接口(或從我們的默認實現派生)。

添加更多API端點

您可以向托管IdentityServer4的應用程序添加更多API端點。

您通常希望通過它們所托管的IdentityServer實例來保護這些API。這不是問題。只需將令牌驗證處理程序添加到主機(請參閱此處):

public void ConfigureServices(IServiceCollection services)
{
  services.AddMvc();

  // details omitted
  services.AddIdentityServer();

  services.AddAuthentication()
      .AddIdentityServerAuthentication("token", isAuth =>
      {
          isAuth.Authority = "base_address_of_identityserver";
          isAuth.ApiName = "name_of_api";
      });
}

在您的API上,您需要添加[Authorize]屬性並顯式引用您要使用的身份驗證方案(token在此示例中,您可以選擇您喜歡的任何名稱):

public class TestController : ControllerBase
{
  [Route("test")]
  [Authorize(AuthenticationSchemes = "token")]
  public IActionResult Get()
  {
      var claims = User.Claims.Select(c => new { c.Type, c.Value }).ToArray();
      return Ok(new { message = "Hello API", claims });
  }
}

如果要從瀏覽器調用該API,則還需要配置CORS(請參閱此處)。

發現

如果需要,您還可以將端點添加到發現文檔中,例如:

services.AddIdentityServer(options =>
{
  options.Discovery.CustomEntries.Add("custom_endpoint", "~/api/custom");
})

添加新協議

除了對OpenID Connect和OAuth 2.0的內置支持之外,IdentityServer4還允許添加對其他協議的支持。

您可以將這些附加協議端點添加為中間件或使用例如MVC控制器。在這兩種情況下,您都可以訪問ASP.NET Core DI系統,該系統允許重用我們的內部服務,例如訪問客戶端定義或密鑰材料。

可以在此處找到添加WS-Federation支持的示例。

典型認證工作流程

身份驗證請求通常如下所示:

  • 身份驗證請求到達協議端點

  • 協議端點執行輸入驗證

    • 重定向到登錄頁面,返回URL設置回協議端點(如果用戶是匿名的)

      通過訪問當前請求詳細信息 IIdentityServerInteractionService用戶身份驗證(本地或通過外部身份驗證中間件)登錄用戶重定向回協議端點

  • 創建協議響應(令牌創建和重定向回客戶端)

有用的IdentityServer服務

要實現上述工作流程,需要與IdentityServer建立一些交互點。

訪問配置並重定向到登錄頁面

您可以通過將IdentityServerOptions 類注入代碼來訪問IdentityServer配置。這個,例如具有登錄頁面的配置路徑:

var returnUrl = Url.Action("Index");
returnUrl = returnUrl.AddQueryString(Request.QueryString.Value);

var loginUrl = _options.UserInteraction.LoginUrl;
var url = loginUrl.AddQueryString(_options.UserInteraction.LoginReturnUrlParameter, returnUrl);

return Redirect(url);

登錄頁面與當前協議請求之間的交互

所述IIdentityServerInteractionService支撐件轉動一個協議返回URL成解析和驗證上下文對象:

var context = await _interaction.GetAuthorizationContextAsync(returnUrl);

默認情況下,交互服務僅了解OpenID Connect協議消息。要擴展支持,您可以自己編寫IReturnUrlParser

public interface IReturnUrlParser
{
  bool IsValidReturnUrl(string returnUrl);
  Task<AuthorizationRequest> ParseAsync(string returnUrl);
}

..然后在DI中注冊解析器:

builder.Services.AddTransient<IReturnUrlParser, WsFederationReturnUrlParser>();

這允許登錄頁面獲取客戶端配置和其他協議參數等信息。

訪問用於創建協議響應的配置和密鑰材料

通過將IKeyMaterialService代碼注入到代碼中,您可以訪問配置的簽名憑據和驗證密鑰:

工具
該IdentityServerTools班是為IdentityServer編寫擴展代碼時,你可能需要有效的內部工具的集合。要使用它,請將其注入代碼,例如控制器:

public MyController(IdentityServerTools tools)
{
  _tools = tools;
}
該IssueJwtAsync方法允許使用IdentityServer令牌創建引擎創建JWT令牌。這IssueClientJwtAsync是用於為服務器到服務器通信創建令牌的簡單版本(例如,當您必須從代碼中調用受IdentityServer保護的API時):

public async Task<IActionResult> MyAction()
{
  var token = await _tools.IssueClientJwtAsync(
      clientId: "client_id",
      lifetime: 3600,
      audiences: new[] { "backend.api" });

  // more code
}var credential = await _keys.GetSigningCredentialsAsync();
var key = credential.Key as Microsoft.IdentityModel.Tokens.X509SecurityKey;

var descriptor = new SecurityTokenDescriptor
{
  AppliesToAddress = result.Client.ClientId,
  Lifetime = new Lifetime(DateTime.UtcNow, DateTime.UtcNow.AddSeconds(result.Client.IdentityTokenLifetime)),
  ReplyToAddress = result.Client.RedirectUris.First(),
  SigningCredentials = new X509SigningCredentials(key.Certificate, result.RelyingParty.SignatureAlgorithm, result.RelyingParty.DigestAlgorithm),
  Subject = outgoingSubject,
  TokenIssuerName = _contextAccessor.HttpContext.GetIdentityServerIssuerUri(),
  TokenType = result.RelyingParty.TokenType
};

發現端點

發現端點可用於檢索有關IdentityServer的元數據 - 它返回諸如頒發者名稱,密鑰材料,支持的范圍等信息。有關詳細信息,請參閱規范

發現端點可通過/.well-known/openid-configuration相對於基地址獲得,例如:

https://demo.identityserver.io/.well-known/openid-configuration

注意

您可以使用IdentityModel客戶端庫以編程方式從.NET代碼訪問發現端點。有關更多信息,請查看IdentityModel 文檔

授權端點

授權端點可用於通過瀏覽器請求令牌或授權碼。此過程通常涉及最終用戶的身份驗證和可選的同意。

注意

IdentityServer支持OpenID Connect和OAuth 2.0授權請求參數的子集。有關完整列表,請參見此處

  • client_id

    客戶的標識符(必填)。

  • scope

    一個或多個注冊范圍(必填)

  • redirect_uri

    必須與該客戶端允許的重定向URI之一完全匹配(必需)

  • response_type

    id_token 請求身份令牌(僅允許身份范圍)token 請求訪問令牌(僅允許資源范圍)id_token token 請求身份令牌和訪問令牌code 請求授權碼code id_token 請求授權代碼和身份令牌code id_token token 請求授權代碼,身份令牌和訪問令牌

  • response_mode

    form_post 將令牌響應作為表單發送而不是片段編碼重定向(可選)

  • state

    identityserver將回顯令牌響應的狀態值,這是客戶端和提供者之間的往返狀態,關聯請求和響應以及CSRF /重放保護。(推薦的)

  • nonce

    identityserver將回顯身份令牌中的nonce值,這是為了重放保護)通過隱式授權對身份令牌是必需的。

  • prompt

    none請求期間不會顯示任何UI。如果這是不可能的(例如,因為用戶必須登錄或同意),則返回錯誤login 即使用戶已登錄並具有有效會話,也會顯示登錄UI

  • code_challenge

    發送PKCE的代碼質詢

  • code_challenge_method

    plain表示挑戰是使用純文本(不推薦) S256表示使用SHA256對挑戰進行哈希處理

  • login_hint

    可用於預先填寫登錄頁面上的用戶名字段

  • ui_locales

    提供有關登錄UI所需顯示語言的提示

  • max_age

    如果用戶的登錄會話超過最大年齡(以秒為單位),將顯示登錄UI

  • acr_values

    允許傳遞額外的身份驗證相關信息 - 身份服務器特殊情況下面的私有acr_values:idp:name_of_idp 繞過login / home領域屏幕並將用戶直接轉發到選定的身份提供者(如果允許每個客戶端配置)tenant:name_of_tenant 可用於將租戶名稱傳遞給登錄UI

GET /connect/authorize?
  client_id=client1&
  scope=openid email api1&
  response_type=id_token token&
  redirect_uri=https://myapp/callback&
  state=abc&
  nonce=xyz

(刪除了URL編碼,並添加了換行符以提高可讀性)

注意

您可以使用IdentityModel客戶端庫以編程方式創建授權請求.NET代碼。有關更多信息,請查看IdentityModel 文檔

令牌端點

令牌端點可用於以編程方式請求令牌。它支持passwordauthorization_codeclient_credentialsrefresh_token補助的類型)。此外,可以擴展令牌端點以支持擴展授權類型。

注意

IdentityServer支持OpenID Connect和OAuth 2.0令牌請求參數的子集。有關完整列表,請參見此處

  • client_id

    客戶標識符(必填)

  • client_secret

    客戶端密鑰可以在帖子正文中,也可以作為基本身份驗證標頭。可選的。

  • grant_type

    authorization_codeclient_credentialspasswordrefresh_token或自定義

  • scope

    一個或多個注冊范圍。如果未指定,將發出所有明確允許范圍的標記。

  • redirect_uri

    authorization_code授權類型所需

  • code

    授權碼(authorization_code授權類型所需)

  • code_verifier

    PKCE證明密鑰

  • username

    資源所有者用戶名(password授予類型所需)

  • password

    資源所有者密碼(password授予類型所需)

  • acr_values

    允許為password授權類型傳遞額外的身份驗證相關信息- identityserver特殊情況下面的專有acr_values:idp:name_of_idp 繞過login / home領域屏幕並將用戶直接轉發到選定的身份提供者(如果允許每個客戶端配置)tenant:name_of_tenant 可用於將租戶名稱傳遞給令牌端點

  • refresh_token

    刷新令牌(refresh_token授予類型所需)

示例

POST /connect/token

  client_id=client1&
  client_secret=secret&
  grant_type=authorization_code&
  code=hdh922&
  redirect_uri=https://myapp.com/callback

(為了便於閱讀,刪除了表格編碼並添加了換行符)

注意

您可以使用IdentityModel客戶端庫以編程方式從.NET代碼訪問令牌端點。有關更多信息,請查看IdentityModel 文檔

UserInfo端點

UserInfo端點可用於檢索有關用戶的身份信息(請參閱規范)。

調用者需要發送代表用戶的有效訪問令牌。根據授予的范圍,UserInfo端點將返回映射的聲明(至少需要openid作用域)。

示例

GET /connect/userinfo
Authorization: Bearer <access_token>
HTTP/1.1 200 OK
Content-Type: application/json

{
  "sub": "248289761001",
  "name": "Bob Smith",
  "given_name": "Bob",
  "family_name": "Smith",
  "role": [
      "user",
      "admin"
  ]
}

注意

您可以使用IdentityModel客戶端庫以編程方式從.NET代碼訪問userinfo端點。有關更多信息,請查看IdentityModel 文檔

自省端點

內省端點是RFC 7662的實現。

它可用於驗證引用令牌(如果消費者不支持適當的JWT或加密庫,則可以使用JWT)。內省端點需要身份驗證 - 因為內省端點的客戶端是API,您可以在上配置密碼ApiResource

示例

POST /connect/introspect
Authorization: Basic xxxyyy

token=<token>

成功的響應將返回狀態代碼200以及活動或非活動令牌:

{
  "active": true,
  "sub": "123"
}

未知或過期的令牌將被標記為無效:

{
  "active": false,
}

無效請求將返回400,未授權請求401。

注意

您可以使用IdentityModel客戶端庫以編程方式從.NET代碼訪問內省端點。有關更多信息,請查看IdentityModel 文檔

撤銷端點

此端點允許撤消訪問令牌(僅限引用令牌)和刷新令牌。它實現了令牌撤銷規范(RFC 7009)

  • token

    要撤銷的令牌(必填)

  • token_type_hint

    或者access_tokenrefresh_token(可選)

示例

POST /connect/revocation HTTP/1.1
Host: server.example.com
Content-Type: application/x-www-form-urlencoded
Authorization: Basic czZCaGRSa3F0MzpnWDFmQmF0M2JW

token=45ghiukldjahdnhzdauz&token_type_hint=refresh_token

注意

您可以使用IdentityModel客戶端庫以編程方式從.NET代碼訪問吊銷端點。有關更多信息,請查看IdentityModel 文檔

結束會話端點

結束會話端點可用於觸發單點注銷(請參閱規范)。

要使用結束會話端點,客戶端應用程序會將用戶的瀏覽器重定向到結束會話URL。用戶在會話期間通過瀏覽器登錄的所有應用程序都可以參與注銷。

注意

終端會話端點的URL可通過發現端點獲得

參數

id_token_hint

當用戶被重定向到端點時,系統會提示他們是否真的想要注銷。發送從身份驗證收到的原始id_token的客戶端可以繞過此提示。這是作為查詢的字符串參數傳遞的id_token_hint

post_logout_redirect_uri

如果id_token_hint傳遞了有效,則客戶端也可以發送post_logout_redirect_uri參數。這可用於允許用戶在注銷后重定向回客戶端。該值必須與客戶端預先配置的PostLogoutRedirectUris(客戶端文檔)之一匹配。

如果post_logout_redirect_uri傳遞了有效,則客戶端也可以發送state參數。在用戶重定向回客戶端后,這將作為查詢字符串參數返回給客戶端。這通常由客戶端用於跨重定向的往返狀態。

示例

GET /connect/endsession?id_token_hint=eyJhbGciOiJSUzI1NiIsImtpZCI6IjdlOGFkZmMzMjU1OTEyNzI0ZDY4NWZmYmIwOThjNDEyIiwidHlwIjoiSldUIn0.eyJuYmYiOjE0OTE3NjUzMjEsImV4cCI6MTQ5MTc2NTYyMSwiaXNzIjoiaHR0cDovL2xvY2FsaG9zdDo1MDAwIiwiYXVkIjoianNfb2lkYyIsIm5vbmNlIjoiYTQwNGFjN2NjYWEwNGFmNzkzNmJjYTkyNTJkYTRhODUiLCJpYXQiOjE0OTE3NjUzMjEsInNpZCI6IjI2YTYzNWVmOTQ2ZjRiZGU3ZWUzMzQ2ZjFmMWY1NTZjIiwic3ViIjoiODg0MjExMTMiLCJhdXRoX3RpbWUiOjE0OTE3NjUzMTksImlkcCI6ImxvY2FsIiwiYW1yIjpbInB3ZCJdfQ.STzOWoeVYMtZdRAeRT95cMYEmClixWkmGwVH2Yyiks9BETotbSZiSfgE5kRh72kghN78N3-RgCTUmM2edB3bZx4H5ut3wWsBnZtQ2JLfhTwJAjaLE9Ykt68ovNJySbm8hjZhHzPWKh55jzshivQvTX0GdtlbcDoEA1oNONxHkpDIcr3pRoGi6YveEAFsGOeSQwzT76aId-rAALhFPkyKnVc-uB8IHtGNSyRWLFhwVqAdS3fRNO7iIs5hYRxeFSU7a5ZuUqZ6RRi-bcDhI-djKO5uAwiyhfpbpYcaY_TxXWoCmq8N8uAw9zqFsQUwcXymfOAi2UF3eFZt02hBu-shKA&post_logout_redirect_uri=http%3A%2F%2Flocalhost%3A7017%2Findex.html

注意

您可以使用IdentityModel客戶端庫以編程方式創建end_session請求.NET代碼。有關更多信息,請查看IdentityModel 文檔

身份資源

此類為身份資源建模。

  • Enabled

    指示此資源是否已啟用且可以請求。默認為true。

  • Name

    身份資源的唯一名稱。這是客戶端將用於授權請求中的scope參數的值。

  • DisplayName

    該值將用於例如同意屏幕上。

  • Description

    該值將用於例如同意屏幕上。

  • Required

    指定用戶是否可以在同意屏幕上取消選擇范圍(如果同意屏幕要實現此類功能)。默認為false。

  • Emphasize

    指定同意屏幕是否會強調此范圍(如果同意屏幕要實現此類功能)。將此設置用於敏感或重要范圍。默認為false。

  • ShowInDiscoveryDocument

    指定此范圍是否顯示在發現文檔中。默認為true。

  • UserClaims

    應包含在身份令牌中的關聯用戶聲明類型的列表。

    API資源

    此類為API資源建模。

    • Enabled

      指示此資源是否已啟用且可以請求。默認為true。

    • Name

      API的唯一名稱。此值用於內省身份驗證,並將添加到傳出訪問令牌的受眾。

    • DisplayName

      該值可以在例如同意屏幕上使用。

    • Description

      該值可以在例如同意屏幕上使用。

    • ApiSecrets

      API密鑰用於內省端點。API可以使用API名稱和密鑰進行內省驗證。

    • UserClaims

      應包含在訪問令牌中的關聯用戶聲明類型的列表。

    • Scopes

      API必須至少有一個范圍。每個范圍可以有不同的設置。

    范圍

    在簡單的情況下,API只有一個范圍。但是在某些情況下,您可能希望細分API的功能,並讓不同的客戶端訪問不同的部分。

    • Name

      范圍的唯一名稱。這是客戶端將用於授權/令牌請求中的scope參數的值。

    • DisplayName

      該值可以在例如同意屏幕上使用。

    • Description

      該值可以在例如同意屏幕上使用。

    • Required

      指定用戶是否可以在同意屏幕上取消選擇范圍(如果同意屏幕要實現此類功能)。默認為false。

    • Emphasize

      指定同意屏幕是否會強調此范圍(如果同意屏幕要實現此類功能)。將此設置用於敏感或重要范圍。默認為false。

    • ShowInDiscoveryDocument

      指定此范圍是否顯示在發現文檔中。默認為true。

    • UserClaims

      應包含在訪問令牌中的關聯用戶聲明類型的列表。此處指定的聲明將添加到為API指定的聲明列表中。

    便利構造函數行為

    只是關於為ApiResource類提供的構造函數的注釋。

    要完全控制數據ApiResource,請使用不帶參數的默認構造函數。如果要為每個API配置多個范圍,可以使用此方法。例如:

    new ApiResource
    {
      Name = "api2",

      Scopes =
      {
          new Scope()
          {
              Name = "api2.full_access",
              DisplayName = "Full access to API 2"
          },
          new Scope
          {
              Name = "api2.read_only",
              DisplayName = "Read only access to API 2"
          }
      }
    }

    對於每個API只需要一個范圍的簡單方案,則提供了幾個接受a的便捷構造函數name。例如:

    new ApiResource("api1", "Some API 1")

    使用便捷構造函數等同於:

    new ApiResource
    {
      Name = "api1",
      DisplayName = "Some API 1",

      Scopes =
      {
          new Scope()
          {
              Name = "api1",
              DisplayName = "Some API 1"
          }
      }
    }

客戶

Client級車型的ID連接或OAuth 2.0用戶端-例如,本地應用,Web應用程序或基於JS的應用程序。

基礎知識

  • Enabled

    指定是否啟用客戶端。默認為true。

  • ClientId

    客戶端的唯一ID

  • ClientSecrets

    客戶端機密列表 - 訪問令牌端點的憑據。

  • RequireClientSecret

    指定此客戶端是否需要密鑰才能從令牌端點請求令牌(默認為true

  • AllowedGrantTypes

    指定允許客戶端使用的授權類型。將該GrantTypes類用於常見組合。

  • RequirePkce

    指定使用基於授權代碼的授權類型的客戶端是否必須發送校驗密鑰

  • AllowPlainTextPkce

    指定使用PKCE的客戶端是否可以使用純文本代碼質詢(不推薦 - 默認為false

  • RedirectUris

    指定允許的URI以返回令牌或授權碼

  • AllowedScopes

    默認情況下,客戶端無權訪問任何資源 - 通過添加相應的范圍名稱來指定允許的資源

  • AllowOfflineAccess

    指定此客戶端是否可以請求刷新令牌(請求offline_access范圍)

  • AllowAccessTokensViaBrowser

    指定是否允許此客戶端通過瀏覽器接收訪問令牌。這對於強化允許多種響應類型的流是有用的(例如,通過禁止混合流客戶端,該客戶端應該使用代碼id_token來添加令牌響應類型,從而將令牌泄露給瀏覽器。

  • Properties

    字典可根據需要保存任何自定義客戶端特定值。

認證/注銷

  • PostLogoutRedirectUris

    指定在注銷后重定向到的允許URI。有關更多詳細信息,請參閱OIDC Connect會話管理規范

  • FrontChannelLogoutUri

    指定客戶端的注銷URI,以用於基於HTTP的前端通道注銷。有關詳細信息,請參閱OIDC Front-Channel規范

  • FrontChannelLogoutSessionRequired

    指定是否應將用戶的會話ID發送到FrontChannelLogoutUri。默認為true。

  • BackChannelLogoutUri

    指定客戶端的注銷URI,以用於基於HTTP的反向通道注銷。有關詳細信息,請參閱OIDC Back-Channel規范

  • BackChannelLogoutSessionRequired

    指定是否應在請求中將用戶的會話ID發送到BackChannelLogoutUri。默認為true。

  • EnableLocalLogin

    指定此客戶端是否可以僅使用本地帳戶或外部IdP。默認為true。

  • IdentityProviderRestrictions

    指定可以與此客戶端一起使用的外部IdP(如果列表為空,則允許所有IdP)。默認為空。

令牌

  • IdentityTokenLifetime

    標識令牌的生命周期(以秒為單位)(默認為300秒/ 5分鍾)

  • AccessTokenLifetime

    訪問令牌的生命周期(以秒為單位)(默認為3600秒/ 1小時)

  • AuthorizationCodeLifetime

    授權代碼的生命周期(以秒為單位)(默認為300秒/ 5分鍾)

  • AbsoluteRefreshTokenLifetime

    刷新令牌的最長生命周期,以秒為單位。默認為2592000秒/ 30天

  • SlidingRefreshTokenLifetime

    刷新令牌的生命周期以秒為單位。默認為1296000秒/ 15天

  • RefreshTokenUsage

    ReUse 刷新令牌時刷新令牌句柄將保持不變OneTime刷新令牌時將更新刷新令牌句柄。這是默認值。

  • RefreshTokenExpiration

    Absolute 刷新令牌將在固定時間點到期(由AbsoluteRefreshTokenLifetime指定)Sliding刷新令牌時,將刷新刷新令牌的生命周期(按SlidingRefreshTokenLifetime中指定的數量)。生命周期不會超過AbsoluteRefreshTokenLifetime。

  • UpdateAccessTokenClaimsOnRefresh

    獲取或設置一個值,該值指示是否應在刷新令牌請求上更新訪問令牌(及其聲明)。

  • AccessTokenType

    指定訪問令牌是引用令牌還是自包含JWT令牌(默認為Jwt)。

  • IncludeJwtId

    指定JWT訪問令牌是否應具有嵌入的唯一ID(通過jti聲明)。

  • AllowedCorsOrigins

    如果指定,將由默認CORS策略服務實現(內存和EF)用於為JavaScript客戶端構建CORS策略。

  • Claims

    允許客戶端的設置聲明(將包含在訪問令牌中)。

  • AlwaysSendClientClaims

    如果設置,將為每個流發送客戶端聲明。如果不是,僅用於客戶端憑證流(默認為false)

  • AlwaysIncludeUserClaimsInIdToken

    在請求id令牌和訪問令牌時,如果用戶聲明始終將其添加到id令牌而不是請求客戶端使用userinfo端點。默認值為false。

  • ClientClaimsPrefix

    如果設置,前綴客戶端聲明類型將以前綴為。默認為client_。目的是確保它們不會意外地與用戶聲明沖突。

  • PairWiseSubjectSalt

    對於此客戶端的用戶,在成對的subjectId生成中使用的salt值。

同意屏幕

  • RequireConsent

    指定是否需要同意屏幕。默認為true。

  • AllowRememberConsent

    指定用戶是否可以選擇存儲同意決策。默認為true。

  • ConsentLifetime

    用戶同意的生命周期,以秒為單位。默認為null(無到期)。

  • ClientName

    客戶端顯示名稱(用於記錄和同意屏幕)

  • ClientUri

    有關客戶端的更多信息的URI(在同意屏幕上使用)

  • LogoUri

    URI到客戶端徽標(在同意屏幕上使用)

GrantValidationResult

GrantValidationResult級車型補助確認為擴展贈款和資源所有者密碼授權的結果。

最常見的用法是使用身份(成功案例)新建它:

context.Result = new GrantValidationResult(
  subject: "818727",
  authenticationMethod: "custom",
  claims: optionalClaims);

...或使用錯誤和描述(失敗案例):

context.Result = new GrantValidationResult(
  TokenRequestErrors.InvalidGrant,
  "invalid custom credential");

在這兩種情況下,您都可以傳遞將包含在令牌響應中的其他自定義值。

個人資料服務

IdentityServer通常在創建令牌或處理對userinfo或內省端點的請求時需要有關用戶的身份信息。默認情況下,IdentityServer僅在身份驗證cookie中具有聲明,以便為此身份數據進行繪制。

將用戶所需的所有可能聲明放入cookie中是不切實際的,因此IdentityServer定義了一個擴展點,允許根據用戶需要動態加載聲明。這個可擴展點是IProfileService開發人員通常可以實現此接口來訪問包含用戶身份數據的自定義數據庫或API。

IProfileService的API

  • GetProfileDataAsync

    預期為用戶加載聲明的API。它傳遞了一個實例ProfileDataRequestContext

  • IsActiveAsync

    預期用於指示當前是否允許用戶獲取令牌的API。它傳遞了一個實例IsActiveContext

ProfileDataRequestContext

模擬用戶聲明的請求,並且是返回這些聲明的工具。它包含以下屬性:

  • Subject

    ClaimsPrincipal模型的用戶。

  • Client

    Client用於正被請求的權利要求。

  • RequestedClaimTypes

    請求的索賠類型集合。

  • Caller

    正在請求聲明的上下文的標識符(例如,身份令牌,訪問令牌或用戶信息端點)。常量IdentityServerConstants.ProfileDataCallers包含不同的常量值。

  • IssuedClaims

    Claim將返回的s 列表。預計這將由自定義IProfileService實現填充。

  • AddRequestedClaims

    擴展方法對ProfileDataRequestContext填充IssuedClaims,但首先過濾基於的聲明RequestedClaimTypes

請求的范圍和聲明映射

客戶端請求的范圍控制用戶聲明在令牌中返回給客戶端的內容。該GetProfileDataAsync方法負責根據上的RequestedClaimTypes集合動態獲取這些聲明ProfileDataRequestContext

RequestedClaimTypes集合是基於該定義的用戶索賠人口資源的范圍進行建模。如果請求的范圍是身份資源,則將RequestedClaimTypes根據在中定義的用戶聲明類型填充聲明中的聲明IdentityResource。如果請求的范圍是API資源,則將RequestedClaimTypes根據ApiResource和/或中定義的用戶聲明類型填充聲明中的聲明Scope

IsActiveContext

對要確定的請求建模是當前允許用戶獲取令牌。它包含以下屬性:

  • Subject

    ClaimsPrincipal模型的用戶。

  • Client

    Client用於正被請求的權利要求。

  • Caller

    正在請求聲明的上下文的標識符(例如,身份令牌,訪問令牌或用戶信息端點)。常量IdentityServerConstants.ProfileDataCallers包含不同的常量值。

  • IsActive

    指示是否允許用戶獲取令牌的標志。預計這將由自定義IProfileService實現分配。

IdentityServer交互服務

IIdentityServerInteractionService接口旨在提供用戶界面用於與IdentityServer通信的服務,主要涉及用戶交互。它可以從依賴注入系統獲得,通常作為構造函數參數注入到IdentityServer的用戶界面的MVC控制器中。

IIdentityServerInteractionService的API

  • GetAuthorizationContextAsync

    返回AuthorizationRequest基於returnUrl傳遞給登錄或同意頁面。

  • IsValidReturnUrl

    指示returnUrl登錄或同意后是否為重定向的有效URL。

  • GetErrorContextAsync

    返回ErrorMessage基於errorId傳遞給錯誤頁面。

  • GetLogoutContextAsync

    返回LogoutRequest基於logoutId傳遞給注銷頁面。

  • CreateLogoutContextAsync

    logoutId如果目前沒有一個用於創建。這將創建一個cookie,捕獲注銷所需的所有當前狀態,並logoutId標識該cookie。這通常在沒有當前時使用,logoutId並且注銷頁面必須捕獲當前用戶在重定向到外部身份提供程序以進行注銷之前注銷所需的狀態。新創建的logoutId需要在注銷時往返外部身份提供商,然后在注銷回調頁面上使用,就像在普通注銷頁面上一樣。

  • GrantConsentAsync

    接受a ConsentResponse以通知IdentityServer用戶同意某個特定用戶AuthorizationRequest

  • GetAllUserConsentsAsync

    返回Consent用戶的集合。

  • RevokeUserConsentAsync

    撤消用戶對用戶的所有同意和授權。

  • RevokeTokensForCurrentSessionAsync

    撤消用戶在當前會話期間簽署的客戶的所有同意和授權。

AuthorizationRequest

  • ClientId

    發起請求的客戶端標識符。

  • RedirectUri

    成功授權后將用戶重定向到的URI。

  • DisplayMode

    顯示模式從授權請求傳遞。

  • UiLocales

    從授權請求傳遞的UI語言環境。

  • IdP

    外部身份提供者請求。這用於繞過家庭領域發現(HRD)。這是通過“idp:”前綴提供給acr_values授權請求的參數。

  • Tenant

    租客要求。這是通過“tenant:”前綴提供給acr_values授權請求的參數。

  • LoginHint

    用戶將用於登錄的預期用戶名。這是通過login_hint授權請求上的參數從客戶端請求的。

  • PromptMode

    從授權請求請求的提示模式。

  • AcrValues

    從授權請求傳遞的acr值。

  • ScopesRequested

    授權請求中請求的范圍。

  • Parameters

    整個參數集合傳遞給授權請求。

的ErrorMessage

  • DisplayMode

    顯示模式從授權請求傳遞。

  • UiLocales

    從授權請求傳遞的UI語言環境。

  • Error

    錯誤代碼。

  • RequestId

    每請求標識符。這可用於向最終用戶顯示,並可用於診斷。

LogoutRequest

  • ClientId

    發起請求的客戶端標識符。

  • PostLogoutRedirectUri

    用戶在注銷后將其重定向到的URL。

  • SessionId

    用戶當前的會話ID。

  • SignOutIFrameUrl

    要在<iframe>注銷頁面上呈現以啟用單點注銷的URL 。

  • Parameters

    整個參數集合傳遞給結束會話端點。

  • ShowSignoutPrompt

    指示是否應根據傳遞到結束會話端點的參數提示用戶注銷。

ConsentResponse

  • ScopesConsented

    用戶同意的范圍集合。

  • RememberConsent

    指示是否持久保留用戶同意的標志。

同意

  • SubjectId

    授予同意的主題ID。

  • ClientId

    同意的客戶端標識符。

  • Scopes

    范圍的集合同意。

  • CreationTime

    獲得同意的日期和時間。

  • Expiration

    同意過期的日期和時間。

IdentityServer選項

    • IssuerUri

      設置將在發現文檔和已頒發的JWT令牌中顯示的頒發者名稱。建議不要設置此屬性,該屬性從客戶端使用的主機名中推斷頒發者名稱。

    • PublicOrigin

      此服務器實例的來源,例如https://myorigin.com。如果未設置,則從請求推斷出原始名稱。

端點

允許啟用/禁用各個端點,例如令牌,授權,用戶信息等。

默認情況下,所有端點都已啟用,但您可以通過禁用不需要的端點來鎖定服務器。

發現

允許啟用/禁用發現文檔的各個部分,例如端點,范圍,聲明,授權類型等。

CustomEntries字典允許向發現文檔添加自定義元素。

認證

    • CookieLifetime

      身份驗證cookie生存期(僅在使用IdentityServer提供的cookie處理程序時有效)。

    • CookieSlidingExpiration

      指定cookie是否應該滑動(僅在使用IdentityServer提供的cookie處理程序時有效)。

    • RequireAuthenticatedUserForSignOutMessage

      指示是否必須對用戶進行身份驗證以接受結束會話端點的參數。默認為false。

    • CheckSessionCookieName

      用於檢查會話端點的cookie的名稱。

    • RequireCspFrameSrcForSignout

      如果設置,將要求frame-src CSP標頭在結束會話回調端點上發出,該端點向客戶端呈現iframe以進行前端通道注銷通知。默認為true。

事件

允許配置是否應將哪些事件提交到已注冊的事件接收器。有關活動的更多信息,請參見此處

InputLengthRestrictions

允許設置各種協議參數的長度限制,如客戶端ID,范圍,重定向URI等。

用戶交互

    • LoginUrlLogoutUrlConsentUrlErrorUrl

      設置登錄,注銷,同意和錯誤頁面的URL。

    • LoginReturnUrlParameter

      設置傳遞給登錄頁面的返回URL參數的名稱。默認為returnUrl

    • LogoutIdParameter

      設置傳遞給注銷頁面的注銷消息id參數的名稱。默認為logoutId

    • ConsentReturnUrlParameter

      設置傳遞給同意頁面的返回URL參數的名稱。默認為returnUrl

    • ErrorIdParameter

      設置傳遞給錯誤頁面的錯誤消息id參數的名稱。默認為errorId

    • CustomRedirectReturnUrlParameter

      設置從授權端點傳遞給自定義重定向的返回URL參數的名稱。默認為returnUrl

    • CookieMessageThreshold

      IdentityServer和某些UI頁面之間的某些交互需要cookie來傳遞狀態和上下文(上面的任何具有可配置“message id”參數的頁面)。由於瀏覽器對cookie的數量及其大小有限制,因此該設置用於防止創建過多的cookie。該值設置將創建的任何類型的消息cookie的最大數量。一旦達到限制,將清除最早的消息cookie。這有效地表示用戶在使用IdentityServer時可以打開多少個選項卡。

緩存

這些設置僅在啟動時在服務配置中啟用了相應的緩存時才適用。

    • ClientStoreExpiration

      從客戶端存儲加載的客戶端配置的緩存持續時間。

    • ResourceStoreExpiration

      緩存從資源存儲加載的標識和API資源配置的持續時間。

CORS

IdentityServer支持某些端點的CORS。底層CORS實現由ASP.NET Core提供,因此它在依賴注入系統中自動注冊。

    • CorsPolicyName

      將為IdentityServer中的CORS請求評估的CORS策略的名稱(默認為"IdentityServer4")。處理此問題的策略提供程序是根據ICorsPolicyService依賴注入系統中注冊的方式實現的。如果您希望自定義允許連接的CORS源集,那么建議您提供自定義實現ICorsPolicyService

    • CorsPaths

      IdentityServer中支持CORS的端點。默認為發現,用戶信息,令牌和吊銷端點。

    • PreflightCacheDuration

      Nullable <TimeSpan>指示在預檢Access-Control-Max-Age響應頭中使用的值。默認為null,表示未在響應上設置緩存標頭。

CSP(內容安全策略)

在適當的情況下,IdentityServer會為某些響應發出CSP標頭。

    • Level

      要使用的CSP級別。默認情況下使用CSP級別2,但如果必須支持舊瀏覽器,則將其更改CspLevel.One為容納它們。

    • AddDeprecatedHeader

      指示是否X-Content-Security-Policy還應發出較舊的CSP標頭(除了基於標准的標頭值)。默認為true。

實體框架支持

為IdentityServer中的配置和操作數據擴展點提供了基於EntityFramework的實現。EntityFramework的使用允許任何EF支持的數據庫與此庫一起使用。

這個庫的repo位於這里,NuGet包在這里

此庫提供的功能分為兩個主要區域:配置存儲和操作存儲支持。根據托管應用程序的需要,這兩個不同的區域可以獨立使用或一起使用。

配置存儲支持客戶端,資源和CORS設置

如果希望從EF支持的數據庫加載客戶端,標識資源,API資源或CORS數據(而不是使用內存配置),則可以使用配置存儲。此支持提供了IClientStoreIResourceStore,以及ICorsPolicyService可擴展性點的實現。這些實現使用一個DbContext被調用的類ConfigurationDbContext來為數據庫中的表建模。

要使用配置存儲支持,請AddConfigurationStore在調用后使用擴展方法AddIdentityServer

public IServiceProvider ConfigureServices(IServiceCollection services)
{
  const string connectionString = @"Data Source=(LocalDb)\MSSQLLocalDB;database=IdentityServer4.EntityFramework-2.0.0;trusted_connection=yes;";
  var migrationsAssembly = typeof(Startup).GetTypeInfo().Assembly.GetName().Name;

  services.AddIdentityServer()
      // this adds the config data from DB (clients, resources, CORS)
      .AddConfigurationStore(options =>
      {
          options.ConfigureDbContext = builder =>
              builder.UseSqlServer(connectionString,
                  sql => sql.MigrationsAssembly(migrationsAssembly));
      });
}

要配置配置存儲,請使用ConfigurationStoreOptions傳遞給配置回調的options對象。

ConfigurationStoreOptions

此選項類包含用於控制配置存儲的屬性ConfigurationDbContext

  • ConfigureDbContext

    Action<DbContextOptionsBuilder>用作回調的類型委托來配置底層證券ConfigurationDbContextConfigurationDbContext如果直接使用EF,代理可以以相同的方式配置AddDbContext,這允許使用任何EF支持的數據庫。

  • DefaultSchema

    允許為中的所有表設置默認數據庫模式名稱ConfigurationDbContext

操作存儲支持授權授予,同意和令牌(刷新和引用)

如果希望從EF支持的數據庫(而不是默認的內存數據庫)加載授權授予,同意和令牌(刷新和引用),則可以使用操作存儲。此支持提供了IPersistedGrantStore可擴展性點的實現。該實現使用一個DbContext被調用的類PersistedGrantDbContext來為數據庫中的表建模。

要使用操作商店支持,請AddOperationalStore在調用后使用擴展方法AddIdentityServer

public IServiceProvider ConfigureServices(IServiceCollection services)
{
  const string connectionString = @"Data Source=(LocalDb)\MSSQLLocalDB;database=IdentityServer4.EntityFramework-2.0.0;trusted_connection=yes;";
  var migrationsAssembly = typeof(Startup).GetTypeInfo().Assembly.GetName().Name;

  services.AddIdentityServer()
      // this adds the operational data from DB (codes, tokens, consents)
      .AddOperationalStore(options =>
      {
          options.ConfigureDbContext = builder =>
              builder.UseSqlServer(connectionString,
                  sql => sql.MigrationsAssembly(migrationsAssembly));

          // this enables automatic token cleanup. this is optional.
          options.EnableTokenCleanup = true;
          options.TokenCleanupInterval = 30; // interval in seconds
      });
}

要配置操作存儲,請使用OperationalStoreOptions傳遞給配置回調的options對象。

OperationalStoreOptions

此選項類包含用於控制操作存儲的屬性PersistedGrantDbContext

  • ConfigureDbContext

    Action<DbContextOptionsBuilder>用作回調的類型委托來配置底層證券PersistedGrantDbContextPersistedGrantDbContext如果直接使用EF,代理可以以相同的方式配置AddDbContext,這允許使用任何EF支持的數據庫。

  • DefaultSchema

    允許為中的所有表設置默認數據庫模式名稱PersistedGrantDbContext

  • EnableTokenCleanup

    指示是否將從數據庫中自動清除過時條目。默認是false

  • TokenCleanupInterval

    令牌清理間隔(以秒為單位)。默認值為3600(1小時)。

跨不同版本的IdentityServer的數據庫創建和架構更改

跨不同版本的IdentityServer(以及EF支持)很可能會更改數據庫架構以適應新的和不斷變化的功能。

我們不為創建數據庫或將數據從一個版本遷移到另一個版本提供任何支持。您需要以組織認為合適的任何方式管理數據庫創建,架構更改和數據遷移。

使用EF遷移是一種可行的方法。如果您確實希望使用遷移,請參閱EF快速入門以獲取有關如何入門的示例,或參閱有關EF遷移的Microsoft 文檔

我們還為當前版本的數據庫模式發布了示例SQL腳本

ASP.NET身份支持

提供了基於ASP.NET身份的實現,用於管理IdentityServer用戶的身份數據庫。此實現實現IdentityServer中的擴展點,以便為用戶加載身份數據以將聲明發送到令牌。

這個支持的回購位於此處,NuGet包就在這里

要使用此庫,請正常配置ASP.NET標識。然后AddAspNetIdentity在調用后使用擴展方法AddIdentityServer

public void ConfigureServices(IServiceCollection services)
{
  services.AddIdentity<ApplicationUser, IdentityRole>()
      .AddEntityFrameworkStores<ApplicationDbContext>()
      .AddDefaultTokenProviders();

  services.AddIdentityServer()
      .AddAspNetIdentity<ApplicationUser>();
}

AddAspNetIdentity需要作為通用參數來為您的用戶建模ASP.NET身份的類(以及傳遞給AddIdentityASP.NET身份的同一個用戶)。這將配置IdentityServer使用的ASP.NET身份的實現IUserClaimsPrincipalFactoryIResourceOwnerPasswordValidatorIProfileService。它還配置了一些用於IdentityServer的ASP.NET Identity選項(例如要使用的聲明類型和身份驗證cookie設置)。

訓練

以下是一些在線,遠程和課堂培訓選項,以了解有關ASP.NET核心身份和IdentityServer4的更多信息。

現代應用程序的身份和訪問控制(使用ASP.NET Core 2和IdentityServer4)

這是我們為期三天的旗艦課程(包括廣泛的實踐實驗室),我們作為會議,現場和遠程的一部分提供。

可在此處找到公共培訓的議程和日期,請 聯系我們參加私人研討會。

PluralSight課程

PluralSight有一些關於身份,ASP.NET Core和IdentityServer的好課程。

舊的

博客文章

團隊帖子

什么是新帖子

社區帖子

視頻

2018年

2017年

2016

2015年

2014

以前


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM