首页 > 解决方案 > 除了 [Authorize] 注释之外运行其他逻辑

问题描述

我将Microsoft.AspNetCore.Authentication.JwtBearerSystem.IdentityModel.Tokens.Jwt用于我的 .NET Core 项目。

每当我生成一个新令牌时,我都会将其存储到数据库中。当用户注销时,我将其从数据库中删除以使其无效(我还通过作业从数据库中删除过期的)。当用户尝试访问受[Authorize]注释保护的路由时,我想检查该令牌是否存在于数据库中。如果没有,我会发送 401。

在我正在调用的方法中的Startup和我设置注释验证的方法中(我试图评论它以显示我想要实现的目标)Configureapp.UseAuthentication()ConfigureServices[Authorize]

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存储库调用的回调异步时,我收到错误消息

返回类型为“无效”

我该如何解决?回调必须是异步的,因为我正在访问数据库。任何帮助将不胜感激!

标签: c#.net-coreasp.net-core-webapiadal

解决方案


我相信更好的方法之一是在您的授权中添加策略。(类似于如果您要创建索赔政策的处理方式)

如果您只希望该策略适用于某些授权标签

启动.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 标签

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;
        }
    }

在您的配置服务中,您需要注册它:

services.AddScoped<IAuthorizationHandler, MustHaveTokenInDbHandler>();

推荐阅读