github:https://github.com/IdentityServer/IdentityServer3/
documentation:https://identityserver.github.io/Documentation/
samples https://github.com/IdentityServer/IdentityServer3.Samples/
OAuth 2.0定義的四種授權方式。
- 授權碼模式(authorization code)
- 簡化模式(implicit)
- 密碼模式(resource owner password credentials)
- 客戶端模式(client credentials)
雖然之前已經基於 Katana 實現了 OAuth 2.0 的相關模式,后面發現 IdentityServer 要相對完善一些,IdentityServer 是屬於 .NET Foundation 的一個子項目,IdentityServer3 是基於 Katana 開發的 OpenID Connect/OAuth 2.0 服務框架,雖然 Katana 微軟已經不維護了,現已經屬於 ASP.NET Core 的一部分,相應對應 ASP.NET Core 的版本是 IdentityServer4 ,還處於 Beta 版本;不過 IdentityServer3 的擴展 IdentityServer3.Integration.AspNetCore 支持 ASP.NET Core , 個人覺得 ASP.NET Core 設計很好,但是現在還不成熟,可能還需要等一段時間,所以選擇了 IdentityServer3 。
授權角色
資源擁有者(resource owner):能授權訪問受保護資源的一個實體,如新浪微博用戶 irving;
資源服務器(resource server):存儲受保護資源,客戶端通過 access token 請求資源,資源服務器響應受保護資源給客戶端;存儲着用戶 irving 的微博等信息。
授權服務器(authorization server):成功驗證資源擁有者並獲取授權之后,授權服務器頒發授權令牌(Access Token)給客戶端。
客戶端(client):如新浪微博第三方應用,也可以是它自己的官方應用;其本身不存儲資源,而是資源擁有者授權通過后,使用它的授權(授權令牌)訪問受保護資源,然后客戶端把相應的數據展示出來。“客戶端”術語不代表任何特定實現(如應用運行在一台服務器、桌面、手機或其他設備)。
Client Credentials Grant 模式只需要關注 授權服務器,資源服務器,客戶端三個角色 具體:https://identityserver.github.io/Documentation/docsv2/overview/terminology.html
授權服務器
Install-Package IdentityServer3
服務配置
public class Startup { /// <summary> /// 配置idsv授權服務 /// </summary> /// <param name="app"></param> public void Configuration(IAppBuilder app) { var opts = new IdentityServerOptions { SiteName = "Embedded OAuth2 Service", EnableWelcomePage = true, Factory = new IdentityServerServiceFactory() .UseInMemoryClients(Clients.Get()) .UseInMemoryScopes(Scopes.Get()) .UseInMemoryUsers(new List<InMemoryUser>()), //.UseInMemoryUsers(Users.Get()) RequireSsl = false, //SigningCertificate = new X509Certificate2(string.Format(@"{0}\bin\identityServer\idsrv3test.pfx", AppDomain.CurrentDomain.BaseDirectory), "idsrv3test") }; app.UseIdentityServer(opts); /* //自定義路由 app.Map("/identity", idsrvApp => { idsrvApp.UseIdentityServer(opts); }); */ } }
客戶端
public class Clients { public static List<Client> Get() { return new List<Client> { new Client { ClientName = "App接口服務", ClientId = "app_test_id", Enabled = true, AccessTokenType = AccessTokenType.Reference, Flow = Flows.ClientCredentials, ClientSecrets = new List<Secret> { new Secret("F621F470-9731-4A25-80EF-67A6F7C5F4B8".Sha256()) }, AllowedScopes = new List<string> { "user", "order" } } }; }
作用域
public class Scopes { public static List<Scope> Get() { return new List<Scope> { new Scope { Name = "user" }, new Scope { Name = "order" } }; } }
用戶(客戶端模式不需要用戶參與所以沒有用戶數據)
完成上述工作,服務端基礎服務就完成了,訪問 /.well-known/openid-configuration ,可以獲得服務的配置信息,暫且部署到 http://192.168.210.165 服務器上,配置信息如下(官方也就有服務端的示例: https://github.com/IdentityServer/demo.identityserver.io)
{ "issuer": "http://192.168.210.165", "jwks_uri": "http://192.168.210.165/.well-known/jwks", "authorization_endpoint": "http://192.168.210.165/connect/authorize", "token_endpoint": "http://192.168.210.165/connect/token", "userinfo_endpoint": "http://192.168.210.165/connect/userinfo", "end_session_endpoint": "http://192.168.210.165/connect/endsession", "check_session_iframe": "http://192.168.210.165/connect/checksession", "revocation_endpoint": "http://192.168.210.165/connect/revocation", "introspection_endpoint": "http://192.168.210.165/connect/introspect", "frontchannel_logout_supported": true, "frontchannel_logout_session_supported": true, "scopes_supported": [ "user", "order" ], "claims_supported": [ ], "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" ], "grant_types_supported": [ "authorization_code", "client_credentials", "password", "refresh_token", "implicit" ], "subject_types_supported": [ "public" ], "id_token_signing_alg_values_supported": [ "RS256" ], "code_challenge_methods_supported": [ "plain", "S256" ], "token_endpoint_auth_methods_supported": [ "client_secret_post", "client_secret_basic" ] }
注意:寄宿到 IIS 中需要 WebConfig 配置中需配置:
<system.webServer> <modules runAllManagedModulesForAllRequests="true"> </modules> </system.webServer
資源服務器
Install-Package Microsoft.Owin.Host.SystemWeb
Install-Package Microsoft.AspNet.WebApi.Owin
Install-Package IdentityServer3.AccessTokenValidation
服務配置
public class Startup { /// <summary> /// 配置授權服務 /// </summary> /// <param name="app"></param> public void Configuration(IAppBuilder app) { app.UseIdentityServerBearerTokenAuthentication(new IdentityServerBearerTokenAuthenticationOptions { Authority = "http://192.168.210.165", ValidationMode = ValidationMode.ValidationEndpoint, EnableValidationResultCache = true, ValidationResultCacheDuration = TimeSpan.FromMinutes(5), RequiredScopes = new[] { "user", "order" } }); } }
控制器
[Route("api/v1/values")] public class ValuesController : ApiController { public IHttpActionResult Get() { var caller = User as ClaimsPrincipal; return Json(new { message = "OK computer", client = caller.FindFirst("client_id").Value }); } }
OK,完成,由於是一台測試服務器,部署到 http://192.168.210.165:88/ 端口。
客戶端
Install-Package serilog
Install-Package serilog.sinks.literate
Install-Package IdentityModel
class Program { static void Main(string[] args) { var log = new LoggerConfiguration() .WriteTo .LiterateConsole(outputTemplate: "{Timestamp:HH:mm} [{Level}] ({Name:l}){NewLine} {Message}{NewLine}{Exception}") .CreateLogger(); var token = new TokenClient( "http://192.168.210.165/connect/token", "app_test_id", "F621F470-9731-4A25-80EF-67A6F7C5F4B8"); var response = token.RequestClientCredentialsAsync("user").Result; var client = new HttpClient(); client.SetBearerToken(response.AccessToken); log.Information(client.GetStringAsync("http://192.168.210.165:88/api/v1/values").Result); Console.ReadKey(); } }
測試
順便使用Fiddler抓了一下包
POST http://192.168.210.165/connect/token HTTP/1.1 Accept: application/json Authorization: Basic YXBwX3Rlc3RfaWQ6RjYyMUY0NzAtOTczMS00QTI1LTgwRUYtNjdBNkY3QzVGNEI4 Content-Type: application/x-www-form-urlencoded Host: 192.168.210.165 Content-Length: 40 Expect: 100-continue Connection: Keep-Alive grant_type=client_credentials&scope=user
GET http://192.168.210.165:88/api/v1/values HTTP/1.1
Authorization: Bearer 9f82476751e1f8b93f1ea6df7de83b51
Host: 192.168.210.165:88
個人比較喜歡使用 Flurl.Http 這個類庫,可以擴展一下客戶端,擴展的方法現在還有一些問題,就不貼代碼了。
其他細節:
- 生產環境最好加證書與HTTPS
- 客戶端,作用域,持久化到DB(有EF的擴展 https://github.com/IdentityServer/IdentityServer3.EntityFramework )
- 票據的持久化與自定義驗證
REFER:
Announcing IdentityServer for ASP.NET 5 and .NET Core
http://leastprivilege.com/2016/01/11/announcing-identityserver-for-asp-net-5-and-net-core/
OAuth2 Implicit Flow with Angular and ASP.NET Core 1.0 IdentityServer
http://damienbod.com/2015/11/08/oauth2-implicit-flow-with-angular-and-asp-net-5-identity-server
理解OAuth 2.0
http://www.ruanyifeng.com/blog/2014/05/oauth_2_0.html


