如何在WebApi中手动为用户上下文分配自定义Token(无需ASP.Net Identity)
没问题,我来给你一步步拆解怎么实现这个需求——不用ASP.NET Identity,自己写Token认证过滤器,还能把用户信息顺畅塞进WebApi的用户上下文里:
第一步:先搞定Token的生成逻辑
首先你得定义Token里要携带的用户核心信息(比如ID、用户名、角色),然后用JWT来生成Token(毕竟JWT是目前最常用的无状态Token方案)。你需要先引用System.IdentityModel.Tokens.Jwt这个NuGet包,然后写个生成Token的工具方法:
using System; using System.Collections.Generic; using System.IdentityModel.Tokens.Jwt; using System.Security.Claims; using System.Text; using Microsoft.IdentityModel.Tokens; public static class TokenGenerator { // 建议把密钥、发行方、受众放到配置文件里,这里只是示例 private const string SecretKey = "Your_Super_Secure_Secret_Key_At_Least_16_Chars"; private const string Issuer = "Your_Api_Issuer_Name"; private const string Audience = "Your_Api_Audience_Name"; public static string GenerateUserToken(string userId, string userName, List<string> userRoles) { // 生成签名密钥 var symmetricKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(SecretKey)); var signingCredentials = new SigningCredentials(symmetricKey, SecurityAlgorithms.HmacSha256); // 组装用户声明(这些信息会被塞进Token,后续解析后就能拿到) var claims = new List<Claim> { new Claim(ClaimTypes.NameIdentifier, userId), // 用户ID new Claim(ClaimTypes.Name, userName), // 用户名 }; // 添加角色声明 claims.AddRange(userRoles.Select(role => new Claim(ClaimTypes.Role, role))); // 生成JWT Token var token = new JwtSecurityToken( issuer: Issuer, audience: Audience, claims: claims, expires: DateTime.UtcNow.AddHours(1), // Token有效期,按需调整 signingCredentials: signingCredentials ); return new JwtSecurityTokenHandler().WriteToken(token); } }
接下来要写一个继承自AuthorizeAttribute的自定义过滤器,核心逻辑是:从请求头里取Token、验证Token有效性、解析用户信息,最后把用户上下文塞进WebApi的请求上下文里,这样控制器里就能像用Identity那样访问User属性了。
using System.Linq; using System.Net; using System.Net.Http; using System.Security.Claims; using System.Web.Http; using System.Web.Http.Controllers; using System.IdentityModel.Tokens.Jwt; using Microsoft.IdentityModel.Tokens; public class CustomTokenAuthorizeAttribute : AuthorizeAttribute { private const string AuthHeaderName = "Authorization"; private const string BearerPrefix = "Bearer "; protected override bool IsAuthorized(HttpActionContext actionContext) { // 1. 从请求头获取Token if (!actionContext.Request.Headers.Contains(AuthHeaderName)) { actionContext.Response = actionContext.Request.CreateResponse(HttpStatusCode.Unauthorized, "缺少Authorization请求头"); return false; } var authHeaderValue = actionContext.Request.Headers.GetValues(AuthHeaderName).FirstOrDefault(); if (string.IsNullOrEmpty(authHeaderValue) || !authHeaderValue.StartsWith(BearerPrefix)) { actionContext.Response = actionContext.Request.CreateResponse(HttpStatusCode.Unauthorized, "Authorization格式错误,应为Bearer {Token}"); return false; } var token = authHeaderValue.Substring(BearerPrefix.Length); try { // 2. 验证并解析Token var tokenHandler = new JwtSecurityTokenHandler(); var validationParams = new TokenValidationParameters { ValidateIssuer = true, ValidIssuer = TokenGenerator.Issuer, // 和生成Token时一致 ValidateAudience = true, ValidAudience = TokenGenerator.Audience, // 和生成Token时一致 ValidateIssuerSigningKey = true, IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(TokenGenerator.SecretKey)), ValidateLifetime = true, ClockSkew = TimeSpan.Zero // 关闭时钟偏差容忍,严格验证过期时间 }; // 验证Token并得到用户身份信息 ClaimsPrincipal principal = tokenHandler.ValidateToken(token, validationParams, out _); // 3. 将用户身份设置到请求上下文,这样控制器里就能通过User访问了 actionContext.RequestContext.Principal = principal; return true; } catch (SecurityTokenExpiredException) { actionContext.Response = actionContext.Request.CreateResponse(HttpStatusCode.Unauthorized, "Token已过期"); return false; } catch (SecurityTokenInvalidSignatureException) { actionContext.Response = actionContext.Request.CreateResponse(HttpStatusCode.Unauthorized, "Token签名无效"); return false; } catch (Exception ex) { actionContext.Response = actionContext.Request.CreateResponse(HttpStatusCode.Unauthorized, $"Token验证失败:{ex.Message}"); return false; } } }
第三步:配置并使用过滤器
现在有两种方式用这个过滤器:
全局注册(所有API都生效)
在WebApiConfig.cs里注册全局过滤器,这样所有控制器都会自动应用这个认证逻辑:
using System.Web.Http; public static class WebApiConfig { public static void Register(HttpConfiguration config) { // 添加自定义认证过滤器 config.Filters.Add(new CustomTokenAuthorizeAttribute()); // 其他WebApi路由配置... config.MapHttpAttributeRoutes(); config.Routes.MapHttpRoute( name: "DefaultApi", routeTemplate: "api/{controller}/{id}", defaults: new { id = RouteParameter.Optional } ); } }
局部使用(指定控制器/方法生效)
如果不想全局生效,就在需要认证的控制器或方法上贴这个特性:
[CustomTokenAuthorize] public class OrdersController : ApiController { public IHttpActionResult GetMyOrders() { // 这里就能像用Identity那样访问User的信息了 var userId = User.Identity.GetUserId(); var userName = User.Identity.Name; var isAdmin = User.IsInRole("Admin"); return Ok(new { UserId = userId, UserName = userName, IsAdmin = isAdmin }); } }
一些关键注意事项
- 密钥保密:绝对不能把签名密钥硬编码到代码里,建议放到
Web.config的<appSettings>或者专门的密钥管理服务中。 - Token有效期:根据业务场景调整过期时间,比如短时间有效期+刷新Token机制,避免Token泄露后被滥用。
- 自定义Claim:如果需要携带更多用户信息(比如邮箱、部门),可以在生成Token时添加自定义Claim,解析后通过
User.Claims.FirstOrDefault(c => c.Type == "YourCustomClaim")?.Value获取。
内容的提问来源于stack exchange,提问作者RezaNoei
相关产品推荐
相关产品推荐

