Blazor Server渲染模式下调用HttpContext.SignInAsync后Cookie未生成
我创建了一个采用Auto渲染模式+全局路由的Blazor新项目,默认包含服务器端和客户端两个项目,所有页面都放在客户端项目中,我在客户端里加了Login.razor页面。
这个Login页面通过HttpClient调用服务器端的WebApi验证凭据,验证通过后服务端会调用HttpContext.SignInAsync,API返回成功响应后就把用户重定向到主页。
服务器端API代码
[HttpPost("signin")] public async Task<IActionResult> SignInAsync([FromForm] string username, [FromForm] string password) { var user = await _userRepository.GetByNameAsync(username); if (user != null) { var passwordHash = await _userRepository.GetPasswordHashAsync(user.Id); if (passwordHash != null) { var passwordResult = _passwordHasher.VerifyPasswordHash(user, passwordHash, password); if (passwordResult == PasswordVerificationResult.Success) { var claimsIdentity = new ClaimsIdentity(GetClaims(user), CookieAuthenticationDefaults.AuthenticationScheme); await HttpContext.SignInAsync(CookieAuthenticationDefaults.AuthenticationScheme, new ClaimsPrincipal(claimsIdentity)); return Ok(user); } } } }
登录页面初始代码
async Task LogInAsync() { var content = new FormUrlEncodedContent(new[] { new KeyValuePair<string, string>("username", LoginInfo.Username), new KeyValuePair<string, string>("password", LoginInfo.Password) }); var response = await HttpClient.PostAsync("api/users/signin", content); if (response.IsSuccessStatusCode) { // 从查询字符串提取returnUrl var uri = new Uri(NavigationManager.Uri); var queryParams = System.Web.HttpUtility.ParseQueryString(uri.Query); string returnUrl = GetQueryStringParameter("returnUrl"); // 重定向到returnUrl NavigationManager.NavigateTo(returnUrl, true); } else { Console.WriteLine("Invalid credentials"); } }
问题现象
因为用了Auto渲染模式,页面初始加载是Server渲染模式。用户点击登录后,断点确认服务端已经执行完HttpContext.SignInAsync,页面也完成重定向,但浏览器里没创建登录Cookie,用户还是未授权状态,会被打回登录页。而重定向后页面重新加载,WASM完全加载,渲染模式切换成WASM,这时候再点登录按钮就一切正常,Cookie能正常创建。也就是说只有在Server渲染模式下,浏览器不会自动创建登录Cookie。
我知道这可能和Server渲染模式通过SignalR运行的机制有关,但不清楚具体原因。
临时解决方案(手动写入Cookie)
我尝试从响应头里拿Cookie手动写入浏览器,功能倒是实现了:
async Task LogInAsync() { var content = new FormUrlEncodedContent(new[] { new KeyValuePair<string, string>("username", LoginInfo.Username), new KeyValuePair<string, string>("password", LoginInfo.Password) }); var response = await HttpClient.PostAsync("api/users/signin", content); if (response.IsSuccessStatusCode) { if (response.Headers.Contains("Set-Cookie")) { var setCookieHeader = response.Headers.GetValues("Set-Cookie").FirstOrDefault(); if (!string.IsNullOrEmpty(setCookieHeader)) { // 手动向浏览器写入Cookie var cook = setCookieHeader.Split(";")[0]; await JSRuntime.InvokeVoidAsync("eval", $"document.cookie = '" + cook + "';"); } } // 从查询字符串提取returnUrl var uri = new Uri(NavigationManager.Uri); var queryParams = System.Web.HttpUtility.ParseQueryString(uri.Query); string returnUrl = GetQueryStringParameter("returnUrl"); // 重定向到returnUrl NavigationManager.NavigateTo(returnUrl, true); //await JSRuntime.InvokeVoidAsync("eval", "window.location.href = '/';"); } else { Console.WriteLine("Invalid credentials"); } }
但不确定这种手动写Cookie的方式是否合理,想知道正确的解决方案是什么。
内容的提问来源于stack exchange,提问作者g_b
相关产品推荐
相关产品推荐

