You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Blazor中Google身份认证实现:兼容Asp.net Core托管与MAUI环境

统一兼容Blazor WASM(Asp.net Core托管)与MAUI Android的Google身份认证方案

核心思路

所有认证流程完全通过后端代理,应用端(Blazor WASM/MAUI)仅负责触发认证请求、接收后端返回的认证结果,全程不接触Google Client ID和Secret,确保密钥安全。

步骤1:后端(Asp.net Core)配置Google认证

  1. 安装NuGet包:Microsoft.AspNetCore.Authentication.Google
  2. 在Program.cs中配置Google认证与JWT(跨场景兼容):
builder.Services.AddAuthentication(options =>
{
    options.DefaultScheme = JwtBearerDefaults.AuthenticationScheme;
    options.DefaultChallengeScheme = GoogleDefaults.AuthenticationScheme;
})
.AddGoogle(googleOptions =>
{
    // 从配置文件读取密钥,避免硬编码
    googleOptions.ClientId = builder.Configuration["Authentication:Google:ClientId"];
    googleOptions.ClientSecret = builder.Configuration["Authentication:Google:ClientSecret"];
    googleOptions.CallbackPath = "/signin-google";
    // 映射Google返回的用户头像字段
    googleOptions.ClaimActions.MapJsonKey("urn:google:picture", "picture", "url");
})
.AddJwtBearer(jwtOptions =>
{
    jwtOptions.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"]))
    };
});

// 启用认证、授权中间件
app.UseAuthentication();
app.UseAuthorization();
  1. 新增认证接口,处理前端触发的认证请求与Token生成:
[ApiController]
[Route("api/auth")]
public class AuthController : ControllerBase
{
    private readonly IConfiguration _config;

    public AuthController(IConfiguration config)
    {
        _config = config;
    }

    // 触发Google认证跳转
    [HttpGet("google-login")]
    public IActionResult GoogleLogin()
    {
        var redirectUrl = Url.Action(nameof(GoogleResponse), "Auth");
        var properties = new AuthenticationProperties { RedirectUri = redirectUrl };
        return Challenge(properties, GoogleDefaults.AuthenticationScheme);
    }

    // 处理Google回调,生成JWT返回给前端
    [HttpGet("google-response")]
    public async Task<IActionResult> GoogleResponse()
    {
        var result = await HttpContext.AuthenticateAsync(GoogleDefaults.AuthenticationScheme);
        if (!result.Succeeded) return BadRequest("Google认证失败");

        // 提取用户Claims
        var claims = result.Principal.Claims.ToList();
        
        // 生成JWT Token
        var securityKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_config["Jwt:Key"]));
        var credentials = new SigningCredentials(securityKey, SecurityAlgorithms.HmacSha256);

        var token = new JwtSecurityToken(
            issuer: _config["Jwt:Issuer"],
            audience: _config["Jwt:Audience"],
            claims: claims,
            expires: DateTime.Now.AddMinutes(30),
            signingCredentials: credentials);

        return Ok(new { Token = new JwtSecurityTokenHandler().WriteToken(token) });
    }
}

步骤2:前端通用认证逻辑(兼容Blazor WASM和MAUI)

通用认证服务

创建IAuthService接口与实现类,封装核心认证逻辑,两个前端项目共用:

public interface IAuthService
{
    Task<string> LoginWithGoogle();
    Task Logout();
}

public class AuthService : IAuthService
{
    private readonly HttpClient _httpClient;
    private readonly object _navigationService; // 平台特定导航服务,Blazor用NavigationManager,MAUI用INavigation

    public AuthService(HttpClient httpClient, object navigationService)
    {
        _httpClient = httpClient;
        _navigationService = navigationService;
    }

    public async Task<string> LoginWithGoogle()
    {
        // 触发后端认证跳转
        var response = await _httpClient.GetAsync("api/auth/google-login");
        if (response.IsRedirect)
        {
            var redirectUrl = response.Headers.Location.ToString();
            // 平台特定跳转逻辑:Blazor用NavigationManager,MAUI用WebView加载
            if (_navigationService is NavigationManager blazorNav)
            {
                blazorNav.NavigateTo(redirectUrl, true);
            }
        }
        // 获取认证后的Token
        var tokenResponse = await _httpClient.GetFromJsonAsync<TokenResponse>("api/auth/google-response");
        return tokenResponse?.Token;
    }

    public Task Logout()
    {
        // 清除前端存储的Token,可选调用后端注销接口
        return Task.CompletedTask;
    }
}

// 辅助模型
public class TokenResponse { public string Token { get; set; } }

平台适配处理

  • Blazor WASM:
    在Program.cs注册服务:

    builder.Services.AddScoped<IAuthService>(sp => 
        new AuthService(sp.GetRequiredService<HttpClient>(), sp.GetRequiredService<NavigationManager>()));
    builder.Services.AddHttpClient("BackendApi", client => 
        client.BaseAddress = new Uri(builder.HostEnvironment.BaseAddress));
    

    页面中触发认证:

    <button @onclick="HandleGoogleLogin">Google登录</button>
    
    @code {
        [Inject] private IAuthService AuthService { get; set; }
        [Inject] private Blazored.LocalStorage.ILocalStorageService LocalStorage { get; set; }
    
        private async Task HandleGoogleLogin()
        {
            var token = await AuthService.LoginWithGoogle();
            if (!string.IsNullOrEmpty(token))
            {
                await LocalStorage.SetItemAsync("authToken", token);
                // 导航到主页面
                NavigationManager.NavigateTo("/");
            }
        }
    }
    
  • MAUI Android:
    使用WebView加载认证地址,监听回调获取Token:

    private readonly HttpClient _httpClient;
    private readonly INavigation _navigation;
    
    public LoginPage(HttpClient httpClient, INavigation navigation)
    {
        InitializeComponent();
        _httpClient = httpClient;
        _navigation = navigation;
    }
    
    private void GoogleLoginBtn_Clicked(object sender, EventArgs e)
    {
        var authUrl = $"{App.BackendBaseUrl}/api/auth/google-login";
        AuthWebView.Source = authUrl;
        AuthWebView.Navigating += AuthWebView_Navigating;
    }
    
    private async void AuthWebView_Navigating(object sender, WebNavigatingEventArgs e)
    {
        if (e.Url.Contains("/api/auth/google-response"))
        {
            e.Cancel = true;
            var tokenResponse = await _httpClient.GetFromJsonAsync<TokenResponse>(e.Url);
            if (!string.IsNullOrEmpty(tokenResponse.Token))
            {
                await SecureStorage.SetAsync("authToken", tokenResponse.Token);
                await _navigation.PushAsync(new MainPage());
            }
            AuthWebView.Navigating -= AuthWebView_Navigating;
        }
    }
    

关键注意事项

  • 密钥安全:Google Client ID/Secret仅存储在后端appsettings.json,前端全程不接触。
  • 跨域配置:若Blazor WASM独立部署,后端需配置CORS允许前端域名访问。
  • MAUI权限:Android端需在AndroidManifest.xml添加<uses-permission android:name="android.permission.INTERNET" />权限。
  • Token存储:前端使用安全存储,Blazor用Blazored.LocalStorage,MAUI用SecureStorage,避免明文存储。

内容的提问来源于stack exchange,提问作者Bobil

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.30 05:54:56