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

ASP.NET Core 5.0 Web API无跳转实现Google登录返回Token方法

ASP.NET Core 5.0 Web API 无跳转Google登录实现方案

你要的无跳转Google登录流程,符合Google安全规范的实现逻辑是:前端通过Google官方JS SDK在当前页完成用户授权拿到ID Token,后端仅负责校验这个Token的合法性,全程不需要后端跳转Google官方页面,禁止直接收集用户Google账号密码请求Google接口(违反Google服务条款会被封禁客户端)。

一、前置准备

  • 到Google Cloud控制台创建OAuth 2.0 Web类型客户端,配置你前端域名到授权JS来源列表,拿到对应的Client ID
  • 新建ASP.NET Core 5.0 Web API项目,通过NuGet安装依赖包:
    • Google.Apis.Auth:Google官方提供的ID Token校验库
    • Microsoft.AspNetCore.Authentication.JwtBearer:用于JWT认证中间件
    • System.IdentityModel.Tokens.Jwt:用于生成自定义应用Token

二、基础配置

首先在appsettings.json添加配置节点:

{
  "GoogleAuthSettings": {
    "ClientId": "你申请到的Google OAuth客户端ID",
    "JwtSecret": "长度不小于16位的自定义JWT签名密钥",
    "JwtExpireMinutes": 120
  }
}

然后在Startup.cs的ConfigureServices方法中注入配置、添加JWT认证服务:

public void ConfigureServices(IServiceCollection services)
{
    // 绑定Google认证配置
    services.Configure<GoogleAuthSettings>(Configuration.GetSection("GoogleAuthSettings"));
    
    // 配置JWT认证
    var jwtSecret = Configuration.GetValue<string>("GoogleAuthSettings:JwtSecret");
    var key = Encoding.ASCII.GetBytes(jwtSecret);
    services.AddAuthentication(x =>
    {
        x.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
        x.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
    })
    .AddJwtBearer(x =>
    {
        x.RequireHttpsMetadata = true;
        x.SaveToken = true;
        x.TokenValidationParameters = new TokenValidationParameters
        {
            ValidateIssuerSigningKey = true,
            IssuerSigningKey = new SymmetricSecurityKey(key),
            ValidateIssuer = false,
            ValidateAudience = false,
            ClockSkew = TimeSpan.Zero
        };
    });

    services.AddScoped<IUserService, UserService>(); // 注册你自己的用户数据操作服务
    services.AddControllers();
}

记得在Configure方法中启用认证中间件:

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
    // 其他中间件配置...
    app.UseAuthentication();
    app.UseAuthorization();
    // 路由配置...
}

三、登录接口实现

接口逻辑:接收前端传来的Google ID Token → 校验Token合法性 → 提取用户信息 → 同步本地用户数据 → 生成应用自有JWT返回。

[ApiController]
[Route("api/auth")]
public class AuthController : ControllerBase
{
    private readonly GoogleAuthSettings _googleAuthSettings;
    private readonly IUserService _userService;

    public AuthController(IOptions<GoogleAuthSettings> googleAuthOptions, IUserService userService)
    {
        _googleAuthSettings = googleAuthOptions.Value;
        _userService = userService;
    }

    [HttpPost("google-login")]
    public async Task<IActionResult> GoogleLogin([FromBody] GoogleLoginRequest request)
    {
        if (string.IsNullOrWhiteSpace(request.IdToken))
            return BadRequest("Google凭据不能为空");

        // 校验Google ID Token有效性
        GoogleJsonWebSignature.Payload googlePayload;
        try
        {
            var validationSettings = new GoogleJsonWebSignature.ValidationSettings
            {
                // 强制校验Token受众为当前应用的Client ID,防止跨应用Token伪造
                Audience = new[] { _googleAuthSettings.ClientId }
            };
            googlePayload = await GoogleJsonWebSignature.ValidateAsync(request.IdToken, validationSettings);
        }
        catch (InvalidJwtException)
        {
            return Unauthorized("无效的Google登录凭据");
        }

        // 校验账号邮箱状态
        if (!googlePayload.EmailVerified)
            return BadRequest("当前Google账号未完成邮箱验证,无法登录");

        // 同步本地用户:存在则返回,不存在则自动注册
        var googleUserId = googlePayload.Subject; // Google侧用户唯一标识,请勿用邮箱作为唯一键
        var appUser = await _userService.GetOrCreateExternalUserAsync(
            provider: "Google",
            providerUserId: googleUserId,
            email: googlePayload.Email,
            displayName: googlePayload.Name,
            avatarUrl: googlePayload.Picture
        );

        // 生成应用自有JWT Token
        var tokenHandler = new JwtSecurityTokenHandler();
        var signKey = Encoding.ASCII.GetBytes(_googleAuthSettings.JwtSecret);
        var tokenDescriptor = new SecurityTokenDescriptor
        {
            Subject = new ClaimsIdentity(new[]
            {
                new Claim(ClaimTypes.NameIdentifier, appUser.Id.ToString()),
                new Claim(ClaimTypes.Email, appUser.Email),
                new Claim(ClaimTypes.Name, appUser.DisplayName)
            }),
            Expires = DateTime.UtcNow.AddMinutes(_googleAuthSettings.JwtExpireMinutes),
            SigningCredentials = new SigningCredentials(new SymmetricSecurityKey(signKey), SecurityAlgorithms.HmacSha256Signature)
        };
        var securityToken = tokenHandler.CreateToken(tokenDescriptor);
        var accessToken = tokenHandler.WriteToken(securityToken);

        return Ok(new
        {
            AccessToken = accessToken,
            ExpiresIn = _googleAuthSettings.JwtExpireMinutes * 60,
            UserInfo = new
            {
                appUser.Id,
                appUser.DisplayName,
                appUser.Email,
                appUser.AvatarUrl
            }
        });
    }
}

// 接口请求模型
public class GoogleLoginRequest
{
    public string IdToken { get; set; }
}

// 配置映射模型
public class GoogleAuthSettings
{
    public string ClientId { get; set; }
    public string JwtSecret { get; set; }
    public int JwtExpireMinutes { get; set; }
}

四、前端对接要点

  • 前端引入Google Identity Services前端SDK,在自有登录页渲染登录按钮,用户点击后会弹出Google官方授权浮层(不会跳离当前页面)
  • 用户完成授权后,SDK会直接返回合法的ID Token,前端将该Token POST到上述登录接口即可
  • 拿到后端返回的应用Token后,存储到本地,后续接口请求携带该Token完成鉴权

五、注意事项

  • 禁止在前后端收集用户Google账号密码直接请求Google登录接口,该行为违反Google OAuth服务条款,会直接封禁客户端权限,同时存在极大数据泄露风险
  • 校验Google ID Token时必须配置Audience参数为当前应用Client ID,否则其他应用的合法Token可绕过校验,存在严重安全漏洞
  • 必须使用Google返回的Subject字段作为第三方登录的用户唯一标识,不要使用邮箱作为唯一键,用户Google邮箱支持修改,会导致账号匹配错误
  • 生产环境JWT签名密钥、Google Client Secret等敏感配置要存储在环境变量或专用配置中心,禁止硬编码到代码仓库

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.28 23:42:18