如何在.NET 8 Blazor Server项目中同时实现JWT与Cookie认证?
你的项目已配置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. 测试验证
- 用Postman或类似工具发送POST请求到
/api/auth/login,传入正确的用户名密码,获取返回的Token。 - 访问受保护的API时,在请求头中添加
Authorization: Bearer {你的Token},即可正常访问。
内容的提问来源于stack exchange,提问作者Mohsen Saleh
相关产品推荐
相关产品推荐

