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

ASP.NET WebAPI独立授权服务自定义Authorize属性实现问询

自定义Authorize属性中获取Bearer Token的实现方案

一、如何在自定义Authorize属性中获取Bearer Token

其实在自定义AuthorizeAttribute里获取Bearer Token非常直接,核心是从请求的Authorization请求头中解析提取。你可以重写OnAuthorization方法,通过HttpActionContext拿到请求对象后操作,具体步骤如下:

  1. 自定义属性类继承AuthorizeAttribute,重写OnAuthorization(HttpActionContext actionContext)方法
  2. 从actionContext.Request.Headers中获取Authorization头信息
  3. 验证头格式是否符合Bearer {token}规范,提取出Token字符串

给你一个可参考的代码示例:

public class CustomAuthorizeAttribute : AuthorizeAttribute
{
    public override void OnAuthorization(HttpActionContext actionContext)
    {
        // 读取Authorization请求头
        var authHeader = actionContext.Request.Headers.Authorization;
        
        if (authHeader != null && authHeader.Scheme.Equals("Bearer", StringComparison.OrdinalIgnoreCase))
        {
            // 提取Bearer Token内容
            string bearerToken = authHeader.Parameter;
            
            // 调用授权服务获取用户角色信息
            var userInfo = FetchUserInfoFromAuthService(bearerToken);
            
            // 验证用户权限(比如匹配接口要求的角色)
            if (userInfo == null || !CheckUserPermission(userInfo, actionContext))
            {
                actionContext.Response = actionContext.Request.CreateResponse(HttpStatusCode.Unauthorized);
                return;
            }
            
            // 将用户信息存入请求上下文,方便后续接口使用
            actionContext.Request.Properties["CurrentAuthenticatedUser"] = userInfo;
        }
        else
        {
            // 未提供有效Bearer Token,返回401
            actionContext.Response = actionContext.Request.CreateResponse(HttpStatusCode.Unauthorized);
        }
    }
    
    // 封装调用授权服务的逻辑
    private UserInfo FetchUserInfoFromAuthService(string token)
    {
        using (var client = new HttpClient())
        {
            client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token);
            var response = client.GetAsync("http://your-auth-service/api/user/info").Result;
            
            if (response.IsSuccessStatusCode)
            {
                return response.Content.ReadAsAsync<UserInfo>().Result;
            }
            return null;
        }
    }
    
    // 验证用户是否具备接口访问权限
    private bool CheckUserPermission(UserInfo userInfo, HttpActionContext actionContext)
    {
        var requiredRoles = Roles.Split(',').Select(r => r.Trim()).ToList();
        return requiredRoles.All(role => userInfo.Roles.Contains(role));
    }
}

// 自定义用户信息模型
public class UserInfo
{
    public string UserId { get; set; }
    public string Username { get; set; }
    public List<string> Roles { get; set; }
}

二、更优的Token传递与验证方案

每次接口请求都远程调用授权服务验证Token,会增加系统延迟和依赖耦合,这里有几个更高效的方案可以考虑:

1. 使用JWT存储用户角色信息

如果你的Token采用JWT格式,可以把用户ID、角色等非敏感信息直接编码到Token的Payload中。这样数据服务无需调用授权服务,只需用授权服务提供的公钥验证Token签名有效性,同时直接解析Payload获取用户信息,性能会大幅提升。

示例JWT解析代码:

private UserInfo ValidateAndParseJwt(string token)
{
    var tokenHandler = new JwtSecurityTokenHandler();
    var validationParams = new TokenValidationParameters
    {
        ValidateIssuer = true,
        ValidIssuer = "your-auth-service-issuer", // 授权服务的Issuer标识
        ValidateAudience = true,
        ValidAudience = "your-data-service-audience", // 数据服务的Audience标识
        ValidateIssuerSigningKey = true,
        IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes("your-production-secret-key")), // 生产环境建议用非对称加密公钥
        ValidateLifetime = true
    };
    
    try
    {
        ClaimsPrincipal principal = tokenHandler.ValidateToken(token, validationParams, out _);
        return new UserInfo
        {
            UserId = principal.FindFirst(ClaimTypes.NameIdentifier)?.Value,
            Username = principal.FindFirst(ClaimTypes.Name)?.Value,
            Roles = principal.FindAll(ClaimTypes.Role).Select(c => c.Value).ToList()
        };
    }
    catch
    {
        return null;
    }
}

2. 缓存用户验证结果

如果必须保留远程调用授权服务的方式,可以在数据服务中加入缓存层(比如Redis、MemoryCache),将Token与对应的用户信息做关联缓存,缓存过期时间设置为与Token有效期一致。这样同一个Token在有效期内只需调用一次授权服务,减少重复请求。

3. 用OWIN中间件替代自定义Authorize属性

相比重写AuthorizeAttribute,更推荐用OWIN中间件统一处理Token验证。中间件可以在请求进入WebAPI控制器前完成Token校验和用户信息注入,代码更解耦,也更容易扩展。

示例OWIN中间件结构:

public class TokenValidationMiddleware
{
    private readonly Func<IDictionary<string, object>, Task> _next;

    public TokenValidationMiddleware(Func<IDictionary<string, object>, Task> next)
    {
        _next = next;
    }

    public async Task Invoke(IDictionary<string, object> environment)
    {
        var context = new OwinContext(environment);
        var authHeader = context.Request.Headers["Authorization"];
        
        if (!string.IsNullOrEmpty(authHeader) && authHeader.StartsWith("Bearer ", StringComparison.OrdinalIgnoreCase))
        {
            var token = authHeader.Substring("Bearer ".Length).Trim();
            var userInfo = ValidateToken(token); // 这里是验证逻辑(JWT解析或远程调用)
            
            if (userInfo != null)
            {
                // 将用户信息注入Owin上下文
                var claims = new List<Claim>
                {
                    new Claim(ClaimTypes.NameIdentifier, userInfo.UserId),
                    new Claim(ClaimTypes.Name, userInfo.Username)
                };
                claims.AddRange(userInfo.Roles.Select(r => new Claim(ClaimTypes.Role, r)));
                
                context.Request.User = new ClaimsPrincipal(new ClaimsIdentity(claims, "Bearer"));
            }
        }
        
        await _next(environment);
    }
    
    private UserInfo ValidateToken(string token)
    {
        // 验证逻辑,同上
        return null;
    }
}

// 在Startup.cs中注册中间件
public void Configuration(IAppBuilder app)
{
    app.Use<TokenValidationMiddleware>();
    // 注册WebAPI路由等其他配置
    app.UseWebApi(GlobalConfiguration.Configuration);
}

配置完成后,你可以直接使用原生的[Authorize]属性控制接口权限,无需自定义属性,中间层已完成Token验证和用户信息注入。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.26 09:50:34