关于 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 受权,但针对的是后端服务调用。