configuration授权服务器端点
题
我们如何使用用户名和密码stream在ASP.NET 5中使用不记名令牌? 对于我们的场景,我们希望让用户注册并使用AJAX调用login,而无需使用外部login。
为此,我们需要一个授权服务器端点。 在之前的ASP.NET版本中,我们将执行以下操作,然后在ourdomain.com/Token
URL上login。
// Configure the application for OAuth based flow PublicClientId = "self"; OAuthOptions = new OAuthAuthorizationServerOptions { TokenEndpointPath = new PathString("/Token"), Provider = new ApplicationOAuthProvider(PublicClientId), AccessTokenExpireTimeSpan = TimeSpan.FromDays(14) };
但是,在ASP.NET的当前版本中,上述不起作用。 我们一直在试图找出新的方法。 例如 ,GitHub上的aspnet / identity示例configurationFacebook,Google和Twitter身份validation,但似乎没有configuration非外部OAuth授权服务器端点,除非这是AddDefaultTokenProviders()
所做的事情,在这种情况下,我们想知道提供商的url就是。
研究
我们从这里阅读源头就了解到,我们可以通过在Startup
类中调用IAppBuilder.UseOAuthBearerAuthentication
将“承载authentication中间件”添加到HTTPpipe道中。 尽pipe我们仍然不确定如何设置令牌端点,但这是一个好的开始。 这不起作用:
public void Configure(IApplicationBuilder app) { app.UseOAuthBearerAuthentication(options => { options.MetadataAddress = "meta"; }); // if this isn't here, we just get a 404 app.Run(async context => { await context.Response.WriteAsync("Hello World."); }); }
在去ourdomain.com/meta
我们只收到我们的你好世界的页面。
进一步的研究表明,我们也可以使用IAppBuilder.UseOAuthAuthentication
扩展方法,并且它需要一个OAuthAuthenticationOptions
参数。 该参数具有TokenEndpoint
属性。 虽然我们不确定我们在做什么,但是我们尝试了这一点,这当然是行不通的。
public void Configure(IApplicationBuilder app) { app.UseOAuthAuthentication("What is this?", options => { options.TokenEndpoint = "/token"; options.AuthorizationEndpoint = "/oauth"; options.ClientId = "What is this?"; options.ClientSecret = "What is this?"; options.SignInScheme = "What is this?"; options.AutomaticAuthentication = true; }); // if this isn't here, we just get a 404 app.Run(async context => { await context.Response.WriteAsync("Hello World."); }); }
换句话说,在去ourdomain.com/token
,没有错误,只是再次我们的你好世界的页面。
好的,让我们回顾一下OWIN / Katana 3提供的不同OAuth2中间件(以及它们各自的IAppBuilder
扩展),以及将移植到ASP.NET Core的那些 :
-
app.UseOAuthBearerAuthentication
/OAuthBearerAuthenticationMiddleware
:它的名字并不是非常明显,但它是(现在仍然是,因为它已经被移植到ASP.NET核心)负责validation由OAuth2服务器中间件发出的访问令牌。 它基本上是cookies中间件的令牌 ,用来保护你的API。 在ASP.NET Core中,已经增加了可选的OpenID Connectfunction (现在可以从发出令牌的OpenID Connect服务器自动检索签名证书)。
注意:从ASP.NET Core beta8开始,它现在被命名为app.UseJwtBearerAuthentication
/ JwtBearerAuthenticationMiddleware
。
-
app.UseOAuthAuthorizationServer
/OAuthAuthorizationServerMiddleware
:顾名思义,OAuthAuthorizationServerMiddleware
是一个OAuth2授权服务器中间件,用于创build和发布访问令牌。 此中间件不会移植到ASP.NET Core : ASP.NET Core中的 OAuth授权服务 。 -
app.UseOAuthBearerTokens
:这个扩展并没有真正对应一个中间件,只是app.UseOAuthAuthorizationServer
和app.UseOAuthBearerAuthentication
的包装。 它是ASP.NET Identity包的一部分,只是一种便捷的方式,可以在一次调用中configuration用于validation访问令牌的OAuth2授权服务器和OAuth2承载中间件。 它不会移植到ASP.NET Core 。
ASP.NET Core将提供一个全新的中间件(我很自豪地说我devise了它):
-
app.UseOAuthAuthentication
/OAuthAuthenticationMiddleware
:这个新的中间件是一个通用的OAuth2交互式客户端,其行为与app.UseFacebookAuthentication
或app.UseGoogleAuthentication
但几乎支持任何标准OAuth2提供者,包括您的。 谷歌,Facebook和微软的供应商都已经更新,从这个新的基础中间件inheritance。
因此,您实际需要的中间件是OAuth2授权服务器中间件 ,又名OAuthAuthorizationServerMiddleware
。
尽pipe它被大部分社区认为是必不可less的组件,但它不会被移植到ASP.NET Core 。
幸运的是,已经有一个直接replace: AspNet.Security.OpenIdConnect.Server ( https://github.com/aspnet-contrib/AspNet.Security.OpenIdConnect.Server )
该中间件是Katana 3自带的OAuth2授权服务器中间件的一个高级分支,但是其目标是OpenID Connect (它本身基于OAuth2)。 它使用相同的低级方法,通过各种通知提供精细的控制,并允许您使用自己的框架(Nancy,ASP.NET Core MVC)为您的授权页面提供服务,就像使用OAuth2服务器中间件。 configuration它很容易:
// Add a new middleware validating access tokens issued by the server. app.UseOAuthBearerAuthentication(options => { options.AutomaticAuthenticate = true; options.Audience = "http://localhost:54540/"; options.Authority = "http://localhost:54540/"; }); // Add a new middleware issuing tokens. app.UseOpenIdConnectServer(options => { options.TokenEndpointPath = "/connect/token"; // Create your own `OpenIdConnectServerProvider` and override // ValidateTokenRequest/HandleTokenRequest to support the resource // owner password flow exactly like you did with the OAuth2 middleware. options.Provider = new AuthorizationProvider(); });
有一个OWIN / Katana 3版本和支持.NET Desktop和.NET Core的ASP.NET Core版本。
不要犹豫,让邮差样本尝试了解它是如何工作的。 我build议阅读相关的博客文章 ,解释如何实现资源所有者密码stream。
如果您仍然需要帮助,请随时联系我。 祝你好运!
在@ Pinpoint的帮助下,我们将答案的雏形联系在一起。 它显示了组件如何连接在一起而不是一个完整的解决scheme。
小提琴演示
在我们基本的项目设置中,我们能够在提琴手中做出以下请求和响应。
请求
POST http://localhost:50000/connect/token HTTP/1.1 User-Agent: Fiddler Host: localhost:50000 Content-Length: 61 Content-Type: application/x-www-form-urlencoded grant_type=password&username=my_username&password=my_password
响应
HTTP/1.1 200 OK Cache-Control: no-cache Pragma: no-cache Content-Length: 1687 Content-Type: application/json;charset=UTF-8 Expires: -1 X-Powered-By: ASP.NET Date: Tue, 16 Jun 2015 01:24:42 GMT { "access_token" : "eyJ0eXAiOi ... 5UVACg", "expires_in" : 3600, "token_type" : "bearer" }
响应提供了一个不记名标记,我们可以使用它来访问应用程序的安全部分。
项目结构
这是Visual Studio中项目的结构。 我们必须将其Properties
> Debug
> Port
为50000
以便它充当我们configuration的身份服务器。 这里是相关的文件:
ResourceOwnerPasswordFlow Providers AuthorizationProvider.cs project.json Startup.cs
Startup.cs
为了便于阅读,我将Startup
类分成两部分。
Startup.ConfigureServices
AddAuthentication()
,我们只需要AddAuthentication()
。
public partial class Startup { public void ConfigureServices(IServiceCollection services) { services.AddAuthentication(); } }
Startup.Configure
public partial class Startup { public void Configure(IApplicationBuilder app) { JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear(); JwtSecurityTokenHandler.DefaultOutboundClaimTypeMap.Clear(); // Add a new middleware validating access tokens issued by the server. app.UseJwtBearerAuthentication(new JwtBearerOptions { AutomaticAuthenticate = true, AutomaticChallenge = true, Audience = "resource_server_1", Authority = "http://localhost:50000/", RequireHttpsMetadata = false }); // Add a new middleware issuing tokens. app.UseOpenIdConnectServer(options => { // Disable the HTTPS requirement. options.AllowInsecureHttp = true; // Enable the token endpoint. options.TokenEndpointPath = "/connect/token"; options.Provider = new AuthorizationProvider(); // Force the OpenID Connect server middleware to use JWT // instead of the default opaque/encrypted format. options.AccessTokenHandler = new JwtSecurityTokenHandler { InboundClaimTypeMap = new Dictionary<string, string>(), OutboundClaimTypeMap = new Dictionary<string, string>() }; // Register an ephemeral signing key, used to protect the JWT tokens. // On production, you'd likely prefer using a signing certificate. options.SigningCredentials.AddEphemeralKey(); }); app.UseMvc(); app.Run(async context => { await context.Response.WriteAsync("Hello World!"); }); } }
AuthorizationProvider.cs
public sealed class AuthorizationProvider : OpenIdConnectServerProvider { public override Task ValidateTokenRequest(ValidateTokenRequestContext context) { // Reject the token requests that don't use // grant_type=password or grant_type=refresh_token. if (!context.Request.IsPasswordGrantType() && !context.Request.IsRefreshTokenGrantType()) { context.Reject( error: OpenIdConnectConstants.Errors.UnsupportedGrantType, description: "Only grant_type=password and refresh_token " + "requests are accepted by this server."); return Task.FromResult(0); } // Since there's only one application and since it's a public client // (ie a client that cannot keep its credentials private), call Skip() // to inform the server that the request should be accepted without // enforcing client authentication. context.Skip(); return Task.FromResult(0); } public override Task HandleTokenRequest(HandleTokenRequestContext context) { // Only handle grant_type=password token requests and let the // OpenID Connect server middleware handle the other grant types. if (context.Request.IsPasswordGrantType()) { // Validate the credentials here (eg using ASP.NET Core Identity). // You can call Reject() with an error code/description to reject // the request and return a message to the caller. var identity = new ClaimsIdentity(context.Options.AuthenticationScheme); identity.AddClaim(OpenIdConnectConstants.Claims.Subject, "[unique identifier]"); // By default, claims are not serialized in the access and identity tokens. // Use the overload taking a "destinations" parameter to make sure // your claims are correctly serialized in the appropriate tokens. identity.AddClaim("urn:customclaim", "value", OpenIdConnectConstants.Destinations.AccessToken, OpenIdConnectConstants.Destinations.IdentityToken); var ticket = new AuthenticationTicket( new ClaimsPrincipal(identity), new AuthenticationProperties(), context.Options.AuthenticationScheme); // Call SetResources with the list of resource servers // the access token should be issued for. ticket.SetResources("resource_server_1"); // Call SetScopes with the list of scopes you want to grant // (specify offline_access to issue a refresh token). ticket.SetScopes("profile", "offline_access"); context.Validate(ticket); } return Task.FromResult(0); } }
project.json
{ "dependencies": { "AspNet.Security.OpenIdConnect.Server": "1.0.0", "Microsoft.AspNetCore.Authentication.JwtBearer": "1.0.0", "Microsoft.AspNetCore.Mvc": "1.0.0", } // other code omitted }