You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

.NET Web API中扩展Authorize属性实现令牌验证及用户注入

解决方案:扩展ASP.NET Core授权流程实现自动令牌验证与用户注入

针对你的需求,我们可以通过自定义授权处理程序和动作过滤器来扩展ASP.NET Core的授权流程,实现策略验证后自动执行令牌校验、用户数据库验证,并将合法用户注入到请求上下文供后续使用,彻底消除控制器中的重复代码。

一、自定义授权处理程序:集成令牌与用户验证

创建自定义的授权需求和处理程序,在策略验证通过后自动执行令牌校验和用户查询,并将合法用户存入请求上下文。

1. 定义授权需求(若原有策略未关联自定义需求)

// 定义Admin角色的授权需求标识
public class AdminRequirement : IAuthorizationRequirement { }

2. 实现自定义授权处理程序

public class AdminAuthorizationHandler : AuthorizationHandler<AdminRequirement>
{
    private readonly UserManager<ApplicationUser> _userManager;
    private readonly IConfiguration _configuration;
    private readonly ILogger<AdminAuthorizationHandler> _logger;
    private readonly IStringLocalizer _localizer;
    private readonly IActionContextAccessor _actionContextAccessor;

    public AdminAuthorizationHandler(
        UserManager<ApplicationUser> userManager,
        IConfiguration configuration,
        ILogger<AdminAuthorizationHandler> logger,
        IStringLocalizer localizer,
        IActionContextAccessor actionContextAccessor)
    {
        _userManager = userManager;
        _configuration = configuration;
        _logger = logger;
        _localizer = localizer;
        _actionContextAccessor = actionContextAccessor;
    }

    protected override async Task HandleRequirementAsync(AuthorizationHandlerContext context, AdminRequirement requirement)
    {
        // 1. 先确保基础身份验证通过
        if (!context.User.Identity.IsAuthenticated)
        {
            context.Fail();
            return;
        }

        // 2. 获取HttpContext上下文
        if (context.Resource is not HttpContext httpContext)
        {
            context.Fail();
            return;
        }

        // 3. 获取Controller上下文用于ModelState校验
        var controllerContext = _actionContextAccessor.ActionContext as ControllerContext;
        if (controllerContext == null)
        {
            context.Fail();
            return;
        }

        // 4. 执行令牌验证与用户查询(复用原有Helper逻辑)
        var (success, user) = await UserHelpers.ValidateTokenAndGetUserAsync(
            httpContext, _userManager, _configuration, _logger, controllerContext, controllerContext.ModelState);
        
        if (!success || user?.CustomerId == null)
        {
            httpContext.Items["AuthErrorMsg"] = _localizer[ResourceConstants.Unauthorized.UserNotFoundError].Value;
            context.Fail();
            return;
        }

        // 5. 将合法用户存入HttpContext,供后续使用
        httpContext.Items["AuthenticatedUser"] = user;

        // 6. 授权通过
        context.Succeed(requirement);
    }
}

二、注册授权服务与策略配置

在Program.cs中注册自定义处理程序,并将其关联到原有"Admin"策略:

// 注册IActionContextAccessor用于获取控制器上下文
builder.Services.AddSingleton<IActionContextAccessor, ActionContextAccessor>();

// 配置授权策略
builder.Services.AddAuthorization(options =>
{
    options.AddPolicy("Admin", policy =>
    {
        // 保留原有策略规则(比如角色要求)
        policy.RequireRole("Admin");
        // 添加自定义授权需求
        policy.Requirements.Add(new AdminRequirement());
    });
});

// 注册自定义授权处理程序
builder.Services.AddScoped<IAuthorizationHandler, AdminAuthorizationHandler>();

三、自定义动作过滤器:自动更新用户Cookie

将原有CookieToResponse逻辑封装为动作过滤器,在请求处理完成后自动执行:

public class UpdateUserCookieFilter : IAsyncActionFilter
{
    private readonly IConfiguration _configuration;
    private readonly IWebHostEnvironment _webHostEnvironment;

    public UpdateUserCookieFilter(IConfiguration configuration, IWebHostEnvironment webHostEnvironment)
    {
        _configuration = configuration;
        _webHostEnvironment = webHostEnvironment;
    }

    public async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)
    {
        // 先执行控制器方法
        var resultContext = await next();

        // 仅当授权成功且请求无异常时更新Cookie
        if (context.HttpContext.Items.TryGetValue("AuthenticatedUser", out var userObj) 
            && userObj is ApplicationUser user)
        {
            await UserHelpers.CookieToResponse(
                user, _configuration, _webHostEnvironment, context.HttpContext.Response, context.HttpContext.Request)
                .ConfigureAwait(false);
        }
    }
}

注册过滤器(可全局注册或按需添加到控制器):

builder.Services.AddControllers(options =>
{
    // 全局注册,所有授权请求自动执行Cookie更新
    options.Filters.Add<UpdateUserCookieFilter>();
});

四、简化控制器方法

现在控制器方法无需手动验证令牌和用户,直接从上下文获取合法用户即可:

方式1:从HttpContext.Items直接获取

[Authorize(Policy = "Admin")]
[Route("get")]
[HttpGet]
public async Task<IActionResult> Get()
{
    // 兜底校验(理论上授权通过后必然存在)
    if (!HttpContext.Items.TryGetValue("AuthenticatedUser", out var userObj) 
        || userObj is not ApplicationUser user)
    {
        var errorMsg = HttpContext.Items["AuthErrorMsg"] as string ?? "Unauthorized";
        return Unauthorized(HttpResponseHelper.GetErrorResponse(StatusCodes.Status401Unauthorized, errorMsg));
    }

    // 直接使用user对象执行业务逻辑
    // ...

    return Ok();
}

方式2:自定义参数绑定(更简洁)

创建模型绑定器,直接将用户注入到方法参数:

public class AuthenticatedUserBinder : IModelBinder
{
    public Task BindModelAsync(ModelBindingContext bindingContext)
    {
        if (bindingContext.HttpContext.Items.TryGetValue("AuthenticatedUser", out var userObj) 
            && userObj is ApplicationUser user)
        {
            bindingContext.Result = ModelBindingResult.Success(user);
        }
        else
        {
            bindingContext.Result = ModelBindingResult.Failed();
        }
        return Task.CompletedTask;
    }
}

// 注册绑定器提供者
public class AuthenticatedUserBinderProvider : IModelBinderProvider
{
    public IModelBinder? GetBinder(ModelBinderProviderContext context)
    {
        return context.Metadata.ModelType == typeof(ApplicationUser) 
            ? new AuthenticatedUserBinder() 
            : null;
    }
}

在Program.cs中注册绑定器:

builder.Services.AddControllers(options =>
{
    options.ModelBinderProviders.Insert(0, new AuthenticatedUserBinderProvider());
    options.Filters.Add<UpdateUserCookieFilter>();
});

控制器方法可简化为:

[Authorize(Policy = "Admin")]
[Route("get")]
[HttpGet]
public async Task<IActionResult> Get(ApplicationUser user)
{
    // 直接使用注入的user对象
    // ...

    return Ok();
}

内容的提问来源于stack exchange,提问作者OJs

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.17 21:46:06