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

如何在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);
    }
}
第二步:编写自定义Authorize过滤器

接下来要写一个继承自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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.20 12:29:07