什么是IdentityServer4
官方解釋:IdentityServer4是基於ASP.NET Core實現的認證和授權框架,是對OpenID Connect和OAuth 2.0協議的實現。
通俗來講,就是服務端對需要認證授權的資源(客戶端請求資源)在外層使用IdentityServer4框架進行封裝加殼,用戶只能通過獲取IdentityServer4頒發的Token令牌才能進行資源訪問。
下面開始進入正題,如何快速搭建實現API接口鑒權。
准備:1.下載准備NetCore sdk環境
2.本文開發環境為VS2019,部分代碼可能和之前的版本不同。
第一步,新建權限認證服務項目,本文以Net Core API項目模板為例(也可以選擇其他模板)

第二步,添加IdentityServer4 Nuget程序包。不同版本依賴的NetCoe sdk環境不同,需手動選擇合適版本。

這里提醒一下,有些同學的系統可能添加Nuget程序包時,發現無法找到程序包。我們這里找出了解決方法,點擊Nuget程序包添加頁面的右上角設置按鈕,看到如下頁面,手動添加如下的nuget.org,然后重新搜索即可。

第三步,添加IdentityServer4配置管理類。本文以用戶密碼授權模式為例。
1 public class Config 2 { 3 /// <summary> 4 /// 定義資源范圍 5 /// </summary> 6 public static IEnumerable<ApiResource> GetApiResources() 7 { 8 return new List<ApiResource> 9 { 10 new ApiResource("api1", "我的第一個API") 11 }; 12 } 13 14 /// <summary> 15 /// 定義訪問的資源客戶端 16 /// </summary> 17 /// <returns></returns> 18 public static IEnumerable<Client> GetClients() 19 { 20 return new List<Client> 21 { 22 new Client{ 23 ClientId="client",//定義客戶端ID 24 ClientSecrets= 25 { 26 new Secret("secret".Sha256())//定義客戶端秘鑰 27 }, 28 AllowedGrantTypes=GrantTypes.ResourceOwnerPassword,//授權方式為用戶密碼模式授權,類型可參考GrantTypes枚舉 29 AllowedScopes={ "api1"}//允許客戶端訪問的范圍 30 31 } 32 }; 33 } 34 35 /// <summary> 36 /// 這個方法是來規范tooken生成的規則和方法的。一般不進行設置,直接采用默認的即可。 37 /// </summary> 38 /// <returns></returns> 39 public static IEnumerable<IdentityResource> GetIdentityResources() 40 { 41 return new IdentityResource[] 42 { 43 new IdentityResources.OpenId() 44 }; 45 } 46 }
第四步,Startup啟動類中注冊服務中間件
1 // This method gets called by the runtime. Use this method to add services to the container. 2 public void ConfigureServices(IServiceCollection services) 3 { 4 services.AddIdentityServer()//注冊服務 5 .AddDeveloperSigningCredential() 6 .AddInMemoryApiResources(Config.GetApiResources())//配置類定義的授權范圍 7 .AddInMemoryClients(Config.GetClients())//配置類定義的授權客戶端 8 .AddTestUsers(new List<TestUser> { new TestUser { Username = "Admin", Password = "123456", SubjectId = "001", IsActive = true } });//模擬測試用戶,這里偷懶了,用戶可以單獨管理,最好不要直接在這里New 9 services.AddControllers(); 10 } 11 12 // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. 13 public void Configure(IApplicationBuilder app, IWebHostEnvironment env) 14 { 15 if (env.IsDevelopment()) 16 { 17 app.UseDeveloperExceptionPage(); 18 } 19 20 app.UseIdentityServer();//添加中間件 21 22 app.UseHttpsRedirection(); 23 24 app.UseRouting(); 25 26 app.UseAuthorization(); 27 28 app.UseEndpoints(endpoints => 29 { 30 endpoints.MapControllers(); 31 }); 32 }
應用程序默認的端口號有兩種:1.http://localhost:5000 2.https://localhost:5001.
到這里,Identityserver4鑒權服務已經簡單搭建完成。我們直接在VS中啟動項目。並在端口號后面加上/.well-known/openid-configuration,出現如下頁面則表示配置成功。

第五步,PostMan模擬請求獲取token(當然這一步非必須,對postman感興趣的同學可以試一試)
我們都知道IdentityServer4需要客戶端先訪問鑒權服務獲取token令牌,才能進一步訪問加權的服務器資源。我們這里先通過PostMan模擬客戶端請求,獲取Token。(postman工具大家可以網上下載,也可以使用谷歌自帶的postman插件)
1.使用postman請求token時,有個地方需要注意下:
很多同學在使用https請求時,即請求https://localhost:5001,會發現無法成功。因為postman默認把SSL證書認證打開了,我們可以手動關閉掉。找到postman頁面右上方的小扳手圖標,進入設置頁面找到ssl關掉即可。當然同學們直接使用http://localhost:5000請求就無需設置SSL.

2.請求參數
這里的參數value就是我們在鑒權服務配置類設置的client和TestUser信息。
Grant_Type為授權類型,本文我們使用的是用戶密碼模式,所以這里填password.

這里我們看到,我們已成功模擬請求獲取了Token。大功告成,鑒權服務已驗證可用,我們趕緊去發布部署吧。
第六步,鑒權服務發布部署。
.Net Core發布模式有三種:
1.框架依賴+可移植
2.框架依賴+運行時環境(帶可執行程序exe)
3.獨立部署
簡單來說,框架依賴模式發布的程序包,都需要部署環境自帶.net core等運行環境;而獨立部署則不需要考慮,發布包已經包含了運行環境,直接部署即可。
下面本文以框架依賴+可移植發布,做簡單介紹。

發布完成后,我們會在發布路徑中看到程序dll.我們找到發布路徑,通過CMD命令窗口:dotnet xxx.dll可直接啟動。

如上,則表示啟動成功。(如果其他發布模式,直接雙擊發布包中可執行exe文件即可啟動)
鑒權服務部署完成后,我們API接口如何使用呢,下面開始正式介紹。
第一步:新建Web Api項目

添加Nuget程序包

第二步:配置啟動類
1 public void ConfigureServices(IServiceCollection services) 2 { 3 //注冊服務 4 services.AddAuthentication("Bearer") 5 .AddIdentityServerAuthentication(x => 6 { 7 x.Authority = "http://localhost:5000";//鑒權服務地址 8 x.RequireHttpsMetadata = false; 9 x.ApiName = "api1";//鑒權范圍 10 }); 11 services.AddControllers(); 12 } 13 14 public void Configure(IApplicationBuilder app, IWebHostEnvironment env) 15 { 16 if (env.IsDevelopment()) 17 { 18 app.UseDeveloperExceptionPage(); 19 } 20 app.UseAuthentication();//添加鑒權認證 21 app.UseHttpsRedirection(); 22 app.UseRouting();
app.UseAuthorization(); 23 app.UseEndpoints(endpoints => 24 { 25 endpoints.MapControllers(); 26 }); 27 }
應用程序默認的端口號有兩種:1.http://localhost:5000 2.https://localhost:5001.為了避免端口號沖突被占用,我們可以在Program類中修改應用程序啟動端口號。
1 public static IHostBuilder CreateHostBuilder(string[] args) =>
2 Host.CreateDefaultBuilder(args)
3
4 .ConfigureWebHostDefaults(webBuilder =>
5 { 6 webBuilder.UseUrls("http://*:5555");//設置啟動端口號 7 webBuilder.UseStartup<Startup>(); 8 });
第三步:創建API DEMO
1 [Route("api/[controller]")] 2 [ApiController] 3 public class TestController : ControllerBase 4 { 5 // GET: api/Test 6 /// <summary> 7 /// 方法加權 8 /// </summary> 9 /// <returns></returns> 10 [Authorize] 11 [HttpGet] 12 public IEnumerable<string> Get() 13 { 14 return new string[] { "value1", "value2" }; 15 } 16 17 /// <summary> 18 /// 方法未加權 可直接訪問 19 /// </summary> 20 /// <param name="id"></param> 21 /// <returns></returns> 22 // GET: api/Test/5 23 [HttpGet("{id}", Name = "Get")] 24 public string Get(int id) 25 { 26 return "value"; 27 } 28 29 /// <summary> 30 /// 開放獲取token API 接口 31 /// </summary> 32 /// <returns></returns> 33 [HttpGet("GetToken")] 34 public async Task<string> GetToken() 35 { 36 var client = new HttpClient(); 37 var tokenResponse = await client.RequestPasswordTokenAsync(new PasswordTokenRequest 38 { 39 Address = "http://localhost:5000/connect/token", 40 ClientId = "client", 41 ClientSecret = "secret", 42 Scope = "api1", 43 UserName = "Admin", 44 Password = "123456", 45 }); 46 47 if (tokenResponse.IsError) 48 { 49 return tokenResponse.Error; 50 } 51 52 return tokenResponse.AccessToken; 53 54 } 55 }
1.接口方法上加上:
[Authorize]
相當於對接口加權,只有被授權的用戶才能訪問(即獲取token的用戶)。此時上文中接口api/Test由於被加權,請求時會報錯;但是api/Test/1接口未加權,仍可正常請求。
那么我們如何才能訪問被加權的接口呢???Go Next
2.我們這里開放了獲取Token的接口GetToken(類似於上文中通過PostMan獲取Token)
訪問被加權的API接口,我們這里需要先請求獲取Token,然后請求加權接口時帶上token參數。

3.請求加權接口

請求加權接口時帶上Token,接口請求成功!
OK,關於如何快速開發和調試基於IdentityServer4框架的API接口鑒權服務,至此我們已介紹完畢。
小弟不才,本文中有考慮不周全或錯誤的地方,歡迎大家指正。
(如果有的同學想通過IIS部署API應用程序,這里有個地方需要注意下,需要在IIS(功能視圖——模塊)中添加AspNetCoreModule模塊。具體原因本文就不在這里介紹了。)
