IdentityServer4 是爲ASP.NET Core系列量身打造的一款基於 OpenID Connect 和 OAuth 2.0 認證的框架git
IdentityServer4官方文檔:https://identityserver4.readthedocs.io/github
看這篇文章前默認你對IdentityServer4 已經有一些瞭解。web
本篇使用IdentityServer4的4.x版本,跟老版本的稍微有些差異。下面直接進入正題。api
使用IdentityServer4 來搭建一個鑑權中心,首先建議安裝一下IdentityServer4的官方項目模板。也能夠不安裝,本身建立項目,而後NuGet安裝須要的包也行。(不過仍是推薦用官方的模板,很方便)。app
命令行執行:dotnet new -i IdentityServer4.Templates
框架
安裝完成後會多出如下項目模板:asp.net
我這裏選用is4inmem這個模板來建立項目,這個模板的數據都是寫死在內存中的,而且包含了Quickstart頁面,比較簡單方便。ide
來到個人項目目錄下執行:dotnet new is4inmem --name Idp
post
執行完成會生成如下文件:測試
VS2019打開項目:
運行項目:
修改Startup:
// in-memory, code config builder.AddInMemoryIdentityResources(Config.IdentityResources); builder.AddInMemoryApiScopes(Config.ApiScopes); //添加API資源 builder.AddInMemoryApiResources(Config.ApiResources); builder.AddInMemoryClients(Config.Clients);
這裏比以前版本多了一個添加ApiScopes的方法:
builder.AddInMemoryApiScopes(Config.ApiScopes);
由於我接下來有要保護的API資源,因此須要添加一行:
builder.AddInMemoryApiResources(Config.ApiResources);
Config中的代碼:
public static class Config { public static IEnumerable<IdentityResource> IdentityResources => new IdentityResource[] { new IdentityResources.OpenId(), new IdentityResources.Profile(), }; public static IEnumerable<ApiScope> ApiScopes => new ApiScope[] { new ApiScope("scope1"), //new ApiScope("scope2"), }; public static IEnumerable<ApiResource> ApiResources => new ApiResource[] { new ApiResource("api1","#api1") { //!!!重要 Scopes = { "scope1"} }, //new ApiResource("api2","#api2") //{ // //!!!重要 // Scopes = { "scope2"} //}, }; public static IEnumerable<Client> Clients => new Client[] { new Client { ClientId = "postman client", ClientName = "Client Credentials Client", AllowedGrantTypes = GrantTypes.ClientCredentials, ClientSecrets = { new Secret("postman secret".Sha256()) }, AllowedScopes = { "scope1" } }, }; }
我添加了一個ID爲postman client的客戶端,受權模式就用最簡單的ClientCredentials客戶端模式。須要注意的是4.x版本的ApiScope和ApiResource是分開配置的,而後在ApiResource中必定要添加Scopes。若是你在網上搜的IdentityServer4教程比較老的,都是沒有這個ApiScope的,默認ApiResource的Name做爲Scope。相似這樣:
public static IEnumerable<ApiResource> ApiResources => new ApiResource[] { new ApiResource("api1","#api1"),//錯誤 new ApiResource("api2","#api2"),//錯誤 }; public static IEnumerable<Client> Clients => new Client[] { new Client { ...... AllowedScopes = { "api1", "api2" } }, };
若是你這麼寫的話,雖然不影響你獲取token,可是你訪問api資源的話,永遠會獲得一個401錯誤!!!
下面添加一個api1資源,新建asp.netcore web應用並使用webapi模板:
NuGet安裝:Microsoft.AspNetCore.Authentication.JwtBearer
Startup部分代碼:
public void ConfigureServices(IServiceCollection services) { services.AddControllers(); services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme) .AddJwtBearer(options => { //IdentityServer地址 options.Authority = "http://localhost:5001"; //對應Idp中ApiResource的Name options.Audience = "api1"; //不使用https options.RequireHttpsMetadata = false; }); } // 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(); } app.UseHttpsRedirection(); app.UseRouting(); //身份驗證 app.UseAuthentication(); //受權 app.UseAuthorization(); app.UseEndpoints(endpoints => { endpoints.MapControllers(); }); }
給WeatherForecastController添加[Authorize]
標記:
運行Api1Resource,用postman測試訪問weatherforecast接口:
此時獲得401錯誤。下面先去Idp獲取一個token:
拿到token後再去訪問weatherforecast就沒問題了:
進行到這裏,好像跟scope都沒什麼關係,那麼scope到底有什麼用處呢?
繼續修改代碼。
Api1Resource項目NuGet安裝:IdentityServer4.AccessTokenValidation
再新建一個TestController用於區分:
下面我須要作的是使用scope結合策略受權來分別限制TestController和WeatherForecastController的訪問權限。
修改Startup:
public void ConfigureServices(IServiceCollection services) { ...... services.AddAuthorization(options => { //基於策略受權 options.AddPolicy("WeatherPolicy", builder => { //客戶端Scope中包含api1.weather.scope才能訪問 builder.RequireScope("api1.weather.scope"); }); //基於策略受權 options.AddPolicy("TestPolicy", builder => { //客戶端Scope中包含api1.test.scope才能訪問 builder.RequireScope("api1.test.scope"); }); }); }
爲了好理解,我把scope名稱分別改爲了:api1.weather.scope和api1.test.scope。
WeatherForecastController的Authorize標記修改一下:[Authorize(Policy = "WeatherPolicy")]
TestController的代碼很簡單:
由於修改了scope名稱,須要把Idp中的scope名稱也改一下:
public static IEnumerable<ApiScope> ApiScopes => new ApiScope[] { new ApiScope("api1.weather.scope"), new ApiScope("api1.test.scope"), //new ApiScope("scope2"), }; public static IEnumerable<ApiResource> ApiResources => new ApiResource[] { new ApiResource("api1","#api1") { //!!!重要 Scopes = { "api1.weather.scope", "api1.test.scope" } }, //new ApiResource("api2","#api2") //{ // //!!!重要 // Scopes = { "scope2"} //}, };
客戶端定義,AllowedScopes暫時只給一個api1.weather.scope測試一下
public static IEnumerable<Client> Clients => new Client[] { new Client { ClientId = "postman client", ClientName = "Client Credentials Client", AllowedGrantTypes = GrantTypes.ClientCredentials, ClientSecrets = { new Secret("postman secret".Sha256()) }, AllowedScopes = { "api1.weather.scope" } }, };
postman獲取token:
訪問weatherforecast接口,正常響應200。
再訪問test,獲得403錯誤:
接下來修改一下Idp的客戶端定義,添加api1.test.scope:
AllowedScopes = { "api1.weather.scope", "api1.test.scope" }
修改Idp後必定要從新獲取token,jwt就是這樣,一旦生成就沒法改變。
拿到新的token後訪問test和weatherforecast,這時候就均可以正常響應了。
以上使用IdentityServer4搭建了一個鑑權中心,保護API資源,並使用ApiScope配合策略受權完成了一個簡單的權限控制。IdentityServer4的玩法很是多,知識點也不少。強烈推薦B站的@solenovex 楊老師的視頻,地址:https://www.bilibili.com/video/BV16b411k7yM 多看幾遍,會有收穫。。。
須要代碼的點這裏:https://github.com/xiajingren/IdentityServer4-4.x-Scope-Demo