本教程的目的在於創造盡可能簡單的identityserver安裝作為一個oauth2授權服務器。這應該能夠讓你了解一些基本功能和配置選項(完整的源代碼可以發現在這里)。在后面的文檔中會介紹更多的高級功能。本教程包括:
-
創建一個自托管identityserver
-
設置為使用一個應用程序的帳戶以及用戶對通信應用的客戶服務代表
-
注冊一個API
-
請求訪問令牌
-
調用API
-
驗證一個訪問令牌
創建一個授權服務器(IdentityServer3)
創建一個控制台應用程序,並且在程序包管理器控制台中輸入
install-package identityserver3
注冊API
API作為請求范圍,您需要注冊所有您希望能夠請求訪問令牌的所有API,然后我們創建一個類用於返回在這個作用范圍內所有的API
using IdentityServer3.Core.Models; static class Scopes { public static List<Scope> Get() { return new List<Scope> { new Scope { Name = "api1" } }; } }
注冊客戶端
現在我們要注冊一個客戶端。這個客戶將能夠申請api1 scope的資源。對於我們的第一次迭代,將有沒有人參與,客戶端將簡單地要求代表自己的令牌(認為機器與機器通信)。
對於這個客戶端,我們配置以下的東西:
-
顯示名稱和編號(唯一的名稱)
-
客戶端密鑰
-
客戶端憑據
-
使用所謂的引用標記。參考標記不需要簽名證書。
-
允許訪問的作用域("api1")
using IdentityServer3.Core.Models; static class Clients { public static List<Client> Get() { return new List<Client> { // no human involved new Client { ClientName = "Silicon-only Client", ClientId = "silicon", Enabled = true, AccessTokenType = AccessTokenType.Reference, Flow = Flows.ClientCredentials, ClientSecrets = new List<Secret> { new Secret("F621F470-9731-4A25-80EF-67A6F7C5F4B8".Sha256()) }, AllowedScopes = new List<string> { "api1" } } }; } }
配置 IdentityServer
identityserver作為OWIN的中間件的實現,它是通過UseIdentityServer擴展方法在Startup類中配置。
下面的代碼配置一個使用我們之前定義的客戶端個作用域的授權服務器,稍后我們會添加用戶進來。
using Owin; using System.Collections.Generic; using IdentityServer3.Core.Configuration; using IdentityServer3.Core.Services.InMemory; namespace IdSrv { class Startup { public void Configuration(IAppBuilder app) { var options = new IdentityServerOptions { Factory = new IdentityServerServiceFactory() .UseInMemoryClients(Clients.Get()) .UseInMemoryScopes(Scopes.Get()) .UseInMemoryUsers(new List<InMemoryUser>()), RequireSsl = false }; app.UseIdentityServer(options); } } }
添加日志記錄
因為我們在一個控制台中運行,它是非常方便的日志輸出直接到控制台窗口。Serilog是一個很好的日志庫
install-package serilog
install-package serilog.sinks.literate
托管 IdentityServer
最后一步是托管identityserver。在程序包管理控制台添加OWIN
install-package Microsoft.Owin.SelfHost
在Program.cs文件中添加一下代碼:
// logging Log.Logger = new LoggerConfiguration() .WriteTo .LiterateConsole(outputTemplate: "{Timestamp:HH:MM} [{Level}] ({Name:l}){NewLine} {Message}{NewLine}{Exception}") .CreateLogger(); // hosting identityserver using (WebApp.Start<Startup>("http://localhost:5000")) { Console.WriteLine("server running..."); Console.ReadLine(); }
然后當你啟動控制台程序的時候你會看到控制台輸出“server running...”
添加一個 API
在這一部分中我們將添加一個簡單的Web API,我們只需要從identityserver設置一個訪問令牌。
創建API應用程序
在的解決方案中添加一個新的ASP.NET Web API 應用程序,選擇空模板
添加必要的NuGet包:
install-package Microsoft.Owin.Host.SystemWeb install-package Microsoft.AspNet.WebApi.Owin install-package IdentityServer3.AccessTokenValidation
添加Controller
添加這個簡單的測試控制器:
[Route("test")] public class TestController : ApiController { public IHttpActionResult Get() { var caller = User as ClaimsPrincipal; return Json(new { message = "OK computer", client = caller.FindFirst("client_id").Value }); } }
控制器上的用戶屬性讓您從訪問令牌中訪問該請求的權限。
添加Startup
添加以下Startup.cs為建立Web API和identityserver配置信任啟動類
using IdentityServer3.AccessTokenValidation; public void Configuration(IAppBuilder app) { // accept access tokens from identityserver and require a scope of 'api1' app.UseIdentityServerBearerTokenAuthentication(new IdentityServerBearerTokenAuthenticationOptions { Authority = "http://localhost:5000", ValidationMode = ValidationMode.ValidationEndpoint, RequiredScopes = new[] { "api1" } }); // configure web api var config = new HttpConfiguration(); config.MapHttpAttributeRoutes(); // require authentication for all controllers config.Filters.Add(new AuthorizeAttribute()); app.UseWebApi(config); }
試着打開瀏覽器,訪問測試控制器-你應該看到一個401,因為必要的訪問令牌丟失。
添加Console客戶端
在下一部分中,我們將添加一個簡單的控制台客戶端,該客戶端將請求訪問令牌,並使用該接口進行身份驗證。
首先添加一個新的控制台項目並安裝一oauth2客戶端需要的NuGet包:
install-package IdentityModel
第一段代碼 獲取客戶端Token使用客戶端證書:
using IdentityModel.Client; static TokenResponse GetClientToken() { var client = new TokenClient( "http://localhost:5000/connect/token", "silicon", "F621F470-9731-4A25-80EF-67A6F7C5F4B8"); return client.RequestClientCredentialsAsync("api1").Result; }
第二段代碼 使用訪問令牌調用API:
static void CallApi(TokenResponse response) { var client = new HttpClient(); client.SetBearerToken(response.AccessToken); Console.WriteLine(client.GetStringAsync("http://localhost:14869/test").Result); }
如果你啟動控制台程序,你應該看到{"message":"OK computer","client":"silicon"}在您的控制台。
添加一個用戶
到目前為止,客戶端請求一個訪問令牌本身,沒有用戶參與。讓我們介紹一個人。
添加一個獲取用戶的服務
用戶服務管理用戶-對於這個示例,我們將使用簡單的內存用戶服務。首先我們需要定義一些用戶:
using IdentityServer3.Core.Services.InMemory; static class Users { public static List<InMemoryUser> Get() { return new List<InMemoryUser> { new InMemoryUser { Username = "bob", Password = "secret", Subject = "1" }, new InMemoryUser { Username = "alice", Password = "secret", Subject = "2" } }; } }
用戶名和密碼被用來驗證用戶,subject是該用戶將被嵌入到訪問令牌的唯一標識符。使用Users.Get()替換Startup.cs中的new List<InMemoryUser>()
添加一個客戶端
接下來,我們將添加一個客戶定義,使用流稱為資源所有者的密碼證書授予。此流程允許客戶端將用戶的用戶名和密碼發送到令牌服務,並在返回中獲取訪問令牌。
using IdentityServer3.Core.Models; using System.Collections.Generic; namespace IdSrv { static class Clients { public static List<Client> Get() { return new List<Client> { // no human involved new Client { ClientName = "Silicon-only Client", ClientId = "silicon", Enabled = true, AccessTokenType = AccessTokenType.Reference, Flow = Flows.ClientCredentials, ClientSecrets = new List<Secret> { new Secret("F621F470-9731-4A25-80EF-67A6F7C5F4B8".Sha256()) }, AllowedScopes = new List<string> { "api1" } }, // human is involved new Client { ClientName = "Silicon on behalf of Carbon Client", ClientId = "carbon", Enabled = true, AccessTokenType = AccessTokenType.Reference, Flow = Flows.ResourceOwner, ClientSecrets = new List<Secret> { new Secret("21B5F798-BE55-42BC-8AA8-0025B903DC3B".Sha256()) }, AllowedScopes = new List<string> { "api1" } } }; } } }
更新API
當涉及到人的時候,訪問令牌將包含子請求以唯一標識用戶。讓我們對API的控制器做小的修改,:
[Route("test")] public class TestController : ApiController { public IHttpActionResult Get() { var caller = User as ClaimsPrincipal; var subjectClaim = caller.FindFirst("sub"); if (subjectClaim != null) { return Json(new { message = "OK user", client = caller.FindFirst("client_id").Value, subject = subjectClaim.Value }); } else { return Json(new { message = "OK computer", client = caller.FindFirst("client_id").Value }); } } }
更新客戶端
下一個向客戶端添加一個新的方法,代表用戶請求訪問令牌:
static TokenResponse GetUserToken() { var client = new TokenClient( "http://localhost:5000/connect/token", "carbon", "21B5F798-BE55-42BC-8AA8-0025B903DC3B"); return client.RequestResourceOwnerPasswordAsync("bob", "secret", "api1").Result; }
現在再次嘗試獲取token看看API返回的信息吧