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

自定义ASP.NET JWT认证:自行校验Token并解决c.Token为空问题

自定义ASP.NET JWT认证与授权流程(自行校验Token)

需求概述

  • 自定义实现ASP.NET应用的JWT认证与授权(包含角色功能)
  • 不依赖ASP.NET内置的Token校验逻辑,完全自行实现Token有效性校验
  • 校验完成后告知ASP.NET认证是否成功,并注入角色等Claim信息

期望步骤

  1. 实现认证与授权流程
  2. 从请求中获取JWT Token
  3. 自行编写Token有效性校验逻辑
  4. 根据校验结果通知ASP.NET认证状态,并构建包含角色的ClaimsPrincipal

已尝试方案(存在问题)

用户尝试使用AddJwtBearer并通过OnMessageReceived事件自定义逻辑,但c.Token始终为空:

builder.AddJwtBearer(o =>
{
    // JWT Validation
    o.TokenValidationParameters = new TokenValidationParameters
    {
        ValidateAudience = false,
        ValidateIssuer = true,
        ValidIssuers = new[] { "http://localhost:8080/realms/GT" },
        ValidateIssuerSigningKey = true,
        IssuerSigningKey = BuildRSAKey(publicKeyJWT),
        ValidateLifetime = true
    };

    o.Events = new JwtBearerEvents()
    {
        OnMessageReceived = async c =>
        {
            if (c.Token == null)
            {
                c.NoResult();

                c.Response.StatusCode = 500;
                c.Response.ContentType = "text/plain";
                await c.Response.WriteAsync("No token was provided");

                return;
            }

            // custom logic to check token

            // if true then Success
            // else fail

            BuildClaims(c.Principal);  // roles claims included
            return;
        }
    };
});

问题点

OnMessageReceived事件中c.Token为空,无法获取到请求中的JWT Token进行自定义校验。


解决方案

方案一:修正OnMessageReceived事件,手动提取Token

OnMessageReceived中c.Token为空是因为默认逻辑还未提取Token,你可以手动从请求头(或其他自定义位置)提取Token:

builder.AddJwtBearer(o =>
{
    // 禁用所有内置Token校验,避免与自定义逻辑冲突
    o.TokenValidationParameters = new TokenValidationParameters
    {
        ValidateIssuer = false,
        ValidateAudience = false,
        ValidateIssuerSigningKey = false,
        ValidateLifetime = false
    };

    o.Events = new JwtBearerEvents()
    {
        OnMessageReceived = async c =>
        {
            // 从Authorization头提取Bearer Token
            var authHeader = c.Request.Headers.Authorization.FirstOrDefault();
            if (string.IsNullOrEmpty(authHeader) || !authHeader.StartsWith("Bearer "))
            {
                c.NoResult();
                c.Response.StatusCode = StatusCodes.Status401Unauthorized;
                await c.Response.WriteAsync("Invalid or missing authorization header");
                return;
            }

            // 提取Token核心部分
            var token = authHeader.Substring("Bearer ".Length).Trim();
            if (string.IsNullOrEmpty(token))
            {
                c.NoResult();
                c.Response.StatusCode = StatusCodes.Status401Unauthorized;
                await c.Response.WriteAsync("No token was provided");
                return;
            }

            // 执行自定义Token校验逻辑
            bool isTokenValid = ValidateTokenCustomLogic(token);
            if (!isTokenValid)
            {
                c.NoResult();
                c.Response.StatusCode = StatusCodes.Status401Unauthorized;
                await c.Response.WriteAsync("Invalid token");
                return;
            }

            // 构建包含角色的自定义ClaimsPrincipal
            var principal = BuildCustomClaimsPrincipal(token);
            c.Principal = principal;
            c.Success(); // 通知ASP.NET认证成功
        }
    };
});

