IdentityServer4是一套身份授權以及訪問控制的解決方案,專注於幫助使用.Net 技術的公司為現代應用程序建立標識和訪問控制解決方案,包括單點登錄、身份管理、授權和API安全。
下面我將具體介紹如何在.Net Core中實現OAuth授權,從最簡單的授權模式開始,在上一篇對OAuth2.0的詳細描述中,在客戶端模式中,我們說它在嚴格意義上講是不存在授權的問題,我們再來看下它的授權流程:

客戶端在向授權服務器申請令牌后,授權服務器直接將令牌返回給了客戶端,這個過程不需要其他角色的任何操作,只是客戶端和授權服務器的交互。我們結合具體的示例來進一步了解這個過程。
操作系統:Mac OS
開發工具:VS Code
調試工具:Postman
開發框架:.Net Core 2.0
在具體示例實現之前,先說一下在VS Code我們會用到的插件,以方便我們的開發,畢竟VS Code沒有我們宇宙第一IDE-Visual Studio那么強大,但是也是目前為止最好用的編輯器,它提供了各式各樣的插件,幾乎滿足我們所有的開發需求。在這里我們用到的一個插件叫做Nuget Package Manager,這個插件可以方便我們使用快捷鍵對Nuget包進行管理。
接下來我們首先創建一個授權服務器的項目,打開VS Code使用控制台,創建一個WebApi的項目,使用命令:
dotnet new WebApi -n IdentityServer4.Server
創建完成后,我們可以啟動查看我們的項目,run一下:

這樣我們的項目是可以運行成功的,下面我們進行添加IdentitServer4包的引用,在我們安裝了Nuget Package Manager后,我們可以快速的使用快捷鍵,在Mac系統中使用command+p,然后輸入">",然后輸入Nuget... (注意一定要切換到當前的項目下)會出現以下提示:

選擇第一個選項添加Package:

按回車,選擇IdentityServer4最新版本的安裝,這里是2.1.3,安裝完后,我們在StartUp中添加IdentityServer4的引用,並使用AddIdentityServer()方法在依賴注入系統中注冊IdentityServer,當然這里我們也可以等到添加完配置類后再進行操作。我們先添加一個配置類,叫做Config.cs,首先定義一個管道(Scope),指定我們所保護Api資源,該方法返回一個ApiResources集合,具體代碼如下:new ApiResource("api","UsersApi"),第一個參數為Api的名稱,第二個參數為顯示的名稱:

下一步進行客戶端注冊,定義給客戶端可以返回的資源,即允許哪個Scope定義,代碼如下:

下面我們將該配置注入到系統中,

AddDeveloperSigningCredential(),是一種RSA證書加密方式,它會生成一個tempkey.rsa證書文件,項目每次啟動時,會檢查項目根目錄是否存在該證書文件,若不存在,則會生成該文件,否則會繼續使用該證書文件。后面依次將ApiResources和Clients添加到內存中。
下一步是配置IdentityServer4的管道,在Configure里面添加,app.UseIdentityServer(),在這里我們用不到mvc,將app.UseMvc()注釋掉即可。下面我們運行我們的項目,當然直接訪問http://localhost:5000是看不到任何東西的,在這里我們使用一個固定的地址http://localhost:5000/.well-known/openid-configuration,可以查看IdentityServer4的配置信息,運行格式化后內容如下:
{
"issuer": "http://localhost:5000",
"jwks_uri": "http://localhost:5000/.well-known/openid-configuration/jwks",
"authorization_endpoint": "http://localhost:5000/connect/authorize",
"token_endpoint": "http://localhost:5000/connect/token",
"userinfo_endpoint": "http://localhost:5000/connect/userinfo",
"end_session_endpoint": "http://localhost:5000/connect/endsession",
"check_session_iframe": "http://localhost:5000/connect/checksession",
"revocation_endpoint": "http://localhost:5000/connect/revocation",
"introspection_endpoint": "http://localhost:5000/connect/introspect",
"frontchannel_logout_supported": true,
"frontchannel_logout_session_supported": true,
"backchannel_logout_supported": true,
"backchannel_logout_session_supported": true,
"scopes_supported": [
"api",
"offline_access"
],
"claims_supported": [],
"grant_types_supported": [
"authorization_code",
"client_credentials",
"refresh_token",
"implicit"
],
"response_types_supported": [
"code",
"token",
"id_token",
"id_token token",
"code id_token",
"code token",
"code id_token token"
],
"response_modes_supported": [
"form_post",
"query",
"fragment"
],
"token_endpoint_auth_methods_supported": [
"client_secret_basic",
"client_secret_post"
],
"subject_types_supported": [
"public"
],
"id_token_signing_alg_values_supported": [
"RS256"
],
"code_challenge_methods_supported": [
"plain",
"S256"
]
}
上面指定的3個參數都是客戶端模式需要指定的參數,下面就是我們請求到的access token。接着我們創建一個Api,去調用我們的授權服務器進行授權,重復上面的步驟,創建一個WebApi項目,不再贅述,直接去配置調用授權服務器,這個項目只是一個Api的項目,不需要完整的IdentityServer4的引用,只引用一個IdentityServer4.AccessTokenValidation包就可以了。在授權服務器中我們已經占用5000端口,所以在這個項目中我們指定為5001端口,在Program.cs里面指定5001端口,添加.UseUrls("http://localhost:5001"):

