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

ASP.NET Core WebAPI实现Cookie与JWT并行认证可行性咨询

当然可以!ASP.NET Core的认证系统天生支持多方案共存,完全能满足你单个端点同时兼容Cookie和JWT认证的需求,而且不需要复杂的自定义中间件,用内置机制就能搞定。下面我一步步给你讲具体实现方式:

核心思路

ASP.NET Core允许你注册多种认证方案(比如JWT Bearer、Cookie),然后通过授权策略指定某个端点可以接受任意一种方案的认证结果。这样客户端无论是发送Cookie还是JWT Bearer令牌,API都能识别并完成认证。

具体实现步骤

1. 注册两种认证方案

在Program.cs中,同时配置JWT Bearer和Cookie认证,给每种方案分配唯一的标识(默认标识分别是JwtBearerDefaults.AuthenticationScheme和CookieAuthenticationDefaults.AuthenticationScheme):

var builder = WebApplication.CreateBuilder(args);

// 1. 配置JWT Bearer认证(保留你原有的逻辑)
builder.Services.AddAuthentication()
    .AddJwtBearer(JwtBearerDefaults.AuthenticationScheme, options =>
    {
        options.TokenValidationParameters = new TokenValidationParameters
        {
            ValidateIssuer = true,
            ValidateAudience = true,
            ValidateLifetime = true,
            ValidateIssuerSigningKey = true,
            ValidIssuer = builder.Configuration["Jwt:Issuer"],
            ValidAudience = builder.Configuration["Jwt:Audience"],
            IssuerSigningKey = new SymmetricSecurityKey(
                Encoding.UTF8.GetBytes(builder.Configuration["Jwt:Key"]))
        };
    })
    // 2. 配置Cookie认证(新增)
    .AddCookie(CookieAuthenticationDefaults.AuthenticationScheme, options =>
    {
        options.Cookie.Name = "MyApp.Auth";
        options.Cookie.HttpOnly = true; // 禁止前端JS读取,提升安全性
        options.Cookie.SecurePolicy = CookieSecurePolicy.Always; // 生产环境强制HTTPS传输
        options.Cookie.SameSite = SameSiteMode.Lax; // 同域场景下足够安全
        options.ExpireTimeSpan = TimeSpan.FromHours(8); // 和JWT过期时间保持一致
        // API不需要重定向到登录页,所以设置自定义的未授权路径
        options.LoginPath = "/api/auth/unauthorized";
        options.AccessDeniedPath = "/api/auth/forbidden";
    });

// 3. 设置默认授权策略:允许通过任意一种认证方案通过授权
builder.Services.AddAuthorization(options =>
{
    options.DefaultPolicy = new AuthorizationPolicyBuilder(
        JwtBearerDefaults.AuthenticationScheme,
        CookieAuthenticationDefaults.AuthenticationScheme)
        .RequireAuthenticatedUser()
        .Build();
});

// 添加其他服务(比如Controllers)
builder.Services.AddControllers();

var app = builder.Build();

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

app.MapControllers();

app.Run();

2. 修改登录接口,同时生成JWT和Cookie

你的登录接口需要同时返回JWT令牌(给移动端/服务器端)和Set-Cookie响应头(给SPA),这样不同客户端可以按需使用:

[ApiController]
[Route("api/auth/jwt")]
public class AuthController : ControllerBase
{
    private readonly IConfiguration _configuration;

    public AuthController(IConfiguration configuration)
    {
        _configuration = configuration;
    }

    [HttpPost("login")]
    public async Task<IActionResult> Login([FromBody] LoginRequest request)
    {
        // 1. 验证用户逻辑(替换成你实际的用户校验代码)
        var user = ValidateUser(request.Username, request.Password);
        if (user == null)
        {
            return Unauthorized("用户名或密码错误");
        }

        // 2. 构建用户Claims
        var claims = new List<Claim>
        {
            new Claim(ClaimTypes.NameIdentifier, user.Id.ToString()),
            new Claim(ClaimTypes.Name, user.Username),
            // 按需添加其他Claims(比如角色、权限)
        };

        // 3. 生成JWT令牌(给非浏览器客户端)
        var jwtKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_configuration["Jwt:Key"]));
        var jwtCreds = new SigningCredentials(jwtKey, SecurityAlgorithms.HmacSha256);
        var jwtToken = new JwtSecurityToken(
            issuer: _configuration["Jwt:Issuer"],
            audience: _configuration["Jwt:Audience"],
            claims: claims,
            expires: DateTime.UtcNow.AddHours(8),
            signingCredentials: jwtCreds);
        var jwtTokenString = new JwtSecurityTokenHandler().WriteToken(jwtToken);

        // 4. 生成Cookie认证票据(给SPA)
        var claimsIdentity = new ClaimsIdentity(claims, CookieAuthenticationDefaults.AuthenticationScheme);
        var authProps = new AuthenticationProperties
        {
            IsPersistent = request.RememberMe, // 是否持久化Cookie
            ExpiresUtc = DateTimeOffset.UtcNow.AddHours(8)
        };
        await HttpContext.SignInAsync(
            CookieAuthenticationDefaults.AuthenticationScheme,
            new ClaimsPrincipal(claimsIdentity),
            authProps);

        // 5. 返回JWT令牌,浏览器会自动保存Set-Cookie头中的Cookie
        return Ok(new { Token = jwtTokenString });
    }

    [HttpPost("logout")]
    [Authorize]
    public async Task<IActionResult> Logout()
    {
        // 清除Cookie认证
        await HttpContext.SignOutAsync(CookieAuthenticationDefaults.AuthenticationScheme);
        // JWT令牌由客户端自行清除(比如移动端删除本地存储的令牌)
        return Ok("退出成功");
    }

    // 模拟用户校验逻辑
    private User ValidateUser(string username, string password)
    {
        // 实际场景中替换为数据库查询
        if (username == "admin" && password == "123456")
        {
            return new User { Id = 1, Username = "admin" };
        }
        return null;
    }
}

// 登录请求模型
public class LoginRequest
{
    public string Username { get; set; }
    public string Password { get; set; }
    public bool RememberMe { get; set; } = false;
}

// 用户模型(示例)
public class User
{
    public int Id { get; set; }
    public string Username { get; set; }
}

3. 验证效果

  • SPA客户端:登录后浏览器会自动保存认证Cookie,后续请求无需手动添加Authorization头,浏览器会自动携带Cookie完成认证。
  • 移动端/服务器端:登录后获取JWT令牌,在请求头中携带Authorization: Bearer <token>即可完成认证。
  • 所有控制器端点都会自动支持两种认证方式,无需单独添加[Authorize(AuthenticationSchemes = "...")]属性(因为我们配置了默认授权策略)。
关键注意事项
  • 中间件顺序:必须保证UseAuthentication()在UseAuthorization()之前执行,否则认证逻辑不会生效。
  • Cookie安全设置:生产环境务必开启HttpOnly和Secure属性,防止XSS攻击和明文传输。
  • 跨域配置:如果有跨域客户端(比如其他域名的SPA),需要在CORS配置中开启AllowCredentials = true,并允许对应的Origin。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.21 04:01:43