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

如何在.NET 8 Blazor Server项目中同时实现JWT与Cookie认证?

在Blazor Server项目中同时支持Cookie认证(Blazor端)与JWT认证(Mobile API)的实现方案

你的项目已配置ASP.NET Identity的Cookie认证用于Blazor Server,要同时支持移动端API的JWT认证,只需在现有基础上添加JWT认证配置、生成Token的接口,并指定API端点使用JWT认证即可,具体步骤如下:

1. 添加JWT配置参数

首先在appsettings.json中添加JWT相关配置(生产环境建议用环境变量或密钥管理工具存储敏感信息):

"JwtSettings": {
  "Issuer": "YourAppIssuer", // 应用颁发者标识,比如域名
  "Audience": "MobileAppClient", // 目标受众(移动端APP)
  "SecretKey": "YourStrongSecretKeyAtLeast16Characters", // 加密密钥,长度不少于16位
  "ExpirationMinutes": 60 // Token有效期(分钟)
}

2. 修改认证服务配置

在Program.cs中,修改现有AddAuthentication配置,添加JWT Bearer认证支持:

using System.Text;
using Microsoft.IdentityModel.Tokens;

// ... 其他现有代码 ...

builder.Services.AddAuthentication(options =>
{
    // 保留Blazor Server默认使用Cookie认证
    options.DefaultScheme = IdentityConstants.ApplicationScheme;
    options.DefaultSignInScheme = IdentityConstants.ApplicationScheme;
})
.AddIdentityCookies() // 保留原有的Identity Cookie认证
.AddJwtBearer(options =>
{
    var jwtSettings = builder.Configuration.GetSection("JwtSettings");
    
    options.TokenValidationParameters = new TokenValidationParameters
    {
        ValidateIssuer = true,
        ValidateAudience = true,
        ValidateLifetime = true,
        ValidateIssuerSigningKey = true,
        ValidIssuer = jwtSettings["Issuer"],
        ValidAudience = jwtSettings["Audience"],
        IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(jwtSettings["SecretKey"]))
    };
});

// ... 原有的AddIdentityCore配置保持不变 ...

3. 实现JWT Token生成接口

添加一个Minimal API接口,供移动端调用获取Token,验证用户密码后生成并返回JWT:

// 定义登录请求模型
public class LoginRequest
{
    public string Username { get; set; }
    public string Password { get; set; }
}

// 在Program.cs的app映射部分添加登录接口
app.MapPost("/api/auth/login", async (LoginRequest request, 
    SignInManager<User> signInManager, 
    IConfiguration config) =>
{
    // 验证用户密码
    var signInResult = await signInManager.PasswordSignInAsync(
        request.Username, 
        request.Password, 
        isPersistent: false, 
        lockoutOnFailure: false);

    if (!signInResult.Succeeded)
    {
        return Results.Unauthorized();
    }

    // 获取用户信息及角色
    var user = await signInManager.UserManager.FindByNameAsync(request.Username);
    var userRoles = await signInManager.UserManager.GetRolesAsync(user);

    // 构建JWT声明
    var claims = new List<Claim>
    {
        new Claim(ClaimTypes.NameIdentifier, user.Id),
        new Claim(ClaimTypes.Name, user.UserName),
        new Claim(ClaimTypes.Email, user.Email)
    };
    // 添加角色声明
    claims.AddRange(userRoles.Select(role => new Claim(ClaimTypes.Role, role)));

    // 生成Token
    var jwtSettings = config.GetSection("JwtSettings");
    var secretKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(jwtSettings["SecretKey"]));
    var signingCredentials = new SigningCredentials(secretKey, SecurityAlgorithms.HmacSha256);

    var jwtToken = new JwtSecurityToken(
        issuer: jwtSettings["Issuer"],
        audience: jwtSettings["Audience"],
        claims: claims,
        expires: DateTime.UtcNow.AddMinutes(Convert.ToDouble(jwtSettings["ExpirationMinutes"])),
        signingCredentials: signingCredentials
    );

    return Results.Ok(new 
    { 
        Token = new JwtSecurityTokenHandler().WriteToken(jwtToken),
        ExpiresAt = jwtToken.ValidTo
    });
})
.AllowAnonymous(); // 登录接口允许匿名访问

4. 保护API端点,指定使用JWT认证

对于需要保护的API端点,通过RequireAuthorization指定使用JWT认证方案:

app.MapGet("/api/protected/data", (HttpContext context) =>
{
    var userId = context.User.FindFirstValue(ClaimTypes.NameIdentifier);
    var userName = context.User.FindFirstValue(ClaimTypes.Name);
    return Results.Ok(new 
    { 
        Message = "这是受JWT保护的API数据",
        UserId = userId,
        UserName = userName
    });
})
.RequireAuthorization(options =>
{
    options.AuthenticationSchemes.Add(JwtBearerDefaults.AuthenticationScheme);
});

如果希望所有API端点默认使用JWT认证,可以配置全局授权策略:

builder.Services.AddAuthorization(options =>
{
    // API默认使用JWT认证策略
    options.DefaultPolicy = new AuthorizationPolicyBuilder(
        JwtBearerDefaults.AuthenticationScheme)
        .RequireAuthenticatedUser()
        .Build();

    // Blazor页面保留使用Cookie认证的策略
    options.AddPolicy("BlazorCookiePolicy", new AuthorizationPolicyBuilder(
        IdentityConstants.ApplicationScheme)
        .RequireAuthenticatedUser()
        .Build());
});

之后Blazor页面只需标注[Authorize(Policy = "BlazorCookiePolicy")]即可继续使用Cookie认证,API端点直接用[Authorize]就会默认使用JWT。

5. 确保中间件顺序正确

在Program.cs中,中间件的顺序必须遵循以下规则:

app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();

// 认证中间件必须在授权中间件之前
app.UseAuthentication();
app.UseAuthorization();

// 先映射API端点
app.MapPost("/api/auth/login", ...);
app.MapGet("/api/protected/data", ...);

// 再映射Blazor相关端点
app.MapBlazorHub();
app.MapFallbackToPage("/_Host");

6. 测试验证

  1. 用Postman或类似工具发送POST请求到/api/auth/login,传入正确的用户名密码,获取返回的Token。
  2. 访问受保护的API时,在请求头中添加Authorization: Bearer {你的Token},即可正常访问。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.25 05:25:56