Blazor WASM的正确认证与授权方式是什么?
Blazor WASM的正确认证与授权方式是什么?
嘿,刚好我最近也在折腾Blazor WASM的认证授权,结合你提到的用HttpOnly Cookie存储JWT的场景,来分享一套靠谱的实现方案,完全贴合你的需求:
一、API端:正确返回HttpOnly Cookie存储JWT
你已经选对了方向——用HttpOnly Cookie存JWT确实比localStorage安全太多,能直接避免XSS窃取的风险。你的Login接口可以这样完善:
[AllowAnonymous] [HttpPost("Login")] public IActionResult Login([FromBody] AccountLoginModel model) { if (!ModelState.IsValid) return BadRequest(ModelState); var user = Authenticate(model); if (user == null) return NotFound("用户名或密码错误"); var token = GenerateToken(user); // 配置安全的Cookie参数,这几个属性缺一不可 HttpContext.Response.Cookies.Append( "AuthToken", token, new CookieOptions { HttpOnly = true, // 禁止JS读取,防XSS Secure = Environment.IsProduction(), // 生产环境必须开启HTTPS才会发送Cookie SameSite = SameSiteMode.Lax, // 防CSRF攻击 Expires = DateTime.UtcNow.AddHours(1), // 设置Token过期时间 Path = "/" // 确保整个站点都能访问该Cookie }); return Ok(new { UserName = user.UserName, Email = user.Email }); }
同时API端要配置JWT验证,并且从Cookie中读取Token:
// 在Program.cs中配置认证服务 builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme) .AddJwtBearer(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"])) }; // 核心:从请求Cookie中获取JWT Token options.Events = new JwtBearerEvents { OnMessageReceived = context => { context.Token = context.Request.Cookies["AuthToken"]; return Task.CompletedTask; } }; }); // 别忘了开启授权中间件 app.UseAuthentication(); app.UseAuthorization();
另外CORS配置必须允许携带凭据,否则Blazor客户端的请求不会带上Cookie:
builder.Services.AddCors(options => { options.AddPolicy("BlazorClientCors", policy => { policy.WithOrigins("https://your-blazor-client-domain.com") // 替换成你的客户端地址 .AllowAnyHeader() .AllowAnyMethod() .AllowCredentials(); // 这个是关键,允许Cookie跨域传递 }); }); // 在中间件中启用CORS app.UseCors("BlazorClientCors");
二、Blazor WASM端:处理认证状态与请求凭据
Blazor WASM是纯客户端应用,默认不会自动在请求中带上Cookie,也无法直接读取HttpOnly Cookie,所以需要做以下配置:
- 配置HttpClient携带凭据
在客户端的Program.cs中,给HttpClient加上允许携带凭据的配置:
builder.Services.AddScoped(sp => new HttpClient { BaseAddress = new Uri(builder.HostEnvironment.BaseAddress) }).AddHttpMessageHandler<BaseAddressAuthorizationMessageHandler>(); // 开启授权服务 builder.Services.AddAuthorizationCore(); // 添加自定义认证状态提供者 builder.Services.AddScoped<AuthenticationStateProvider, CustomAuthStateProvider>();
- 自定义AuthenticationStateProvider
因为客户端拿不到HttpOnly Cookie,所以需要调用API的一个端点(比如/api/Account/Me)来获取当前用户的身份信息,以此构建认证状态:
public class CustomAuthStateProvider : AuthenticationStateProvider { private readonly HttpClient _httpClient; public CustomAuthStateProvider(HttpClient httpClient) { _httpClient = httpClient; } public override async Task<AuthenticationState> GetAuthenticationStateAsync() { try { // 调用API获取当前用户信息 var response = await _httpClient.GetFromJsonAsync<UserInfoDto>("api/Account/Me"); if (response != null) { // 构建用户Claims var claims = new List<Claim> { new Claim(ClaimTypes.Name, response.UserName), new Claim(ClaimTypes.Email, response.Email) // 可以添加更多角色、权限等Claims }; var identity = new ClaimsIdentity(claims, "CookieAuth"); var user = new ClaimsPrincipal(identity); return new AuthenticationState(user); } } catch (HttpRequestException) { // 如果请求失败(比如Token过期),返回未认证状态 } return new AuthenticationState(new ClaimsPrincipal(new ClaimsIdentity())); } // 提供一个方法手动刷新认证状态,比如登录/登出后调用 public void NotifyAuthenticationStateChanged() { NotifyAuthenticationStateChanged(GetAuthenticationStateAsync()); } }
对应的API端/Me端点:
[HttpGet("Me")] [Authorize] public IActionResult GetCurrentUser() { var user = User; return Ok(new UserInfoDto { UserName = user.Identity.Name, Email = user.FindFirstValue(ClaimTypes.Email) }); }
- 在组件中使用授权
现在你可以在Blazor页面/组件中正常使用授权功能了:
- 用
<AuthorizeView>组件根据认证状态显示不同内容:
<AuthorizeView> <Authorized> <p>欢迎你,@context.User.Identity.Name!</p> <button @onclick="Logout">登出</button> </Authorized> <NotAuthorized> <p>请先登录</p> <NavLink href="/login">前往登录</NavLink> </NotAuthorized> </AuthorizeView>
- 用
[Authorize]属性装饰路由页面,限制未认证用户访问:
[Authorize] public class DashboardBase : ComponentBase { // 页面逻辑 }
三、额外注意事项
- 必须使用HTTPS:生产环境下Cookie的
Secure属性必须开启,只有HTTPS请求才会携带Cookie,避免明文传输。 - CSRF防护:如果API和客户端是同一域名,SameSite=Lax已经足够;如果是跨域,建议额外添加CSRF Token验证。
- Token刷新:可以在API端实现刷新Token的逻辑,当用户访问时如果Token即将过期,自动返回新的Token到Cookie中,Blazor端通过
CustomAuthStateProvider刷新认证状态即可。
备注:内容来源于stack exchange,提问作者Riley R Arbin
相关产品推荐
相关产品推荐