// 自定义校验方法示例
private bool ValidateTokenCustomLogic(string token)
{
    // 在这里实现你的Token校验逻辑:比如签名验证、有效期检查、Issuer校验等
    // 示例:解析Token并校验Issuer
    var handler = new JwtSecurityTokenHandler();
    var jwtToken = handler.ReadJwtToken(token);
    return jwtToken.Issuer == "http://localhost:8080/realms/GT" && jwtToken.ValidTo > DateTime.UtcNow;
}

// 构建ClaimsPrincipal示例(包含角色)
private ClaimsPrincipal BuildCustomClaimsPrincipal(string token)
{
    var jwtToken = new JwtSecurityTokenHandler().ReadJwtToken(token);
    var claims = jwtToken.Claims.ToList();
    // 手动添加角色Claim(如果Token中没有的话)
    claims.Add(new Claim(ClaimTypes.Role, "Admin"));
    
    var identity = new ClaimsIdentity(claims, "CustomJwt");
    return new ClaimsPrincipal(identity);
}

方案二:完全自定义认证Handler(更灵活)

如果需要彻底脱离JwtBearer中间件的默认逻辑,可以实现IAuthenticationHandler接口,完全掌控认证流程:

  1. 创建自定义认证Handler:
public class CustomJwtAuthenticationHandler : AuthenticationHandler<AuthenticationSchemeOptions>
{
    public CustomJwtAuthenticationHandler(IOptionsMonitor<AuthenticationSchemeOptions> options, ILoggerFactory logger, UrlEncoder encoder, ISystemClock clock)
        : base(options, logger, encoder, clock)
    {
    }

    protected override async Task<AuthenticateResult> HandleAuthenticateAsync()
    {
        // 1. 从请求中提取Token
        var authHeader = Request.Headers.Authorization.FirstOrDefault();
        if (string.IsNullOrEmpty(authHeader) || !authHeader.StartsWith("Bearer "))
        {
            return AuthenticateResult.Fail("Missing or invalid authorization header");
        }
        var token = authHeader.Substring("Bearer ".Length).Trim();

        // 2. 执行自定义Token校验逻辑
        bool isValid = await ValidateTokenAsync(token);
        if (!isValid)
        {
            return AuthenticateResult.Fail("Invalid token");
        }

        // 3. 构建包含角色的ClaimsPrincipal
        var jwtToken = new JwtSecurityTokenHandler().ReadJwtToken(token);
        var claims = jwtToken.Claims.ToList();
        // 添加自定义角色Claim(按需调整)
        claims.Add(new Claim(ClaimTypes.Role, "Admin"));
        
        var identity = new ClaimsIdentity(claims, Scheme.Name);
        var principal = new ClaimsPrincipal(identity);
        var ticket = new AuthenticationTicket(principal, Scheme.Name);

        // 4. 返回认证成功结果
        return AuthenticateResult.Success(ticket);
    }

    private Task<bool> ValidateTokenAsync(string token)
    {
        // 在这里实现完整的自定义校验逻辑:签名、有效期、Issuer/Audience等
        try
        {
            var jwtToken = new JwtSecurityTokenHandler().ReadJwtToken(token);
            return Task.FromResult(
                jwtToken.Issuer == "http://localhost:8080/realms/GT" && 
                jwtToken.ValidTo > DateTime.UtcNow &&
                // 其他校验规则...
                true);
        }
        catch
        {
            return Task.FromResult(false);
        }
    }
}
  1. 注册自定义认证方案:
builder.Services.AddAuthentication("CustomJwt")
    .AddScheme<AuthenticationSchemeOptions, CustomJwtAuthenticationHandler>("CustomJwt", options => { });

// 配置授权策略(比如基于角色的授权)
builder.Services.AddAuthorization(options =>
{
    options.AddPolicy("AdminOnly", policy => policy.RequireRole("Admin"));
});
  1. 启用认证和授权中间件:
app.UseAuthentication();
app.UseAuthorization();

这种方式完全独立于ASP.NET内置的JWT校验逻辑,适合复杂的自定义认证需求。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.24 13:25:16