Blazor Server项目中Google认证回调User为空的调试求助
问题
在Blazor Server Web应用中实现Google认证,应用使用Identity框架的UserManager和SignInManager,但采用自定义Razor Pages。目标是获取用户选择的谷歌账户信息并在数据库中创建对应用户,但回调方法中User对象始终为空,无法通过if (GoogleUser.IsAuthenticated)判断。以下是相关代码:
Program.cs代码
var builder = WebApplication.CreateBuilder(args); // 添加数据库和数据库认证配置 var connectionString = builder.Configuration.GetConnectionString("StatTrade"); builder.Services.AddDbContext<StatTradeDbContext>(options => options.UseSqlServer(connectionString)); builder.Services.AddIdentity<IdentityUser, IdentityRole>(options => { options.Password.RequireDigit = false; options.Password.RequiredLength = 5; options.Password.RequireLowercase = false; options.Password.RequireUppercase = false; options.Password.RequireNonAlphanumeric = false; options.SignIn.RequireConfirmedEmail = false; }) .AddRoles<IdentityRole>() .AddEntityFrameworkStores<StatTradeDbContext>(); builder.Services.AddAuthentication(options => { options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme; options.DefaultChallengeScheme = GoogleDefaults.AuthenticationScheme; }) .AddCookie() .AddGoogle(GoogleDefaults.AuthenticationScheme, opt => { opt.ClientId = builder.Configuration["Google:Id"]; opt.ClientSecret = builder.Configuration["Google:Secret"]; opt.ClaimActions.MapJsonKey("urn:google:picture", "picture", "url"); }); builder.Services.AddHttpContextAccessor(); builder.Services.AddScoped<HttpContextAccessor>(); builder.Services.AddHttpClient(); builder.Services.AddScoped<HttpClient>(); // 添加服务到容器 builder.Services.AddRazorPages(); builder.Services.AddServerSideBlazor(); // 添加fontawesome依赖 builder.Services .AddBlazorise() .AddBootstrap5Providers() .AddFontAwesomeIcons(); AddBlazorise(builder.Services); var app = builder.Build(); // 配置HTTP请求管道 if (!app.Environment.IsDevelopment()) { app.UseExceptionHandler("/Error"); app.UseHsts(); } app.UseHttpsRedirection(); app.UseStaticFiles(); app.UseCookiePolicy(); app.UseRouting(); // 添加微软标准认证系统 app.UseAuthentication(); app.UseAuthorization(); app.MapBlazorHub(); app.MapFallbackToPage("/_Host"); app.MapRazorPages(); app.Run(); void AddBlazorise(IServiceCollection services) { services .AddBlazorise(); services .AddBootstrap5Providers() .AddFontAwesomeIcons(); }
Razor页面代码(LoginModel.cs)
namespace BlazorGmail.Pages.Identity { [AllowAnonymous] public class LoginModel : PageModel { public IActionResult OnGetAsync(string returnUrl = null) { // 请求重定向到外部登录提供者 var authenticationProperties = new AuthenticationProperties { RedirectUri = Url.Page("./GoogleLogin", pageHandler: "Callback", values: new { returnUrl }), }; return new ChallengeResult(GoogleDefaults.AuthenticationScheme, authenticationProperties); } public async Task<IActionResult> OnGetCallbackAsync( string returnUrl = null, string remoteError = null) { // 从外部登录提供者获取用户信息 var GoogleUser = this.User.Identities.FirstOrDefault(); if (GoogleUser.IsAuthenticated) { var authProperties = new AuthenticationProperties { IsPersistent = true, RedirectUri = this.Request.Host.Value }; await HttpContext.SignInAsync( CookieAuthenticationDefaults.AuthenticationScheme, new ClaimsPrincipal(GoogleUser), authProperties); } return LocalRedirect("/"); } } }
调试方向建议
修正认证Scheme冲突:
AddIdentity已经默认注册了基于IdentityConstants.ApplicationScheme的Cookie认证,手动添加的AddCookie()会创建另一个Cookie Scheme,导致认证上下文混乱。可以去掉手动的.AddCookie(),或者将认证配置的DefaultScheme改为IdentityConstants.ApplicationScheme,保持Scheme统一。调整回调地址的正确性:当前
OnGetAsync中设置的RedirectUri指向./GoogleLogin,但你的PageModel是LoginModel(对应Login.cshtml),回调方法属于当前页面,应该改为:RedirectUri = Url.Page("./Login", pageHandler: "Callback", values: new { returnUrl })确保回调请求能正确路由到当前页面的
OnGetCallbackAsync方法。改用SignInManager获取外部登录信息:回调方法中不能直接通过
this.User获取谷歌用户信息,此时还未完成本地认证。应该注入SignInManager<IdentityUser>,并调用GetExternalLoginInfoAsync()来获取谷歌返回的用户数据:private readonly SignInManager<IdentityUser> _signInManager; public LoginModel(SignInManager<IdentityUser> signInManager) { _signInManager = signInManager; } public async Task<IActionResult> OnGetCallbackAsync(string returnUrl = null, string remoteError = null) { var loginInfo = await _signInManager.GetExternalLoginInfoAsync(); if (loginInfo == null) { // 处理获取失败的情况 return LocalRedirect("/"); } // 从loginInfo中获取谷歌用户的Email、Name等信息 var email = loginInfo.Principal.FindFirstValue(ClaimTypes.Email); // 后续创建用户或关联现有用户逻辑 }完善Google Claim映射:当前只映射了头像信息,需要添加邮箱、姓名等Claim的映射,确保能获取到必要的用户数据:
.AddGoogle(GoogleDefaults.AuthenticationScheme, opt => { opt.ClientId = builder.Configuration["Google:Id"]; opt.ClientSecret = builder.Configuration["Google:Secret"]; opt.ClaimActions.MapJsonKey("urn:google:picture", "picture", "url"); opt.ClaimActions.MapJsonKey(ClaimTypes.Email, "email"); opt.ClaimActions.MapJsonKey(ClaimTypes.Name, "name"); });启用认证日志排查:在
appsettings.json中添加认证相关的调试日志,查看认证流程中的错误或警告:"Logging": { "LogLevel": { "Microsoft.AspNetCore.Authentication": "Debug" } }检查中间件顺序:确保
UseAuthentication()和UseAuthorization()在UseRouting()之后,MapBlazorHub()、MapRazorPages()之前,当前代码顺序正确,但如果后续调整配置需保持这个顺序。
内容的提问来源于stack exchange,提问作者Miky-Bet

