一.簡介
官方文檔https://identityserver4.readthedocs.io/en/latest/
IdentityServer4是一個框架,IdentityServer4是為ASP.NET CORE量身定制的實現了OpenId Connect和OAuth2.0協議的認證授權中間件。OpenId用於身份認證(Authentication) OAuth2.0 用於授權(Authorization) OpenId Connect = OIDC = (Identity, Authentication) + OAuth2.0。
IdentityServer4功能特性
- Authentication as a Service:可以為你的應用(如網站、本地應用、移動端、服務)做集中式的登錄邏輯和工作流控制。IdentityServer是完全實現了OpenID Connect協議標准
- Single Sign-on / Sign-out:在多個應用程序類型上進行單點登錄(和單點退出)。
- Access Control for APIs:為不同類型的客戶端,例如服務器到服務器、web應用程序、SPAs和本地/移動應用程序,發出api的訪問令牌。
- Federation Gateway:支持來自Azure Active Directory, Google, Facebook這些知名應用的身份認證,可以不必關心連接到這些應用的細節就可以保護你的應用。
- Focus on Customization:最重要的是identityserver可以根據需求自行開發來適應應用程序的變化。identityserver不是一個框架、也不是一個盒裝產品或一個saas系統,您可以編寫代碼來適應各種場景。
IdentityServer是將規范兼容的OpenID Connect和OAuth 2.0端點添加到任意ASP.NET Core應用程序的中間件。通常,您構建(或重新使用)包含登錄和注銷頁面的應用程序,IdentityServer中間件會向其添加必要的協議頭,以便客戶端應用程序可以與其對話 使用這些標准協議。
IdentityServer4 默認支持兩種類型的 Token,一種是 Reference Token,一種是 JWT Token 。前者的特點是 Token 的有效與否是由 Token 頒發服務集中化控制的,頒發的時候會持久化 Token,然后每次驗證都需要將 Token 傳遞到頒發服務進行驗證,是一種中心化的比較傳統的驗證方式。JWT Token 的特點與前者相反,每個資源服務不需要每次都要都去頒發服務進行驗證 Token 的有效性驗證,該 Token 由三部分組成,其中最后一部分包含了一個簽名,是在頒發的時候采用非對稱加密算法(最新的JWT Token)進行數據簽名的,保證了 Token 的不可篡改性,保證了安全,與頒發服務的交互,僅僅是獲取公鑰用於驗證簽名,且該公鑰獲取以后可以自己緩存,持續使用,不用再去交互獲得,除非Token包含的 keyid 對應的 公鑰沒被緩存(新的),就會再次向頒發服務獲取。流程圖:
二.identity server4 五種授權模式
下面介紹4種模式安全性從低到高
客戶端模式
客戶端模式只對客戶端進行授權,不涉及到用戶信息。如果你的api需要提供到第三方應用,第三方應用自己做用戶授權,不需要用到你的用戶資源,就可以用客戶端模式,只對客戶端進行授權訪問api資源。
這是一種最簡單的模式,只要client請求,我們就將AccessToken發送給它。這種模式是最方便但最不安全的模式。因此這就要求我們對client完全的信任,而client本身也是安全的
密碼模式
需要客戶端提供用戶名和密碼,密碼模式相較於客戶端憑證模式。通過User的用戶名和密碼向Identity Server申請訪問令牌。
如果你高度信任某個應用,RFC 6749 也允許用戶把用戶名和密碼,直接告訴該應用
(授權碼)隱藏模式
密碼模式將用戶的密碼暴露給了客戶端,這無疑是不安全的,隱藏模式可以解決這個問題,由用戶自己在IdentityServer服務器進行登錄驗證,客戶端不需要知道用戶的密碼。
有些 Web 應用是前后端分離的純前端應用,沒有后端。這時就不能用上面的授權碼模式了,必須將令牌儲存在前端。
這種方式沒有授權碼這個中間步驟,所以稱為(授權碼)"隱藏式"(implicit)。
一般用的這個,token會直接返回到訪問的地方,適用於前后端分離的項目(前后端分離后,只能把token保存到前端了,為他只有前端沒有服務器)
授權碼模式
授權碼模式隱藏碼模式最大不同是授權碼模式不直接返回token,而是先返回一個授權碼,然后再根據這個授權碼去請求token。這比隱藏模式更為安全。從應用場景上來區分的話,隱藏模式適應於全前端的應用,授權碼模式適用於有后端的應用,因為客戶端根據授權碼去請求token時是需要把客戶端密碼轉進來的,為了避免客戶端密碼被暴露,所以請求token這個過程需要放在后台
混合模式(Hybrid),就是基於隱藏模式與授權碼模式的結合
OpenIdConnect是OAuth2.0與OpenId的結合,並加入了一個重要的概念:id_token。我們之前所講的token是用於訪問授權的access_token,而id_token是用於身份驗證的,作用完全不同,這一點要區分開來。access_token是OAth2.0特性,而id_token是OpenIdConnect方案為改善OAuth2.0方案在身份驗證方面的薄弱而加入的特性。
客戶端獲取Id_token與隱藏模式和授權碼模式一樣,都是通過redirect_url參數返回的,所以前面的四種模式中的客戶端模式與密碼模式不支持獲取id_token,而授權碼模式受限於流程,必需先取得Code才能取到token,所以不能直接支持獲取id_token,如果需求是使用授權碼模式,同時又需要id_token,OpenIdConnect支持第五種模式:混合模式(Hybrid),就是基於隱藏模式與授權碼模式的結合
三.使用
IdentityService4(簡稱is4)有兩種初始化方式:
1使用命令行安裝is4模板
1)安裝模板命令:dotnet new -i IdentityServer4.Templates
2)安裝完成之后執行dotnet new命令如下圖所示多出了一些模板
3)使用dotnet new is4empty -n IdentityServer創建一個is4empty模板,is4項目初始化完成
2.手動建立
1、創建ASP.NET Core Web應用程序,選擇MVC。
2、添加nuget包:IdentityServer4。
3、添加Config.cs文件作為IdentityServer配置文件,用於定義IdentityServer資源和客戶端等。

