参考Matt Dekrey的精彩回答,我创建了一个完全可用的基于令牌的身份验证的示例,可以在ASP.NET Core (1.0.1)上工作。您可以在GitHub上的此存储库中找到完整的代码(包括1.0.0-rc1, beta8, beta7等替代分支),但简要来说,重要步骤如下:
为应用程序生成密钥
在我的示例中,每次启动应用程序时都会生成一个随机密钥,您需要生成并将其存储在某个位置,并提供给您的应用程序。可以查看此文件以了解如何生成随机密钥以及如何从.json文件中导入密钥。正如@kspearrin在评论中建议的那样,Data Protection API似乎是管理密钥“正确”的理想选择,但我尚未确定是否可能。如果您找到了解决方法,请提交拉取请求!
Startup.cs - ConfigureServices
在这里,我们需要加载用于签署令牌的私钥,我们还将使用它来验证呈现的令牌。我们将密钥存储在类级别变量key
中,在下面的Configure方法中重复使用。TokenAuthOptions是一个简单的类,它保存我们在TokenController中创建密钥所需的签名标识、受众和颁发者。
RSAParameters keyParams = RSAKeyUtils.GetRandomKey();
key = new RsaSecurityKey(keyParams);
tokenOptions = new TokenAuthOptions()
{
Audience = TokenAudience,
Issuer = TokenIssuer,
SigningCredentials = new SigningCredentials(key, SecurityAlgorithms.Sha256Digest)
};
services.AddSingleton<TokenAuthOptions>(tokenOptions);
services.AddAuthorization(auth =>
{
auth.AddPolicy("Bearer", new AuthorizationPolicyBuilder()
.AddAuthenticationSchemes(JwtBearerDefaults.AuthenticationScheme)
.RequireAuthenticatedUser().Build());
});
我们还设置了授权策略,允许我们在需要保护的端点和类上使用
[Authorize("Bearer")]
。
Startup.cs - Configure
在这里,我们需要配置JwtBearerAuthentication:
app.UseJwtBearerAuthentication(new JwtBearerOptions {
TokenValidationParameters = new TokenValidationParameters {
IssuerSigningKey = key,
ValidAudience = tokenOptions.Audience,
ValidIssuer = tokenOptions.Issuer,
ValidateLifetime = true,
ClockSkew = TimeSpan.FromMinutes(0)
}
});
TokenController
在token控制器中,需要有一个使用在Startup.cs中加载的密钥生成签名密钥的方法。我们已在Startup中注册了TokenAuthOptions实例,因此需要在TokenController的构造函数中注入它:
[Route("api/[controller]")]
public class TokenController : Controller
{
private readonly TokenAuthOptions tokenOptions;
public TokenController(TokenAuthOptions tokenOptions)
{
this.tokenOptions = tokenOptions;
}
...
那么您需要在登录端点的处理程序中生成令牌,在我的示例中,我使用if语句获取用户名和密码并验证它们,但关键的是您需要创建或加载基于声明的标识,并为其生成令牌:
public class AuthRequest
{
public string username { get; set; }
public string password { get; set; }
}
[HttpPost]
public dynamic Post([FromBody] AuthRequest req)
{
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();
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错误,则需要注册自定义异常处理程序,就像我在此示例中做的那样。