问题描述
我正在为我的 .NET Core 项目使用 Microsoft.AspNetCore.Authentication.JwtBearer 和 System.IdentityModel.Tokens.Jwt.
I'm using the Microsoft.AspNetCore.Authentication.JwtBearer and System.IdentityModel.Tokens.Jwt for my .NET Core project.
每当我生成一个新令牌时,我都会将其存储到数据库中.当用户退出时,我将其从数据库中删除以使其无效(我还通过作业从数据库中删除了过期的).当用户尝试访问受 [Authorize]
注释保护的路由时,我想检查该令牌是否存在于数据库中.如果没有,我会发送 401.
Whenever I generate a new token I store that to the database. When a user signs out, I remove it from the database to invalidate it (I also remove the expired ones from the database with a job). When a user tries to access a route protected by the [Authorize]
annotation I want to check if that token exists in the database. If not, I send a 401.
在我的 Startup 中的 Configure
方法中,我正在调用 app.UseAuthentication()
和 ConfigureServices
> 方法我设置了对 [Authorize]
注释的验证(我试图对其进行评论以显示我想要实现的目标)
In my Startup in the Configure
method I'm calling app.UseAuthentication()
and in the ConfigureServices
method I setup the validation for the [Authorize]
annotation (I tried to comment it to show what I want to achieve)
services
.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(jwtBearerOptions =>
{
byte[] symmetricKey = Convert.FromBase64String("secretFromConfig");
SymmetricSecurityKey symmetricSecurityKey = new SymmetricSecurityKey(symmetricKey);
jwtBearerOptions.TokenValidationParameters = new TokenValidationParameters()
{
ValidateIssuerSigningKey = true,
ValidateLifetime = true,
ValidateIssuer = false,
ValidateAudience = false,
IssuerSigningKey = symmetricSecurityKey,
};
jwtBearerOptions.Events = new JwtBearerEvents()
{
OnTokenValidated = tokenValidatedContext =>
{
// inject my database repository
ITokensRepository tokensRepository = tokenValidatedContext.HttpContext.RequestServices.GetRequiredService<ITokensRepository>();
JwtSecurityTokenHandler tokenHandler = new JwtSecurityTokenHandler();
// convert the token to a string
string token = tokenHandler.WriteToken(tokenValidatedContext.SecurityToken);
// read the username from the token payload
Claim usernameClaim = tokenValidatedContext.Principal.Claims.FirstOrDefault(claim => claim.Type == "username");
// assign if exists
string username = usernameClaim?.Value;
// fetch token from database
object tokenInfo = await tokensRepository.GetUserTokenAsync(token, username);
if (tokenInfo == null)
{
// return a 401 if token doesn't exist in database
tokenValidatedContext.Fail("Invalid token.");
// return new UnauthorizedResult();
}
else
{
tokenValidatedContext.Success();
// controller methods can fetch it from here later on
tokenValidatedContext.HttpContext.Items["username"] = username;
}
return Task.CompletedTask;
}
};
});
当我尝试使存储库调用的 OnTokenValidated
回调异步时,我收到错误消息
When I try to make the OnTokenValidated
callback async for the repository call I get the error message
返回类型是'void'
我该如何解决?回调必须是异步的,因为我正在访问数据库.任何帮助将不胜感激!
How can I solve it? The callback has to be async because I'm accessing the database. Any help will highly appreciated!
推荐答案
我相信更好的方法之一是在您的授权中添加策略.(类似于如果您要创建索赔政策的处理方式)
I believe one of the better ways to do this is through adding a policy to your Authorization. (Similar to how it is handled if you want to create a claims policy)
如果您只想要某个授权标签的策略
If you want the policy only for certain authorize tage
Startup.cs
services.AddAuthorization(authorizationOptions =>
{
authorizationOptions.AddPolicy(
"MustHaveTokenInDb",
policyBuilder =>
{
//add any other policy requirements here too including ones by default
//eg policyBuilder.RequireAuthenticatedUser();
policyBuilder.AddRequirements(
new MustHaveTokenInDbRequirement());
});
//only if you want to register as the default policy
authorizationOptions.DefaultPolicy = authorizationOptions.GetPolicy("MustHaveTokenInDb");
});
然后在授权
您使用的标签
[Authorize("MustHaveTokenInDB")]
您可以设置默认策略,允许您照常使用 Authorize 标签
You can set a default policy either which allows you to use the Authorize tag as normal
authorizationOptions.DefaultPolicy = authorizationOptions.GetPolicy("MustHaveTokenInDb");
您的需求类
public class MustHaveTokenInDbRequirement : IAuthorizationRequirement
{
public MustHaveTokenInDbRequirement ()
{
}
}
处理程序类
public class MustHaveTokenInDbHandler : AuthorizationHandler<MustHaveTokenInDbRequirement >
{
public MustHaveTokenInDbHandler ()
{
//your dependency injections
}
protected override Task HandleRequirementAsync(
AuthorizationHandlerContext context,
MustHaveTokenInDbRequirement requirement)
{
//your logic
if (noTokenInDb)
{
context.Fail();
return Task.CompletedTask;
}
// has token in db
context.Succeed(requirement);
return Task.CompletedTask;
}
}
在你的配置服务中你需要注册它:
In your configure services you need to register it:
services.AddScoped<IAuthorizationHandler, MustHaveTokenInDbHandler>();
这篇关于运行除 [Authorize] 注释之外的其他逻辑的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!