Blazor Server中JWT认证实现:结合内置特性管理认证状态
一、配置认证服务(Program.cs)
首先在Program.cs中配置认证体系,结合Cookie认证维护服务器端会话,同时添加JWT验证支持(用于校验API返回的令牌):
builder.Services.AddAuthentication(options => { options.DefaultAuthenticateScheme = CookieAuthenticationDefaults.AuthenticationScheme; options.DefaultSignInScheme = CookieAuthenticationDefaults.AuthenticationScheme; options.DefaultChallengeScheme = "JwtBearer"; }) .AddCookie(options => { options.Cookie.HttpOnly = true; options.Cookie.SecurePolicy = CookieSecurePolicy.Always; // 生产环境强制HTTPS options.ExpireTimeSpan = TimeSpan.FromHours(8); options.LoginPath = "/Login"; // 未认证时自动跳转登录页 }) .AddJwtBearer("JwtBearer", options => { options.TokenValidationParameters = new TokenValidationParameters { ValidateIssuer = true, ValidateAudience = true, ValidateLifetime = true, ValidateIssuerSigningKey = true, ValidIssuer = builder.Configuration["Jwt:Issuer"], // 与API配置一致 ValidAudience = builder.Configuration["Jwt:Audience"], // 与API配置一致 IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(builder.Configuration["Jwt:SecretKey"])) }; }); // 启用授权服务 builder.Services.AddAuthorization(); // 注入HttpContextAccessor用于操作认证会话 builder.Services.AddHttpContextAccessor();
确保中间件顺序正确:
app.UseAuthentication(); app.UseAuthorization();
二、实现登录逻辑,将JWT转换为服务器端认证会话
创建Login.razor组件,调用API获取JWT后,验证令牌并生成服务器端Cookie会话:
@inject HttpClient Http @inject IHttpContextAccessor HttpContextAccessor @inject IConfiguration Configuration @inject NavigationManager NavigationManager <h3>登录</h3> <form @onsubmit="HandleLogin"> <div> <label>用户名:</label> <input type="text" @bind="Username" required /> </div> <div> <label>密码:</label> <input type="password" @bind="Password" required /> </div> <button type="submit">登录</button> @if (!string.IsNullOrEmpty(ErrorMsg)) { <p style="color:red">@ErrorMsg</p> } </form> @code { private string Username { get; set; } private string Password { get; set; } private string ErrorMsg { get; set; } private async Task HandleLogin() { ErrorMsg = string.Empty; try { // 调用API获取令牌 var loginDto = new { Username = Username, Password = Password }; var response = await Http.PostAsJsonAsync($"{Configuration["ApiBaseUrl"]}/auth/login", loginDto); if (!response.IsSuccessStatusCode) { ErrorMsg = await response.Content.ReadAsStringAsync(); return; } var tokenRes = await response.Content.ReadFromJsonAsync<TokenResponse>(); var tokenHandler = new JwtSecurityTokenHandler(); var validationParams = new TokenValidationParameters { ValidateIssuer = true, ValidateAudience = true, ValidateLifetime = true, ValidateIssuerSigningKey = true, ValidIssuer = Configuration["Jwt:Issuer"], ValidAudience = Configuration["Jwt:Audience"], IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(Configuration["Jwt:SecretKey"])) }; // 验证令牌并生成ClaimsPrincipal var principal = tokenHandler.ValidateToken(tokenRes.AccessToken, validationParams, out _); // 创建服务器端认证会话,持久化到Cookie await HttpContextAccessor.HttpContext.SignInAsync( CookieAuthenticationDefaults.AuthenticationScheme, principal, new AuthenticationProperties { IsPersistent = true, ExpiresUtc = DateTimeOffset.UtcNow.AddHours(8) }); // 跳转到授权页面 NavigationManager.NavigateTo("/"); } catch (Exception ex) { ErrorMsg = ex.Message; } } private class TokenResponse { public string AccessToken { get; set; } public string RefreshToken { get; set; } } }
三、直接使用[Authorize]特性
现在可以在任意组件或页面上添加@attribute [Authorize],Blazor Server会自动识别服务器端的认证状态:
@attribute [Microsoft.AspNetCore.Authorization.Authorize] <h1>用户专属页面</h1> <p>只有已登录用户才能访问此内容</p> <p>当前用户:@context.User.Identity.Name</p>
四、安全管理令牌的关键要点
- 避免客户端存储JWT:不要把JWT存在localStorage或sessionStorage,依赖服务器端HttpOnly Cookie维护会话,防止XSS攻击。
- 令牌刷新机制:如果API提供刷新令牌,可在服务器端将刷新令牌存储到数据库/缓存,当会话即将过期时自动调用API刷新令牌,更新会话。
- 权限控制:利用JWT中的Claims(如角色、权限),结合
[Authorize(Roles = "Admin")]或自定义授权策略实现细粒度权限控制。
五、注销逻辑实现
创建注销组件,清除服务器端的认证会话:
@inject IHttpContextAccessor HttpContextAccessor @inject NavigationManager NavigationManager @inject HttpClient Http @inject IConfiguration Configuration <button @onclick="HandleLogout" style="cursor:pointer">注销</button> @code { private async Task HandleLogout() { // 可选:调用API的注销接口,失效刷新令牌 // await Http.PostAsync($"{Configuration["ApiBaseUrl"]}/auth/logout", null); // 清除服务器端认证Cookie await HttpContextAccessor.HttpContext.SignOutAsync(CookieAuthenticationDefaults.AuthenticationScheme); NavigationManager.NavigateTo("/Login"); } }
内容的提问来源于stack exchange,提问作者Shervin Ivari
相关产品推荐
相关产品推荐

