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

如何借助JWT令牌与Action过滤器获取用户专属内容?

.NET Web API 基于JWT声明的权限过滤实现方案

完全可以通过自定义Action过滤器、资源过滤器或者策略授权来实现自动验证JWT声明与请求资源的匹配,避免重复代码。以下是几种实用方案:

一、自定义Action过滤器(通用参数验证)

创建一个继承ActionFilterAttribute的过滤器,在Action执行前自动验证当前用户的JWT声明与请求中的用户ID/资源ID是否匹配:

public class UserResourceAuthorizationFilter : ActionFilterAttribute
{
    public override void OnActionExecuting(ActionExecutingContext context)
    {
        // 从JWT令牌中获取当前用户ID声明
        var currentUserId = context.HttpContext.User.FindFirst(ClaimTypes.NameIdentifier)?.Value;
        if (string.IsNullOrEmpty(currentUserId))
        {
            context.Result = new UnauthorizedResult();
            return;
        }

        // 场景1:验证Action参数中的userId
        if (context.ActionArguments.TryGetValue("userId", out var paramUserId) 
            && paramUserId?.ToString() != currentUserId)
        {
            context.Result = new ForbidResult();
            return;
        }

        // 场景2:验证路由中的资源ID(比如/api/records/{recordId})
        if (context.HttpContext.Request.RouteValues.TryGetValue("recordId", out var routeRecordId))
        {
            // 注入数据仓储验证资源归属(需提前注册仓储服务)
            var recordRepo = context.HttpContext.RequestServices.GetService<IRecordRepository>();
            if (recordRepo != null && !recordRepo.IsRecordOwnedByUser(routeRecordId.ToString(), currentUserId))
            {
                context.Result = new ForbidResult();
                return;
            }
        }

        base.OnActionExecuting(context);
    }
}

使用方式

  • 全局注册(所有接口生效):在Program.cs中添加
builder.Services.AddControllers(options =>
{
    options.Filters.Add<UserResourceAuthorizationFilter>();
});
  • 局部生效(仅指定Controller/Action):
[ApiController]
[Route("api/records")]
[UserResourceAuthorizationFilter]
public class RecordsController : ControllerBase
{
    // 接口逻辑
}

二、策略授权(灵活权限规则)

利用.NET内置的策略授权,定义专属的权限验证逻辑,适合复杂的权限场景:

1. 注册授权策略

在Program.cs中添加:

builder.Services.AddAuthorization(options =>
{
    options.AddPolicy("UserOwnsResource", policy =>
        policy.RequireAssertion(context =>
        {
            var currentUserId = context.User.FindFirst(ClaimTypes.NameIdentifier)?.Value;
            if (string.IsNullOrEmpty(currentUserId)) return false;

            // 从HttpContext获取请求中的目标资源ID
            var httpContext = context.Resource as HttpContext;
            var targetUserId = httpContext?.Request.RouteValues["userId"]?.ToString();
            var targetRecordId = httpContext?.Request.RouteValues["recordId"]?.ToString();

            // 这里可扩展多种验证逻辑,比如验证记录归属
            if (!string.IsNullOrEmpty(targetUserId))
            {
                return targetUserId == currentUserId;
            }
            else if (!string.IsNullOrEmpty(targetRecordId))
            {
                var repo = httpContext.RequestServices.GetService<IRecordRepository>();
                return repo?.IsRecordOwnedByUser(targetRecordId, currentUserId) ?? false;
            }

            return false;
        }));
});

2. 在接口中使用

[Authorize(Policy = "UserOwnsResource")]
[HttpGet("{recordId}")]
public async Task<IActionResult> GetRecord(string recordId)
{
    // 无需手动验证,已由策略完成权限检查
    var record = await _recordRepo.GetByIdAsync(recordId);
    return Ok(record);
}

三、自动过滤查询数据(列表场景)

如果需要返回用户专属的列表数据,可以通过扩展方法自动拼接过滤条件,避免每个接口重复写Where:

1. 定义实体接口

public interface IUserOwnedEntity
{
    string UserId { get; set; }
}

// 让你的实体实现该接口
public class Record : IUserOwnedEntity
{
    public string Id { get; set; }
    public string UserId { get; set; }
    // 其他属性
}

2. 创建查询扩展方法

public static class QueryableExtensions
{
    public static IQueryable<T> FilterByCurrentUser<T>(this IQueryable<T> query, HttpContext httpContext)
        where T : IUserOwnedEntity
    {
        var currentUserId = httpContext.User.FindFirst(ClaimTypes.NameIdentifier)?.Value;
        if (string.IsNullOrEmpty(currentUserId))
        {
            return query.Where(x => false); // 无权限返回空集合
        }
        return query.Where(x => x.UserId == currentUserId);
    }
}

3. 在接口中使用

[HttpGet]
public async Task<IActionResult> GetMyRecords()
{
    // 自动过滤当前用户的记录
    var myRecords = await _dbContext.Records.FilterByCurrentUser(HttpContext).ToListAsync();
    return Ok(myRecords);
}

这些方案可以根据你的业务场景组合使用,彻底消除手动验证的重复代码,实现权限逻辑的集中管理。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.22 17:09:25