ASP.NET WebAPI独立授权服务自定义Authorize属性实现问询
一、如何在自定义Authorize属性中获取Bearer Token
其实在自定义AuthorizeAttribute里获取Bearer Token非常直接,核心是从请求的Authorization请求头中解析提取。你可以重写OnAuthorization方法,通过HttpActionContext拿到请求对象后操作,具体步骤如下:
- 自定义属性类继承
AuthorizeAttribute,重写OnAuthorization(HttpActionContext actionContext)方法 - 从
actionContext.Request.Headers中获取Authorization头信息 - 验证头格式是否符合
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
相关产品推荐
相关产品推荐