在ValuesController里面添加 Microsoft.AspNetCore.Authorization的引用,並添加[Authorize]標簽,即要求該Controller要通過授權才可以訪問,

下面我們在StartUp.cs里面去配置授權服務:

接着我們分別啟動授權服務器和Api,我們運行Api,訪問:http://localhost:5001/api/values ,訪問結果如下:
提示Http Error 401 ,在狀態碼中401即未授權,我們把它拿到Postman中運行:

我們在Headers中指定Authorization 模式為Bearer,狀態401 未授權,這個結果也是我們意料中的結果了,因為我們並未獲取到令牌,我們繼續使用Postman模擬獲取到access token,

我們將獲取到的access_token放到Api的Headers里面:

注意令牌與Bearer中間加空格,接下來繼續請求:

這樣我們就拿到了Api里面要獲取的值了,到這里我們使用Postman驗證了我們的結果,下面我們再創建一個第三方應用,去請求我們的Api資源,繼續了解我們的授權流程,客戶端我使用控制台程序進行測試。
我們繼續使用命令行創建第三方應用,名稱為ThirdPartyApplication,IdentityServer4有一個專門專門為客戶端程序用的Nuget包,叫做IdentityModel,我們還是通過快捷鍵添加Nuget Package,下面直接上代碼,必要說明會在代碼中直接注釋:
using System;
using System.Net.Http;
using IdentityModel;
using IdentityModel.Client;
namespace ThirdPartyApplication
{
class Program
{
static void Main(string[] args)
{
//請求授權服務器
var diso=DiscoveryClient.GetAsync("http://localhost:5000").Result;
if(diso.IsError)
{
Console.WriteLine(diso.Error);
}
//授權服務器根據客戶端發來的請求返回令牌
var tokenClient=new TokenClient(diso.TokenEndpoint,"Client","secret");
var tokenResponse=tokenClient.RequestClientCredentialsAsync("api").Result;
if(tokenResponse.IsError)
{
Console.WriteLine(tokenResponse.Error);
}
//如果成功,則打印輸出返回的令牌信息
else
{
Console.WriteLine(tokenResponse.Json);
}
//創建HttpClient對象
var httpClient=new HttpClient();
//設置Authorization的Value值
httpClient.SetBearerToken(tokenResponse.AccessToken);
//根據授權服務器返回的令牌信息請求Api資源
var response= httpClient.GetAsync("http://localhost:5001/api/values").Result;
//如果返回結果為成功,輸出Api資源的結果
if(response.IsSuccessStatusCode)
{
Console.WriteLine(response.Content.ReadAsStringAsync().Result);
}
}
}
}
下面輸出結果:

以上就是我們整個OAuth2.0授權模式中完整的客戶端授權模式了,以上流程簡化如下:

后面文章會繼續講解其他幾種授權模式的使用。各位,晚安。

掃描二維碼關注我的公眾號,共同學習,共同進步!
