1. 程式人生 > >IdentityServer4 4.x版本 配置Scope的正確姿勢

IdentityServer4 4.x版本 配置Scope的正確姿勢

# 前言 > IdentityServer4 是為ASP.NET Core系列量身打造的一款基於 OpenID Connect 和 OAuth 2.0 認證的框架 IdentityServer4官方文件:https://identityserver4.readthedocs.io/ 看這篇文章前預設你對IdentityServer4 已經有一些瞭解。 本篇使用IdentityServer4的4.x版本,跟老版本的稍微有些差別。下面直接進入正題。 # 鑑權中心 ## 建立IdentityServer4專案 使用IdentityServer4 來搭建一個鑑權中心,首先建議安裝一下IdentityServer4的官方專案模板。也可以不安裝,自己建立專案,然後NuGet安裝需要的包也行。(不過還是推薦用官方的模板,很方便)。 命令列執行:`dotnet new -i IdentityServer4.Templates` ![image-20200629205619088](https://xhznl-blogs-bucket.oss-cn-beijing.aliyuncs.com/images/20200702120419.png) 安裝完成後會多出以下專案模板: ![image-20200629205731577](https://xhznl-blogs-bucket.oss-cn-beijing.aliyuncs.com/images/20200702120429.png) 我這裡選用is4inmem這個模板來建立專案,這個模板的資料都是寫死在記憶體中的,並且包含了Quickstart頁面,比較簡單方便。 來到我的專案目錄下執行:`dotnet new is4inmem --name Idp` ![image-20200701190325246](https://xhznl-blogs-bucket.oss-cn-beijing.aliyuncs.com/images/20200702120433.png) 執行完成會生成以下檔案: ![image-20200701195853822](https://xhznl-blogs-bucket.oss-cn-beijing.aliyuncs.com/images/20200702120437.png) VS2019開啟專案: ![image-20200701195955107](https://xhznl-blogs-bucket.oss-cn-beijing.aliyuncs.com/images/20200702120441.png) 執行專案: ![image-20200701200225015](https://xhznl-blogs-bucket.oss-cn-beijing.aliyuncs.com/images/20200702120444.png) ## 配置ApiResource、ApiScope、Clients 修改Startup: ```csharp // 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中的程式碼: ```csharp public static class Config { public static IEnumerable IdentityResources => new IdentityResource[] { new IdentityResources.OpenId(), new IdentityResources.Profile(), }; public static IEnumerable ApiScopes => new ApiScope[] { new ApiScope("scope1"), //new ApiScope("scope2"), }; public static IEnumerable ApiResources => new ApiResource[] { new ApiResource("api1","#api1") { //!!!重要 Scopes = { "scope1"} }, //new ApiResource("api2","#api2") //{ // //!!!重要 // Scopes = { "scope2"} //}, }; public static IEnumerable 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 ApiResources => new ApiResource[] { new ApiResource("api1","#api1"),//錯誤 new ApiResource("api2","#api2"),//錯誤 }; public static IEnumerable Clients => new Client[] { new Client { ...... AllowedScopes = { "api1", "api2" } }, }; ``` 如果你這麼寫的話,雖然不影響你獲取token,但是你訪問api資源的話,永遠會得到一個401錯誤!!! # ApiResource 下面新增一個api1資源,新建asp.netcore web應用並使用webapi模板: ![image-20200701211036365](https://xhznl-blogs-bucket.oss-cn-beijing.aliyuncs.com/images/20200702120453.png) NuGet安裝:`Microsoft.AspNetCore.Authentication.JwtBearer` Startup部分程式碼: ```csharp 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]`標記: ![image-20200701214601854](https://xhznl-blogs-bucket.oss-cn-beijing.aliyuncs.com/images/20200702120458.png) 執行Api1Resource,用postman測試訪問weatherforecast介面: ![image-20200701214742071](https://xhznl-blogs-bucket.oss-cn-beijing.aliyuncs.com/images/20200702120502.png) 此時得到401錯誤。下面先去Idp獲取一個token: ![image-20200701215031535](https://xhznl-blogs-bucket.oss-cn-beijing.aliyuncs.com/images/20200702120505.png) 拿到token後再去訪問weatherforecast就沒問題了: ![image-20200701215748634](https://xhznl-blogs-bucket.oss-cn-beijing.aliyuncs.com/images/20200702120509.png) 進行到這裡,好像跟scope都沒什麼關係,那麼scope到底有什麼用處呢? # ApiScope策略授權 繼續修改程式碼。 Api1Resource專案NuGet安裝:IdentityServer4.AccessTokenValidation ![image-20200701221017612](https://xhznl-blogs-bucket.oss-cn-beijing.aliyuncs.com/images/20200702120514.png) 再新建一個TestController用於區分: ![image-20200701223359517](https://xhznl-blogs-bucket.oss-cn-beijing.aliyuncs.com/images/20200702120517.png) 下面我需要做的是使用scope結合策略授權來分別限制TestController和WeatherForecastController的訪問許可權。 修改Startup: ```csharp 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的程式碼很簡單: ![image-20200701224046637](https://xhznl-blogs-bucket.oss-cn-beijing.aliyuncs.com/images/20200702120521.png) 因為修改了scope名稱,需要把Idp中的scope名稱也改一下: ``` public static IEnumerable ApiScopes => new ApiScope[] { new ApiScope("api1.weather.scope"), new ApiScope("api1.test.scope"), //new ApiScope("scope2"), }; public static IEnumerable 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 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: ![image-20200701225242813](https://xhznl-blogs-bucket.oss-cn-beijing.aliyuncs.com/images/20200702120538.png) 訪問weatherforecast介面,正常響應200。 ![image-20200701225430395](https://xhznl-blogs-bucket.oss-cn-beijing.aliyuncs.com/images/20200702120541.png) 再訪問test,得到403錯誤: ![image-20200701225508071](https://xhznl-blogs-bucket.oss-cn-beijing.aliyuncs.com/images/20200702120544.png) 接下來修改一下Idp的客戶端定義,新增api1.test.scope: `AllowedScopes = { "api1.weather.scope", "api1.test.scope" }` 修改Idp後一定要重新獲取token,jwt就是這樣,一旦生成就無法改變。 ![image-20200701230022811](https://xhznl-blogs-bucket.oss-cn-beijing.aliyuncs.com/images/20200702120548.png) 拿到新的token後訪問test和weatherforecast,這時候就都可以正常響應了。 ![image-20200701230107290](https://xhznl-blogs-bucket.oss-cn-beijing.aliyuncs.com/images/20200702120551.png) ![image-20200701230209695](https://xhznl-blogs-bucket.oss-cn-beijing.aliyuncs.com/images/20200702120554.png) # 總結 以上使用IdentityServer4搭建了一個鑑權中心,保護API資源,並使用ApiScope配合策略授權完成了一個簡單的許可權控制。IdentityServer4的玩法非常多,知識點也很多。強烈推薦B站的@[solenovex](https://space.bilibili.com/361469957) 楊老師的視訊,地址:https://www.bilibili.com/video/BV16b411k7yM 多看幾遍,會有收穫。。。 需要程式碼的點這裡:https://github.com/xiajingren/IdentityServer4-4.x-Sco