using IdentityModel; using IdentityServer4; using IdentityServer4.Models; using IdentityServer4.Test; using System; using System.Collections.Generic; using System.Linq; using System.Security.Claims; using System.Threading.Tasks; namespace WebApplication10 { public class OAuthConfig { /// <summary> /// token有效時間 過期秒數 //2小時 = 3600 * 2 /// </summary> public static int ExpireIn = 3600; /// <summary> /// 跨域地址 /// </summary> public static string[] CorUrls = new[] { "http://*:4137", "http://*:5072" }; /// <summary> /// Api資源名稱 /// </summary> public static string ApiName = "user_api"; /// <summary> /// 客戶端唯一ID /// </summary> public static string ClientId = "user_clientid"; /// <summary> /// 密鑰 /// </summary> public static string Secret = "user_secret"; } /// <summary> /// 密碼授權模式 分資源分為身份資源(Identity resources)和API資源(API resources)。 /// </summary> public class Config { #region 定義資源 // 身份信息授權資源 public static IEnumerable<IdentityResource> GetIdentityResources() => new IdentityResource[] { new IdentityResources.OpenId(), new IdentityResources.Profile() }; //API訪問授權資源 public static IEnumerable<ApiResource> GetApiResources() => new ApiResource[] { //標識名稱 顯示名稱(自定義) new ApiResource(OAuthConfig.ApiName,"我的Ids4") { UserClaims = { ClaimTypes.Name, JwtClaimTypes.Name }, ApiSecrets = new List<Secret>() { new Secret(OAuthConfig.Secret.Sha256()) } } }; #endregion #region 定義客戶端Client /// <summary> /// 4種模式 客戶端模式(ClientCredentials) 密碼模式(ResourceOwnerPassword) 隱藏模式(Implicit) 授權碼模式(Code) /// </summary> /// <returns></returns> public static IEnumerable<Client> GetClients() => new Client[] { new Client { ClientId = OAuthConfig.ClientId,//客戶端的唯一ID ClientName = "隱式模式",//客戶端顯示名稱 AllowedGrantTypes = GrantTypes.Implicit,// 隱式模式 ClientSecrets = new []{ new Secret(OAuthConfig.Secret.Sha256()) },//客戶端加密方式 RequireConsent = false, //如果不需要顯示否同意授權 頁面 這里就設置為false AllowAccessTokensViaBrowser = true,//控制是否通過瀏覽器傳輸此客戶端的訪問令牌 AccessTokenLifetime = OAuthConfig.ExpireIn, //過期秒數 //登錄成功跳轉地址 RedirectUris = { "http://localhost:4137/oauth2-redirect.html", }, //退出登錄跳轉地址 PostLogoutRedirectUris = { $"http://www.taobao.com" }, //跨域地址 AllowedCorsOrigins = OAuthConfig.CorUrls, //配置授權范圍,這里指定哪些API 受此方式保護 AllowedScopes = { IdentityServerConstants.StandardScopes.OpenId, IdentityServerConstants.StandardScopes.Profile, OAuthConfig.ApiName } } }; #endregion /// <summary> /// 測試的賬號和密碼 /// </summary> /// <returns></returns> public static List<TestUser> GetTestUsers() { return new List<TestUser> { new TestUser() { SubjectId = "1", Username = "admin", Password = "123456" }, new TestUser() { SubjectId = "2", Username = "test", Password = "123456" } }; } } }
4.配置Startup

using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Threading.Tasks; using Admin.IdentityServer.Account; using IdentityServer4.Configuration; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; using Microsoft.OpenApi.Models; using Newtonsoft.Json; using Newtonsoft.Json.Serialization; namespace WebApplication10 { public class Startup { public Startup(IConfiguration configuration) { Configuration = configuration; } public IConfiguration Configuration { get; } private string basePath => AppContext.BaseDirectory; // This method gets called by the runtime. Use this method to add services to the container. public void ConfigureServices(IServiceCollection services) { var builder = services.AddIdentityServer(options => { options.UserInteraction = new UserInteractionOptions { LoginUrl = "/user/login", LogoutUrl = "/user/logout" }; }) .AddInMemoryIdentityResources(Config.GetIdentityResources()) .AddInMemoryApiResources(Config.GetApiResources())//把受保護的Api資源添加到內存中 .AddInMemoryClients(Config.GetClients())//客戶端配置添加到內存中 .AddProfileService<AdminProfileService>() .AddResourceOwnerValidator<AdminResourceOwnerPasswordValidator>(); //添加證書加密方式,執行該方法,會先判斷tempkey.rsa證書文件是否存在,如果不存在的話,就創建一個新的tempkey.rsa證書文件,如果存在的話,就使用此證書文件 builder.AddDeveloperSigningCredential(); #region Cors 跨域 services.AddCors(options => { options.AddPolicy("Limit", policy => { policy .WithOrigins(OAuthConfig.CorUrls) .AllowAnyHeader() .AllowAnyMethod() .AllowCredentials(); }); }); #endregion services.AddControllersWithViews(); } // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. public void Configure(IApplicationBuilder app, IWebHostEnvironment env) { if (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); } else { app.UseExceptionHandler("/Home/Error"); } app.UseCors("Limit"); app.UseStaticFiles(); app.UseRouting(); //啟動ids4中間件 app.UseIdentityServer(); app.UseAuthorization(); app.UseEndpoints(endpoints => { endpoints.MapControllerRoute( name: "default", pattern: "{controller=Home}/{action=Index}/{id?}"); }); } } }
5.配置服務端

services.AddAuthentication("Bearer") .AddIdentityServerAuthentication(options => { options.Authority = "http://localhost:4137"; //配置Identityserver的授權地址 options.RequireHttpsMetadata = false; //不需要https options.ApiName = "user_api"; //api的name,需要和config的名稱相同 });
參考地址 IdentityServer4之Implicit和純前端好像很配哦 https://github.com/zyq025/IDS4Demo
live-server安裝
npm install -g live-server
live-server
live-server運行在8080端口下,可以通過127.0.0.1:8080來訪問靜態頁面。
npm install --save live-server-https
這是https