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
相关产品推荐
相关产品推荐

