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

是否应使用[Authorize]特性?JWT会话存储下的授权疑问

如何让ASP.NET的[Authorize]特性支持从Session读取JWT令牌

问题根源

默认的JWT认证方案只会从Authorization请求头(Bearer格式)读取令牌,而浏览器直接输入地址发起的GET请求不会自动携带这个头,所以用[Authorize]会导致认证失败,跳转到登录页或者返回401。

正确解决方案:自定义认证方案,保留[Authorize]特性

你应该继续使用[Authorize]特性,但是需要扩展ASP.NET的认证体系,让它支持从Session中读取并验证JWT令牌。这样既符合框架的设计规范,又能利用其内置的授权功能(比如角色、声明校验),避免手动校验的重复工作。

步骤1:创建自定义认证处理程序

实现一个继承自AuthenticationHandler<AuthenticationSchemeOptions>的处理类,负责从Session读取JWT并完成验证:

public class SessionJwtAuthenticationHandler : AuthenticationHandler<AuthenticationSchemeOptions>
{
    private readonly IHttpContextAccessor _httpContextAccessor;
    private readonly TokenValidationParameters _tokenValidationParams;

    public SessionJwtAuthenticationHandler(
        IOptionsMonitor<AuthenticationSchemeOptions> options,
        ILoggerFactory logger,
        UrlEncoder encoder,
        ISystemClock clock,
        IHttpContextAccessor httpContextAccessor,
        IOptions<TokenValidationParameters> tokenValidationOptions)
        : base(options, logger, encoder, clock)
    {
        _httpContextAccessor = httpContextAccessor;
        _tokenValidationParams = tokenValidationOptions.Value;
    }

    protected override async Task<AuthenticateResult> HandleAuthenticateAsync()
    {
        var context = _httpContextAccessor.HttpContext;
        if (context == null)
            return AuthenticateResult.Fail("HttpContext unavailable");

        // 从Session获取存储的JWT令牌
        var jwtToken = context.Session.GetString("jwt-token");
        if (string.IsNullOrEmpty(jwtToken))
            return AuthenticateResult.NoResult(); // 无令牌,交由后续认证方案处理或返回未授权

        try
        {
            // 验证JWT有效性
            var tokenHandler = new JwtSecurityTokenHandler();
            var principal = tokenHandler.ValidateToken(jwtToken, _tokenValidationParams, out var validatedToken);
            
            // 创建认证票据
            var identity = new ClaimsIdentity(principal.Claims, Scheme.Name);
            var authTicket = new AuthenticationTicket(new ClaimsPrincipal(identity), Scheme.Name);
            
            return AuthenticateResult.Success(authTicket);
        }
        catch (Exception ex)
        {
            return AuthenticateResult.Fail($"Invalid JWT token: {ex.Message}");
        }
    }
}

步骤2:配置认证服务和Session

在Program.cs中注册Session服务、自定义认证方案,并配置JWT验证参数:

// 注册Session服务
builder.Services.AddSession(options =>
{
    options.IdleTimeout = TimeSpan.FromMinutes(30);
    options.Cookie.HttpOnly = true;
    options.Cookie.IsEssential = true;
});

// 配置JWT验证参数
builder.Services.Configure<TokenValidationParameters>(options =>
{
    options.ValidateIssuer = true;
    options.ValidateAudience = true;
    options.ValidateLifetime = true;
    options.ValidateIssuerSigningKey = true;
    options.ValidIssuer = builder.Configuration["Jwt:Issuer"];
    options.ValidAudience = builder.Configuration["Jwt:Audience"];
    options.IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(builder.Configuration["Jwt:Key"]));
});

// 配置认证体系
builder.Services.AddAuthentication(options =>
{
    // 设置默认认证方案为自定义的SessionJwt方案
    options.DefaultAuthenticateScheme = "SessionJwt";
    options.DefaultChallengeScheme = "SessionJwt";
})
// 添加自定义SessionJwt认证方案
.AddScheme<AuthenticationSchemeOptions, SessionJwtAuthenticationHandler>("SessionJwt", _ => { })
// 可选:保留JWTBearer方案,支持API请求的Bearer令牌
.AddJwtBearer("JwtBearer", options =>
{
    options.TokenValidationParameters = builder.Configuration.GetSection("Jwt").Get<TokenValidationParameters>()!;
});

builder.Services.AddAuthorization();

// 中间件顺序很重要:先Session,再认证,再授权
app.UseSession();
app.UseAuthentication();
app.UseAuthorization();

// 现在可以直接使用[Authorize]特性
app.MapGet("/home", [Authorize] () => {
    return Results.Content("<h1>Welcome to Home Page</h1>", "text/html");
});

为什么不推荐手动校验Session?

手动校验Session中的JWT虽然能实现功能,但存在以下问题:

  • 重复造轮子:框架已经提供了成熟的认证授权体系,手动校验需要重复实现令牌验证、Claims解析等逻辑。
  • 无法利用框架特性:比如[Authorize(Roles = "Admin")]、[Authorize(Policy = "RequireEmailVerified")]这类声明/角色/策略授权,手动校验需要额外处理。
  • 一致性问题:如果后续需要支持多种认证方式(比如API的Bearer令牌、第三方登录),手动校验会导致逻辑分散,难以维护。

扩展:同时支持Session和Bearer令牌

如果你的应用同时需要支持浏览器Session登录和API的Bearer令牌登录,可以在[Authorize]特性中指定多个认证方案:

app.MapGet("/home", [Authorize(AuthenticationSchemes = "SessionJwt,JwtBearer")] () => {
    return Results.Content("<h1>Welcome to Home Page</h1>", "text/html");
});

这样无论是从Session读取JWT,还是从Authorization头读取Bearer令牌,都能通过认证。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.23 06:36:07