本教程的目的在於創造儘量簡單的identityserver安裝做爲一個oauth2受權服務器。這應該可以讓你瞭解一些基本功能和配置選項(完整的源代碼能夠發現在這裏)。在後面的文檔中會介紹更多的高級功能。本教程包括:git
建立一個自託管identityservergithub
設置爲使用一個應用程序的賬戶以及用戶對通訊應用的客戶服務表明web
註冊一個APIapi
請求訪問令牌瀏覽器
調用API服務器
驗證一個訪問令牌app
建立一個控制檯應用程序,而且在程序包管理器控制檯中輸入ide
install-package identityserver3
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的資源。對於咱們的第一次迭代,將有沒有人蔘與,客戶端將簡單地要求表明本身的令牌(認爲機器與機器通訊)。ui
對於這個客戶端,咱們配置如下的東西:
顯示名稱和編號(惟一的名稱)
客戶端密鑰
客戶端憑據
使用所謂的引用標記。參考標記不須要簽名證書。
容許訪問的做用域("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做爲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。在程序包管理控制檯添加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...」
在這一部分中咱們將添加一個簡單的Web API,咱們只須要從identityserver設置一個訪問令牌。
在的解決方案中添加一個新的ASP.NET Web API 應用程序,選擇空模板
添加必要的NuGet包:
install-package Microsoft.Owin.Host.SystemWeb install-package Microsoft.AspNet.WebApi.Owin install-package IdentityServer3.AccessTokenValidation
添加這個簡單的測試控制器:
[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.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,由於必要的訪問令牌丟失。
在下一部分中,咱們將添加一個簡單的控制檯客戶端,該客戶端將請求訪問令牌,並使用該接口進行身份驗證。
首先添加一個新的控制檯項目並安裝一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的控制器作小的修改,:
[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返回的信息吧