IdentityServer4-用EF配置Client(一)
一、背景
IdentityServer4的介紹將不再敘述,百度下可以找到,且官網的快速入門例子也有翻譯的版本。這裡主要從Client應用場景方面介紹對IdentityServer4的應用。
首先簡要介紹ID Token和Access Token:
Access Token是授權第三方客戶端訪問受保護資源的令牌。 ID Token是第三方客戶端標識使用者身份認證的問令牌,是JSON Web Token格式。
二、Client應用場景介紹
Client類是為OpenID Connect或OAuth 2.0 協議建模的。
我們先看官網快速入門中給的Client例子
public static IEnumerable<Client> GetClients() { // client credentials client return new List<Client> { new Client { ClientId = "Client", AllowedGrantTypes= GrantTypes.ClientCredentials, ClientSecrets = { new Secret("secret".Sha256()) }, AllowedScopes = { "api1" } }, // resource owner password grant client newClient { ClientId = "ro.client", AllowedGrantTypes = GrantTypes.ResourceOwnerPassword, ClientSecrets = { new Secret("secret".Sha256()) }, AllowedScopes = { "api1" } }, // OpenID Connect hybrid flow and client credentials client (MVC) 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 }, // 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" }, } }; }
裡面主要介紹四種Client應用場景。
(1)客戶端模式(AllowedGrantTypes = GrantTypes.ClientCredentials)
這是一種最簡單的授權方式,應用於服務於服務之間的通訊,token通常代表的是客戶端的請求,而不是使用者。
使用這種授權型別,會向token endpoint傳送token請求,並獲得代表客戶機的access token。客戶端通常必須使用token endpoint的Client ID和secret進行身份驗證。
適用場景:用於和使用者無關,服務與服務之間直接互動訪問資源
(2)密碼模式(ClientAllowedGrantTypes = GrantTypes.ResourceOwnerPassword)
該方式傳送使用者名稱和密碼到token endpoint,向資源伺服器請求令牌。這是一種“非互動式”授權方法。
官網上稱,為了解決一些歷史遺留的應用場景,所以保留了這種授權方式,但不建議使用。
適用場景:用於當前的APP是專門為服務端設計的情況。
(3)混合模式和客戶端模式(ClientAllowedGrantTypes =GrantTypes.HybridAndClientCredentials)
ClientCredentials授權方式在第一種應用場景已經介紹了,這裡主要介紹Hybrid授權方式。Hybrid是由Implicit和Authorization code結合起來的一種授權方式。其中Implicit用於身份認證,ID token被傳輸到瀏覽器並在瀏覽器進行驗證;而Authorization code使用反向通道檢索token和重新整理token。
推薦適用Hybrid模式。
適用場景:用於MVC框架,伺服器端 Web 應用程式和原生桌面/移動應用程式。
(4)簡化模式(ClientAllowedGrantTypes =GrantTypes.Implicit)
Implicit要麼僅用於服務端和JavaScript應用程式端進行身份認證,要麼用於身份身份驗證和access token的傳輸。
在Implicit中,所有token都通過瀏覽器傳輸的。
適用場景:JavaScript應用程式。
三、Server端搭建
為了介紹IdentityServer4的Client應用場景,我們需要先搭建IdentityServer服務端。
這裡搭建的是使用EF Core來做資料操作,儲存到SQL Server中。
(1)新建API專案
(2)安裝IdentityServer4.EntityFramework包
(3)安裝IdentityServer4包
(4)右鍵專案的屬性,編輯專案的.csproj檔案
新增如下元素
<ItemGroup> <DotNetCliToolReference Include="Microsoft.EntityFrameworkCore.Tools.DotNet" Version="2.0.0" /> </ItemGroup>
如圖:
(5)cmd管理員身份進入專案目錄路徑(D:\IdentityServer4\Server),執行:dotnet ef
(6)專案內新增Config.cs類,程式碼如下
public class Config { public static List<TestUser> GetUsers() { return new List<TestUser> { new TestUser { SubjectId = "1", Username = "alice", Password = "password", Claims = new List<Claim>(){new Claim(JwtClaimTypes.Role,"superadmin") } }, new TestUser { SubjectId = "2", Username = "bob", Password = "password", Claims = new List<Claim> { new Claim("name", "Bob"), new Claim("website", "https://bob.com") }, } }; } public static IEnumerable<Client> GetClients() { // client credentials client return new List<Client> { new Client { ClientId = "Client", AllowedGrantTypes = GrantTypes.ClientCredentials, ClientSecrets = { new Secret("secret".Sha256()) }, AllowedScopes = { "api1" } }, // resource owner password grant client new Client { ClientId = "ro.client", AllowedGrantTypes = GrantTypes.ResourceOwnerPassword, ClientSecrets = { new Secret("secret".Sha256()) }, AllowedScopes = { "api1" } }, // OpenID Connect hybrid flow and client credentials client (MVC) 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 }, // 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" }, } }; } public static IEnumerable<IdentityResource> GetIdentityResources() { return new List<IdentityResource> { new IdentityResources.OpenId(), new IdentityResources.Profile(), }; } public static IEnumerable<ApiResource> GetApiResources() { return new List<ApiResource> { new ApiResource("api1", "My API") }; }
新增引用:
using IdentityModel;
using IdentityServer4;
using IdentityServer4.Models;
using IdentityServer4.Test;
using System.Collections.Generic;
using System.Security.Claims;
(7)編輯Startup.cs檔案的ConfigureServices方法,改成如下程式碼。
public void ConfigureServices(IServiceCollection services) { const string connectionString = @"Server=localhost;database=IdentityServer4;User ID=sa;Password=Pwd;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 = false;//是否從資料庫清楚令牌資料,預設為false options.TokenCleanupInterval = 300;//令牌過期時間,預設為3600秒,一個小時 }); //.AddInMemoryClients(Config.GetClients()); services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1); }
新增引用:
using Microsoft.EntityFrameworkCore;
using System.Reflection;
(8)cmd管理員身份進入到專案目錄路徑(D:\IdentityServer4\Server\Server),注意,多了一層目錄,分別執行以下兩條指令:
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資料夾
(9)在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(); } } }
新增引用:
using IdentityServer4.EntityFramework.DbContexts;
using IdentityServer4.EntityFramework.Mappers;
(10)在Startup.cs中的Configure方法修改成以下程式碼。
public void Configure(IApplicationBuilder app, IHostingEnvironment env) { //if (env.IsDevelopment()) //{ // app.UseDeveloperExceptionPage(); //} InitializeDatabase(app); //app.UseMvc(); }
到這裡,把專案以控制檯形式執行
點選執行,可以跑起來,且生成資料庫IdentityServer4DB。
關於Client的說明可以查閱官網資料:https://identityserver4.readthedocs.io/en/release/reference/client.html
原始碼地址:https://github.com/Bingjian-Zhu/Server.git
服務端準備好之後,下篇文章開始介紹Client客戶端的應用。
文中如有錯漏,歡迎指正,將對此係列文章進行維護。