ASP.NET Core 中基于令牌的身份验证(更新) [英] Token Based Authentication in ASP.NET Core (refreshed)
问题描述
我正在使用 ASP.NET Core 应用程序.我正在尝试实施基于令牌的身份验证,但无法弄清楚如何使用新的安全系统.
I'm working with ASP.NET Core application. I'm trying to implement Token Based Authentication but can not figure out how to use new Security System.
我的场景:客户端请求令牌.我的服务器应该授权用户并返回 access_token,客户端将在以下请求中使用该令牌.
My scenario: A client requests a token. My server should authorize the user and return access_token which will be used by the client in following requests.
这里有两篇关于实现我所需要的很棒的文章:
Here are two great articles about implementing exactly what I need:
问题是 - 在 ASP.NET Core 中如何做同样的事情对我来说并不明显.
The problem is - it is not obvious for me how to do the same thing in ASP.NET Core.
我的问题是:如何配置 ASP.NET Core Web Api 应用程序以使用基于令牌的身份验证?我应该追求什么方向?你有没有写过任何关于最新版本的文章,或者知道我在哪里可以找到?
My question is: how to configure ASP.NET Core Web Api application to work with token based authentication? What direction should I pursue? Have you written any articles about the newest version, or know where I could find ones?
谢谢!
推荐答案
从 Matt Dekrey 的精彩回答开始工作,我'已经创建了一个完整的基于令牌的身份验证示例,适用于 ASP.NET Core (1.0.1).您可以在 GitHub 上的此存储库中找到完整代码 (1.0.0-rc1, beta8、beta7),但简而言之,重要的步骤是:
Working from Matt Dekrey's fabulous answer, I've created a fully working example of token-based authentication, working against ASP.NET Core (1.0.1). You can find the full code in this repository on GitHub (alternative branches for 1.0.0-rc1, beta8, beta7), but in brief, the important steps are:
为您的应用程序生成一个密钥
在我的示例中,每次应用程序启动时我都会生成一个随机密钥,您需要生成一个并将其存储在某处并将其提供给您的应用程序.请参阅此文件了解我如何生成随机密钥以及您如何可以从 .json 文件导入它.正如@kspearrin 在评论中所建议的,数据保护 API 似乎是正确"管理密钥的理想人选,但我还没有弄清楚是否可能.如果您解决了,请提交拉取请求!
In my example, I generate a random key each time the app starts, you'll need to generate one and store it somewhere and provide it to your application. See this file for how I'm generating a random key and how you might import it from a .json file. As suggested in the comments by @kspearrin, the Data Protection API seems like an ideal candidate for managing the keys "correctly", but I've not worked out if that's possible yet. Please submit a pull request if you work it out!
Startup.cs - 配置服务
在这里,我们需要为要签名的令牌加载私钥,我们还将使用它来验证出现的令牌.我们将密钥存储在类级变量 key
中,我们将在下面的 Configure 方法中重新使用它.TokenAuthOptions 是一个保存签名身份的简单类,我们需要在 TokenController 中创建我们的密钥的受众和发行者.
Here, we need to load a private key for our tokens to be signed with, which we will also use to verify tokens as they are presented. We're storing the key in a class-level variable key
which we'll re-use in the Configure method below. TokenAuthOptions is a simple class which holds the signing identity, audience and issuer that we'll need in the TokenController to create our keys.
// Replace this with some sort of loading from config / file.
RSAParameters keyParams = RSAKeyUtils.GetRandomKey();
// Create the key, and a set of token options to record signing credentials
// using that key, along with the other parameters we will need in the
// token controlller.
key = new RsaSecurityKey(keyParams);
tokenOptions = new TokenAuthOptions()
{
Audience = TokenAudience,
Issuer = TokenIssuer,
SigningCredentials = new SigningCredentials(key, SecurityAlgorithms.Sha256Digest)
};
// Save the token options into an instance so they're accessible to the
// controller.
services.AddSingleton<TokenAuthOptions>(tokenOptions);
// Enable the use of an [Authorize("Bearer")] attribute on methods and
// classes to protect.
services.AddAuthorization(auth =>
{
auth.AddPolicy("Bearer", new AuthorizationPolicyBuilder()
.AddAuthenticationSchemes(JwtBearerDefaults.AuthenticationScheme)
.RequireAuthenticatedUser().Build());
});
我们还设置了授权策略,允许我们在我们希望保护的端点和类上使用 [Authorize("Bearer")]
.
We've also set up an authorization policy to allow us to use [Authorize("Bearer")]
on the endpoints and classes we wish to protect.
Startup.cs - 配置
这里,我们需要配置JwtBearerAuthentication:
Here, we need to configure the JwtBearerAuthentication:
app.UseJwtBearerAuthentication(new JwtBearerOptions {
TokenValidationParameters = new TokenValidationParameters {
IssuerSigningKey = key,
ValidAudience = tokenOptions.Audience,
ValidIssuer = tokenOptions.Issuer,
// When receiving a token, check that it is still valid.
ValidateLifetime = true,
// This defines the maximum allowable clock skew - i.e.
// provides a tolerance on the token expiry time
// when validating the lifetime. As we're creating the tokens
// locally and validating them on the same machines which
// should have synchronised time, this can be set to zero.
// Where external tokens are used, some leeway here could be
// useful.
ClockSkew = TimeSpan.FromMinutes(0)
}
});
令牌控制器
在令牌控制器中,您需要有一个方法来使用在 Startup.cs 中加载的密钥生成签名密钥.我们已经在 Startup 中注册了一个 TokenAuthOptions 实例,所以我们需要将它注入到 TokenController 的构造函数中:
In the token controller, you need to have a method to generate signed keys using the key that was loaded in Startup.cs. We've registered a TokenAuthOptions instance in Startup, so we need to inject that in the constructor for TokenController:
[Route("api/[controller]")]
public class TokenController : Controller
{
private readonly TokenAuthOptions tokenOptions;
public TokenController(TokenAuthOptions tokenOptions)
{
this.tokenOptions = tokenOptions;
}
...
然后您需要在处理程序中为登录端点生成令牌,在我的示例中,我使用用户名和密码并使用 if 语句验证它们,但您需要做的关键是创建或加载基于声明的身份并为其生成令牌:
Then you'll need to generate the token in your handler for the login endpoint, in my example I'm taking a username and password and validating those using an if statement, but the key thing you need to do is create or load a claims-based identity and generate the token for that:
public class AuthRequest
{
public string username { get; set; }
public string password { get; set; }
}
/// <summary>
/// Request a new token for a given username/password pair.
/// </summary>
/// <param name="req"></param>
/// <returns></returns>
[HttpPost]
public dynamic Post([FromBody] AuthRequest req)
{
// Obviously, at this point you need to validate the username and password against whatever system you wish.
if ((req.username == "TEST" && req.password == "TEST") || (req.username == "TEST2" && req.password == "TEST"))
{
DateTime? expires = DateTime.UtcNow.AddMinutes(2);
var token = GetToken(req.username, expires);
return new { authenticated = true, entityId = 1, token = token, tokenExpires = expires };
}
return new { authenticated = false };
}
private string GetToken(string user, DateTime? expires)
{
var handler = new JwtSecurityTokenHandler();
// Here, you should create or look up an identity for the user which is being authenticated.
// For now, just creating a simple generic identity.
ClaimsIdentity identity = new ClaimsIdentity(new GenericIdentity(user, "TokenAuth"), new[] { new Claim("EntityID", "1", ClaimValueTypes.Integer) });
var securityToken = handler.CreateToken(new Microsoft.IdentityModel.Tokens.SecurityTokenDescriptor() {
Issuer = tokenOptions.Issuer,
Audience = tokenOptions.Audience,
SigningCredentials = tokenOptions.SigningCredentials,
Subject = identity,
Expires = expires
});
return handler.WriteToken(securityToken);
}
应该就是这样.只需将 [Authorize("Bearer")]
添加到您想要保护的任何方法或类,如果您尝试在没有令牌的情况下访问它,您应该会得到一个错误.如果要返回 401 而不是 500 错误,则需要注册自定义异常处理程序 就像我在这里的例子.
And that should be it. Just add [Authorize("Bearer")]
to any method or class you want to protect, and you should get an error if you attempt to access it without a token present. If you want to return a 401 instead of a 500 error, you'll need to register a custom exception handler as I have in my example here.
这篇关于ASP.NET Core 中基于令牌的身份验证(更新)的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!