關於 OAuth 2.0 的相關內容,點擊查看:ASP.NET WebApi OWIN 實現 OAuth 2.0html
OpenID 是一個去中心化的網上身份認證系統。對於支持 OpenID 的網站,用戶不須要記住像用戶名和密碼這樣的傳統驗證標記。取而代之的是,他們只須要預先在一個做爲 OpenID 身份提供者(identity provider, IdP)的網站上註冊。OpenID 是去中心化的,任何網站均可以使用 OpenID 來做爲用戶登陸的一種方式,任何網站也均可以做爲 OpenID 身份提供者。OpenID 既解決了問題而又不須要依賴於中心性的網站來確認數字身份。前端
OpenID 相關基本術語:git
最終用戶(End User):想要向某個網站代表身份的人。github
標識(Identifier):最終用戶用以標識其身份的 URL 或 XRI。web
身份提供者(Identity Provider, IdP):提供 OpenID URL 或 XRI 註冊和驗證服務的服務提供者。sql
依賴方(Relying Party, RP):想要對最終用戶的標識進行驗證的網站。數據庫
以上概念來自:https://zh.wikipedia.org/wiki/OpenIDjson
針對 .NET Core 跨平臺,微軟官方並無針對 OAuth 2.0 的實現(Microsoft.AspNetCore.Authentication.OAuth
組件,僅限客戶端),IdentityServer4 實現了 ASP.NET Core 下的 OpenID Connect 和 OAuth 2.0,IdentityServer4 也是微軟基金會成員。後端
閱讀目錄:api
OpenID 和 OAuth 的區別
客戶端模式(Client Credentials)
密碼模式(resource owner password credentials)
簡化模式-With OpenID(implicit grant type)
簡化模式-With OpenID & OAuth(JS 客戶端調用)
混合模式-With OpenID & OAuth(Hybrid Flow)
ASP.NET Core Identity and Using EntityFramework Core for configuration data
開源地址:https://github.com/yuezhongxin/IdentityServer4.Demo
簡單歸納:
OpenID:authentication(認證),用戶是誰?
OAuth:authorization(受權),用戶能作什麼?
其實,OAuth 的密碼受權模式和 OpenID 有些相似,但也不相同,好比用戶登陸落網選擇微博快捷登陸方式,大體的區別:
OAuth:用戶在微博受權頁面輸入微博的帳號和密碼,微博驗證成功以後,返回 access_token,而後落網拿到 access_token 以後,再去請求微博的用戶 API,微博受權中心驗證 access_token,若是驗證經過,則返回用戶 API 的請求數據給落網。
OpenID:落網能夠沒有用戶的任何實現,落網須要確認一個 URL 標識(能夠是多個),而後用戶登陸的時候,選擇一個 URL 進行登陸(好比微博),跳轉到微博 OpenID 登陸頁面,用戶輸入微博的帳號和密碼,微博驗證成功以後,按照用戶的選擇,返回用戶的一些信息。
能夠看到,OAuth 首先須要拿到一個受權(access_token),而後再經過這個受權,去資源服務器(具體的 API),獲取想要的一些數據,上面示例中,用戶 API 只是資源服務器的一種(能夠是視頻 API、文章 API 等等),在這個過程當中,OAuth 最重要的就是獲取受權(四種模式),獲取到受權以後,你就能夠經過這個受權,作受權範圍之類的任何事了。
而對於 OpenID 來講,受權和它沒任何關係,它只關心的是用戶,好比落網,能夠不進行用戶的任何實現(具體體現就是數據庫沒有 User 表),而後使用支持 OpenID 的服務(好比微博),經過特定的 URL 標識(能夠看做是 OpenID 標識),而後輸入提供服務的帳號和密碼,返回具體的用戶信息,對於落網來講,它關心的是用戶信息,僅此而已。
上面實際上是 OAuth 的受權,因此會有「得到如下權限」提示,若是是 OpenID 的話,「權限」應該改成「用戶信息」。
支持 OpenID 的服務列表:http://openid.net/get-an-openid/
OpenID 流程圖(來自 Using OpenID):
簡單概述:客戶端提供 ClientId 和 ClientSecret 給認證受權服務,驗證若是成功,返回 access_token,客戶端拿到 access_token,訪問 API 資源服務。
建立 ASP.NET Core 站點,Startup 配置修改以下:
public class Startup { public void ConfigureServices(IServiceCollection services) { // configure identity server with in-memory stores, keys, clients and scopes services.AddIdentityServer() .AddTemporarySigningCredential() .AddInMemoryApiResources(new List<ApiResource> { new ApiResource("api1", "My API") }) .AddInMemoryClients(new List<Client> { // client credentials client new Client { ClientId = "client", AllowedGrantTypes = GrantTypes.ClientCredentials, ClientSecrets = { new Secret("secret".Sha256()) }, AllowedScopes = { "api1" } } }); } public void Configure(IApplicationBuilder app, ILoggerFactory loggerFactory) { loggerFactory.AddConsole(LogLevel.Debug); app.UseDeveloperExceptionPage(); app.UseIdentityServer(); } }
IdentityServer4 中AddInMemory
的相關配置,都是 Mock 的(代碼配置),也能夠把這些配置存儲在數據庫中,這個後面再講。
AddInMemoryApiResources
增長的 API 資源服務(List 集合),也就此認證受權服務所管轄的 API 資源,好比上面配置的 api1,這個會在客戶端調用的時候用到,若是不一致,是不容許訪問的,另外,Clinet 中配置的AllowedScopes = { "api1" }
,表示此種受權模式容許的 API 資源集合(前提是須要添加ApiResource
)。
配置很簡單,咱們也能夠訪問http://localhost:5000/.well-known/openid-configuration
,查看具體的配置信息:
API 資源服務站點,須要添加程序包:
"IdentityServer4.AccessTokenValidation": "1.0.1"
添加一個ValuesController
:
[Route("[controller]")] [Authorize] public class ValuesController : ControllerBase { [HttpGet] public IActionResult Get() { return Content("hello world"); } }
須要添加程序包:
"IdentityModel": "2.0.0"
單元測試代碼:
[Fact] public async Task ClientCredentials_Test() { // request token var disco = await DiscoveryClient.GetAsync("http://localhost:5000"); var tokenClient = new TokenClient(disco.TokenEndpoint, "client", "secret"); var tokenResponse = await tokenClient.RequestClientCredentialsAsync("api1"); Assert.False(tokenResponse.IsError); Console.WriteLine(tokenResponse.Json); // call api var client = new HttpClient(); client.SetBearerToken(tokenResponse.AccessToken); var response = await client.GetAsync("http://localhost:5010/values"); Assert.True(response.IsSuccessStatusCode); var content = await response.Content.ReadAsStringAsync(); Console.WriteLine(content); }
很簡單,和咱們以前用 ASP.NET WebApi OWIN 實現 OAuth 2.0 同樣,只不過配置和調用簡化了不少,由於 IdentityServer4 替咱們作了不少工做。
簡單概述:客戶端提供 UserName 和 Password 給認證受權服務,驗證若是成功,返回 access_token,客戶端拿到 access_token,訪問 API 資源服務。
建立 ASP.NET Core 站點,Startup 配置修改以下:
public class Startup { public void ConfigureServices(IServiceCollection services) { // configure identity server with in-memory stores, keys, clients and scopes services.AddIdentityServer() .AddTemporarySigningCredential() .AddInMemoryApiResources(new List<ApiResource> { new ApiResource("api1", "My API") }) .AddInMemoryClients(new List<Client> { // resource owner password grant client new Client { ClientId = "ro.client", AllowedGrantTypes = GrantTypes.ResourceOwnerPassword, ClientSecrets = { new Secret("secret".Sha256()) }, AllowedScopes = { "api1" } } }) .AddTestUsers(new List<TestUser> { new TestUser { SubjectId = "1", Username = "xishuai", Password = "123" } }); } public void Configure(IApplicationBuilder app, ILoggerFactory loggerFactory) { loggerFactory.AddConsole(LogLevel.Debug); app.UseDeveloperExceptionPage(); app.UseIdentityServer(); } }
和客戶端模式不一樣的是,AllowedGrantTypes
受權模式改成了ResourceOwnerPassword
,而後增長了測試用戶(用來驗證用戶名和密碼),也能夠存儲在數據庫中。
API 資源服務站點,須要添加程序包:
"IdentityServer4.AccessTokenValidation": "1.0.1"
添加一個IdentityController
:
[Route("[controller]")] [Authorize] public class IdentityController : ControllerBase { [HttpGet] public IActionResult Get() { return new JsonResult(from c in User.Claims select new { c.Type, c.Value }); } }
須要添加程序包:
"IdentityModel": "2.0.0"
單元測試代碼:
[Fact] public async Task ResourceOwnerPassword_Test() { // request token var disco = await DiscoveryClient.GetAsync("http://localhost:5000"); var tokenClient = new TokenClient(disco.TokenEndpoint, "ro.client", "secret"); var tokenResponse = await tokenClient.RequestResourceOwnerPasswordAsync("xishuai", "123", "api1"); Assert.False(tokenResponse.IsError); Console.WriteLine(tokenResponse.Json); // call api var client = new HttpClient(); client.SetBearerToken(tokenResponse.AccessToken); var response = await client.GetAsync("http://localhost:5010/identity"); Assert.True(response.IsSuccessStatusCode); var content = await response.Content.ReadAsStringAsync(); Console.WriteLine(JArray.Parse(content)); }
簡化模式在 IdentityServer4 中的實現,就是 OpenID Connect。
簡單概述:客戶端肯定 URL(用戶認證服務),登陸在用戶認證服務,驗證成功,返回客戶端想要的用戶數據,並使此用戶爲登陸狀態,能夠在客戶端進行註銷用戶。
建立 ASP.NET Core 站點,Startup 配置修改以下:
public class Startup { public void ConfigureServices(IServiceCollection services) { // configure identity server with in-memory stores, keys, clients and scopes services.AddIdentityServer() .AddTemporarySigningCredential() .AddInMemoryIdentityResources(new List<IdentityResource> { new IdentityResources.OpenId(), new IdentityResources.Profile(), }) .AddInMemoryClients(new List<Client> { // OpenID Connect implicit flow client (MVC) new Client { ClientId = "mvc", ClientName = "MVC Client", AllowedGrantTypes = GrantTypes.Implicit, RedirectUris = { "http://localhost:5020/signin-oidc" }, PostLogoutRedirectUris = { "http://localhost:5020" }, AllowedScopes = { IdentityServerConstants.StandardScopes.OpenId, IdentityServerConstants.StandardScopes.Profile } } }) .AddTestUsers(new List<TestUser> { new TestUser { SubjectId = "1", Username = "xishuai", Password = "123", Claims = new List<Claim> { new Claim("name", "xishuai"), new Claim("website", "http://xishuai.cnblogs.com") } } }); } public void Configure(IApplicationBuilder app, ILoggerFactory loggerFactory) { loggerFactory.AddConsole(LogLevel.Debug); app.UseDeveloperExceptionPage(); app.UseIdentityServer(); } }
AddInMemoryIdentityResources
和AllowedScopes
所配置的,是客戶端容許訪問的用戶信息,具體查看:Requesting Claims using Scope Values
ClientId 很重要,必須和客戶端一一對應,因此想要使用 OpenID 認證服務的客戶端,須要向提供 OpenID 認證服務的機構,申請一個 ClientId,OpenID 認證服務會統一發放一個用戶登陸的 URL。
TestUser
中的Claims
配置,其實就是IdentityServerConstants.StandardScopes.Profile
。
另外,還有用戶登陸的一些操做代碼,這邊就不貼了,能夠查看具體的實現:ImplicitServer.Web
建立 ASP.NET Core 站點,添加程序包:
"Microsoft.AspNetCore.Authentication.Cookies": "1.0.*", "Microsoft.AspNetCore.Authentication.OpenIdConnect": "1.0.*"
Startup 配置修改以下:
public Startup(IHostingEnvironment env) { var builder = new ConfigurationBuilder() .SetBasePath(env.ContentRootPath) .AddJsonFile("appsettings.json", optional: true, reloadOnChange: true) .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true) .AddEnvironmentVariables(); Configuration = builder.Build(); } public IConfigurationRoot Configuration { get; } public void ConfigureServices(IServiceCollection services) { services.AddMvc(); } public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory) { JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear(); loggerFactory.AddConsole(Configuration.GetSection("Logging")); loggerFactory.AddDebug(); if (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); } else { app.UseExceptionHandler("/Home/Error"); } app.UseCookieAuthentication(new CookieAuthenticationOptions { AuthenticationScheme = "Cookies" }); app.UseOpenIdConnectAuthentication(new OpenIdConnectOptions { AuthenticationScheme = "oidc", SignInScheme = "Cookies", Authority = "http://localhost:5001", RequireHttpsMetadata = false, ClientId = "mvc", SaveTokens = true }); app.UseStaticFiles(); app.UseMvcWithDefaultRoute(); }
UseOpenIdConnectAuthentication
配置中的Authority
,就是 OpenID 認證服務的 URL。
添加一個HomeController
:
public class HomeController : Controller { public IActionResult Index() { return View(); } [Authorize] public IActionResult Secure() { ViewData["Message"] = "Secure page."; return View(); } public async Task Logout() { await HttpContext.Authentication.SignOutAsync("Cookies"); await HttpContext.Authentication.SignOutAsync("oidc"); } public IActionResult Error() { return View(); } }
訪問 Secure 頁面,跳轉到認證服務地址,進行帳號密碼登陸,Logout 用於用戶的註銷操做。
簡單概述:客戶端肯定 URL(用戶認證服務),登陸在用戶認證服務,驗證成功,返回客戶端想要的用戶數據 和 access_token,並使此用戶爲登陸狀態,能夠在客戶端進行註銷用戶,客戶端能夠拿到 access_token,去訪問受權範圍以內的 API 資源。
須要注意的是:由於簡化模式,因此 access_token 是做爲 URL 參數返回的。
建立 ASP.NET Core 站點,Startup 配置修改以下:
public class Startup { public void ConfigureServices(IServiceCollection services) { // configure identity server with in-memory stores, keys, clients and scopes services.AddIdentityServer() .AddTemporarySigningCredential() .AddInMemoryIdentityResources(new List<IdentityResource> { new IdentityResources.OpenId(), new IdentityResources.Profile(), }) .AddInMemoryApiResources(new List<ApiResource> { new ApiResource("api1", "My API") }) .AddInMemoryClients(new List<Client> { // OpenID Connect implicit flow client (MVC) new Client { ClientId = "js", ClientName = "JavaScript Client", AllowedGrantTypes = GrantTypes.Implicit, AllowAccessTokensViaBrowser = true, RedirectUris = { "http://localhost:5022/callback.html" }, PostLogoutRedirectUris = { "http://localhost:5022/index.html" }, AllowedCorsOrigins = { "http://localhost:5022" }, RequireConsent = false, //禁用 consent 頁面確認 https://github.com/IdentityServer/IdentityServer3/issues/863 AllowedScopes = { IdentityServerConstants.StandardScopes.OpenId, IdentityServerConstants.StandardScopes.Profile, "api1" } } }) .AddTestUsers(new List<TestUser> { new TestUser { SubjectId = "1", Username = "xishuai", Password = "123", Claims = new List<Claim> { new Claim("name", "xishuai"), new Claim("website", "http://xishuai.cnblogs.com") } } }); } public void Configure(IApplicationBuilder app, ILoggerFactory loggerFactory) { loggerFactory.AddConsole(LogLevel.Debug); app.UseDeveloperExceptionPage(); app.UseIdentityServer(); } }
由於涉及到訪問 API 資源操做,須要須要添加AddInMemoryApiResources
配置,AllowedScopes
也須要添加對應的 API 資源名稱,AllowAccessTokensViaBrowser = true
的配置的做用就是,能夠在瀏覽器地址中訪問 access_token。
更多實現代碼,點擊查看:ImplicitServerWithJS.Web
API 資源服務站點,須要添加程序包:
"IdentityServer4.AccessTokenValidation": "1.0.1", "Microsoft.AspNetCore.Cors": "1.1.0"
Startup 配置修改以下:
public Startup(IHostingEnvironment env) { var builder = new ConfigurationBuilder() .SetBasePath(env.ContentRootPath) .AddJsonFile("appsettings.json", optional: true, reloadOnChange: true) .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true); builder.AddEnvironmentVariables(); Configuration = builder.Build(); } public IConfigurationRoot Configuration { get; } public void ConfigureServices(IServiceCollection services) { services.AddCors(options => { // this defines a CORS policy called "default" options.AddPolicy("default", policy => { policy.WithOrigins("http://localhost:5022") .AllowAnyHeader() .AllowAnyMethod(); }); }); services.AddMvcCore() .AddAuthorization() .AddJsonFormatters(); } public void Configure(IApplicationBuilder app, ILoggerFactory loggerFactory) { loggerFactory.AddConsole(Configuration.GetSection("Logging")); loggerFactory.AddDebug(); app.UseCors("default"); app.UseIdentityServerAuthentication(new IdentityServerAuthenticationOptions { Authority = "http://localhost:5003", RequireHttpsMetadata = false, ApiName = "api1" }); app.UseMvc(); }
由於 JS 須要跨域訪問 API 資源服務,因此須要增長 CORS 配置。
添加一個IdentityController
:
[Route("[controller]")] [Authorize] public class IdentityController : ControllerBase { [HttpGet] public IActionResult Get() { return new JsonResult(from c in User.Claims select new { c.Type, c.Value }); } }
建立一個 ASP.NET Core 站點,添加oidc-client.js
前端組件,測試 JS 代碼:
/// <reference path="oidc-client.js" /> function log() { document.getElementById('results').innerText = ''; Array.prototype.forEach.call(arguments, function (msg) { if (msg instanceof Error) { msg = "Error: " + msg.message; } else if (typeof msg !== 'string') { msg = JSON.stringify(msg, null, 2); } document.getElementById('results').innerHTML += msg + '\r\n'; }); } document.getElementById("login").addEventListener("click", login, false); document.getElementById("api").addEventListener("click", api, false); document.getElementById("logout").addEventListener("click", logout, false); var config = { authority: "http://localhost:5003", client_id: "js", redirect_uri: "http://localhost:5022/callback.html", response_type: "id_token token", scope:"openid profile api1", post_logout_redirect_uri: "http://localhost:5022/index.html", }; var mgr = new Oidc.UserManager(config); mgr.getUser().then(function (user) { if (user) { log("User logged in", user.profile); } else { log("User not logged in"); } }); function login() { mgr.signinRedirect(); } function api() { mgr.getUser().then(function (user) { var url = "http://localhost:5012/identity"; var xhr = new XMLHttpRequest(); xhr.open("GET", url); xhr.onload = function () { log(xhr.status, JSON.parse(xhr.responseText)); } xhr.setRequestHeader("Authorization", "Bearer " + user.access_token); xhr.send(); }); } function logout() { mgr.signoutRedirect(); }
測試過程(注意下 URL 中的參數):
混合模式(Hybrid Flow)是一種新的模式,是簡化模式(implicit flow)和驗證碼模式(authorization code flow)的混合。
簡單概述:客戶端肯定 URL(用戶認證服務),登陸在用戶認證服務,驗證成功,返回客戶端想要的用戶數據 和 access_token,並使此用戶爲登陸狀態,能夠在客戶端進行註銷用戶,客戶端能夠拿到 access_token,去訪問受權範圍以內的 API 資源。
和上面的簡化模式流程差很少,不過 access_token 不是經過瀏覽器獲取的,而是經過後臺服務獲取。
建立 ASP.NET Core 站點,Startup 配置修改以下:
public class Startup { public void ConfigureServices(IServiceCollection services) { // configure identity server with in-memory stores, keys, clients and scopes services.AddIdentityServer() .AddTemporarySigningCredential() .AddInMemoryIdentityResources(new List<IdentityResource> { new IdentityResources.OpenId(), new IdentityResources.Profile(), }) .AddInMemoryApiResources(new List<ApiResource> { new ApiResource("api1", "My API") }) .AddInMemoryClients(new List<Client> { // OpenID Connect implicit flow client (MVC) new Client { ClientId = "mvc", ClientName = "MVC Client", AllowedGrantTypes = GrantTypes.HybridAndClientCredentials, ClientSecrets = { new Secret("secret".Sha256()) }, RedirectUris = { "http://localhost:5021/signin-oidc" }, PostLogoutRedirectUris = { "http://localhost:5021" }, AllowedScopes = { IdentityServerConstants.StandardScopes.OpenId, IdentityServerConstants.StandardScopes.Profile, "api1" }, AllowOfflineAccess = true } }) .AddTestUsers(new List<TestUser> { new TestUser { SubjectId = "1", Username = "xishuai", Password = "123", Claims = new List<Claim> { new Claim("name", "xishuai"), new Claim("website", "http://xishuai.cnblogs.com") } } }); } public void Configure(IApplicationBuilder app, ILoggerFactory loggerFactory) { loggerFactory.AddConsole(LogLevel.Debug); app.UseDeveloperExceptionPage(); app.UseIdentityServer(); } }
AllowedGrantTypes
配置改成HybridAndClientCredentials
,AllowOfflineAccess
須要設置爲true
。
更多實現代碼,點擊查看:HybridServer.Web
API 資源服務站點,須要添加程序包:
"IdentityServer4.AccessTokenValidation": "1.0.1"
Startup 配置修改以下:
public Startup(IHostingEnvironment env) { var builder = new ConfigurationBuilder() .SetBasePath(env.ContentRootPath) .AddJsonFile("appsettings.json", optional: true, reloadOnChange: true) .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true); builder.AddEnvironmentVariables(); Configuration = builder.Build(); } public IConfigurationRoot Configuration { get; } public void ConfigureServices(IServiceCollection services) { services.AddMvcCore() .AddAuthorization() .AddJsonFormatters(); } public void Configure(IApplicationBuilder app, ILoggerFactory loggerFactory) { loggerFactory.AddConsole(Configuration.GetSection("Logging")); loggerFactory.AddDebug(); app.UseIdentityServerAuthentication(new IdentityServerAuthenticationOptions { Authority = "http://localhost:5002", RequireHttpsMetadata = false, ApiName = "api1" }); app.UseMvc(); }
添加一個IdentityController
:
[Route("[controller]")] [Authorize] public class IdentityController : ControllerBase { [HttpGet] public IActionResult Get() { return new JsonResult(from c in User.Claims select new { c.Type, c.Value }); } }
建立 ASP.NET Core 站點,添加程序包:
"Microsoft.AspNetCore.Authentication.Cookies": "1.0.*", "Microsoft.AspNetCore.Authentication.OpenIdConnect": "1.0.*", "IdentityModel": "2.0.0"
Startup 配置修改以下:
public Startup(IHostingEnvironment env) { var builder = new ConfigurationBuilder() .SetBasePath(env.ContentRootPath) .AddJsonFile("appsettings.json", optional: true, reloadOnChange: true) .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true) .AddEnvironmentVariables(); Configuration = builder.Build(); } public IConfigurationRoot Configuration { get; } public void ConfigureServices(IServiceCollection services) { services.AddMvc(); } public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory) { JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear(); loggerFactory.AddConsole(Configuration.GetSection("Logging")); loggerFactory.AddDebug(); if (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); } else { app.UseExceptionHandler("/Home/Error"); } app.UseCookieAuthentication(new CookieAuthenticationOptions { AuthenticationScheme = "Cookies" }); app.UseOpenIdConnectAuthentication(new OpenIdConnectOptions { AuthenticationScheme = "oidc", SignInScheme = "Cookies", Authority = "http://localhost:5002", RequireHttpsMetadata = false, ClientId = "mvc", ClientSecret = "secret", ResponseType = "code id_token", Scope = { "api1", "offline_access" }, GetClaimsFromUserInfoEndpoint = true, SaveTokens = true }); app.UseStaticFiles(); app.UseMvcWithDefaultRoute(); }
添加一個HomeController
:
public class HomeController : Controller { public IActionResult Index() { return View(); } [Authorize] public IActionResult Secure() { ViewData["Message"] = "Secure page."; return View(); } public async Task Logout() { await HttpContext.Authentication.SignOutAsync("Cookies"); await HttpContext.Authentication.SignOutAsync("oidc"); } public IActionResult Error() { return View(); } public async Task<IActionResult> CallApiUsingClientCredentials() { var tokenClient = new TokenClient("http://localhost:5002/connect/token", "mvc", "secret"); var tokenResponse = await tokenClient.RequestClientCredentialsAsync("api1"); var client = new HttpClient(); client.SetBearerToken(tokenResponse.AccessToken); var content = await client.GetStringAsync("http://localhost:5011/identity"); ViewBag.Json = JArray.Parse(content).ToString(); return View("json"); } public async Task<IActionResult> CallApiUsingUserAccessToken() { var accessToken = await HttpContext.Authentication.GetTokenAsync("access_token"); var client = new HttpClient(); client.SetBearerToken(accessToken); var content = await client.GetStringAsync("http://localhost:5011/identity"); ViewBag.Json = JArray.Parse(content).ToString(); return Content("json"); } }
CallApiUsingClientCredentials
是經過客戶端模式獲取 access_token,CallApiUsingUserAccessToken
是經過上下文獲取保存的 access_token,其實和瀏覽器 URL 中獲取是同樣的意思,但須要配置SaveTokens = true
。
使用 ASP.NET Core Identity,就是用戶管理不禁 OpenID 認證服務進行提供,ASP.NET Core Identity 就至關於用戶的一個管理者,好比用戶的存儲等。
我沒作這一塊的示例,配置比較簡單:
public void ConfigureServices(IServiceCollection services) { services.AddDbContext<ApplicationDbContext>(options => options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection"))); services.AddIdentity<ApplicationUser, IdentityRole>() .AddEntityFrameworkStores<ApplicationDbContext>() .AddDefaultTokenProviders(); services.AddMvc(); services.AddTransient<IEmailSender, AuthMessageSender>(); services.AddTransient<ISmsSender, AuthMessageSender>(); // Adds IdentityServer services.AddIdentityServer() .AddTemporarySigningCredential() .AddInMemoryIdentityResources(Config.GetIdentityResources()) .AddInMemoryApiResources(Config.GetApiResources()) .AddInMemoryClients(Config.GetClients()) .AddAspNetIdentity<ApplicationUser>(); }
詳細使用:Using ASP.NET Core Identity
關於 IdentityServer4 的配置信息,可使用 EntityFramework Core 進行存儲,配置以下:
public void ConfigureServices(IServiceCollection services) { services.AddMvc(); var connectionString = @"server=(localdb)\mssqllocaldb;database=IdentityServer4.Quickstart;trusted_connection=yes"; var migrationsAssembly = typeof(Startup).GetTypeInfo().Assembly.GetName().Name; // configure identity server with in-memory users, but EF stores for clients and resources services.AddIdentityServer() .AddTemporarySigningCredential() .AddTestUsers(Config.GetUsers()) .AddConfigurationStore(builder => builder.UseSqlServer(connectionString, options => options.MigrationsAssembly(migrationsAssembly))) .AddOperationalStore(builder => builder.UseSqlServer(connectionString, options => options.MigrationsAssembly(migrationsAssembly))); }
詳細使用:Using EntityFramework Core for configuration data
最後,簡要總結下使用 IdentityServer4 的幾種應用場景:
客戶端模式(Client Credentials):和用戶無關,用於應用程序與 API 資源的直接交互場景。
密碼模式(resource owner password credentials):和用戶有關,通常用於第三方登陸。
簡化模式-With OpenID(implicit grant type):僅限 OpenID 認證服務,用於第三方用戶登陸及獲取用戶信息,不包含受權。
簡化模式-With OpenID & OAuth(JS 客戶端調用):包含 OpenID 認證服務和 OAuth 受權,但只針對 JS 調用(URL 參數獲取),通常用於前端或無線端。
混合模式-With OpenID & OAuth(Hybrid Flow):推薦使用,包含 OpenID 認證服務和 OAuth 受權,但針對的是後端服務調用。