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

ASP.NET Web API中Google认证结合JWT授权配置问题求助

问题原因分析

你遇到的错误核心是:JwtBearerHandler仅负责验证JWT令牌,不支持SignInAsync操作——这个方法是用于创建用户会话(比如Cookie认证场景)的。而你把默认认证Scheme全设为了JwtBearer,Google认证完成后框架尝试用该Scheme执行登录操作,自然会报错。

解决方案步骤

1. 修正认证服务配置

调整默认Scheme的分工:让常规API请求用JWT验证,认证挑战(跳转到Google登录)用Google的Scheme。同时配置Google认证的回调路径和用户信息捕获事件。

修改后的服务注册代码:

builder.Services.AddAuthentication(options =>
{
    // 常规API请求用JWT验证
    options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
    options.DefaultScheme = JwtBearerDefaults.AuthenticationScheme;
    // 触发登录跳转时用Google的Scheme
    options.DefaultChallengeScheme = GoogleDefaults.AuthenticationScheme;
})
.AddJwtBearer(opt =>
{
    opt.SaveToken = true;
    opt.RequireHttpsMetadata = true;
    opt.TokenValidationParameters = new TokenValidationParameters()
    {
        ValidateIssuer = !string.IsNullOrEmpty(configuration["JWT:Issuer"]),
        ValidateAudience = !string.IsNullOrEmpty(configuration["JWT:Audience"]),
        ValidateLifetime = true,
        ValidateIssuerSigningKey = true,
        ValidAudience = configuration["JWT:Audience"],
        ValidIssuer = configuration["JWT:Issuer"],
        IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(configuration["JWT:Secret"]))
    };
})
.AddGoogle(googleOptions =>
{
    googleOptions.ClientId = configuration["Google:ClientId"]!;
    googleOptions.ClientSecret = configuration["Google:ClientSecret"]!;
    // 回调路径要和Google开发者控制台配置的完全一致
    googleOptions.CallbackPath = "/api/auth/google-callback";
    // 捕获Google返回的用户信息,添加到Claims中
    googleOptions.Events.OnCreatingTicket = context =>
    {
        var email = context.User?.GetValue("email")?.ToString();
        var name = context.User?.GetValue("name")?.ToString();
        context.Identity?.AddClaim(new Claim(ClaimTypes.Email, email));
        context.Identity?.AddClaim(new Claim(ClaimTypes.Name, name));
        return Task.CompletedTask;
    };
});

2. 编写认证回调API端点

创建Auth控制器,处理Google登录的触发和回调,手动生成JWT返回给客户端:

[ApiController]
[Route("api/auth")]
public class AuthController : ControllerBase
{
    private readonly IConfiguration _configuration;

    public AuthController(IConfiguration configuration)
    {
        _configuration = configuration;
    }

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

    // Google认证回调,生成JWT返回
    [HttpGet("google-callback")]
    public async Task<IActionResult> GoogleCallback()
    {
        var authResult = await HttpContext.AuthenticateAsync(GoogleDefaults.AuthenticationScheme);
        if (!authResult.Succeeded)
        {
            return BadRequest("Google认证失败");
        }

        // 从认证结果中提取用户信息
        var userId = authResult.Principal.FindFirstValue(ClaimTypes.NameIdentifier);
        var email = authResult.Principal.FindFirstValue(ClaimTypes.Email);
        var name = authResult.Principal.FindFirstValue(ClaimTypes.Name);

        // 生成JWT令牌
        var tokenHandler = new JwtSecurityTokenHandler();
        var key = Encoding.UTF8.GetBytes(_configuration["JWT:Secret"]!);
        var tokenDescriptor = new SecurityTokenDescriptor
        {
            Subject = new ClaimsIdentity(new[]
            {
                new Claim(ClaimTypes.NameIdentifier, userId),
                new Claim(ClaimTypes.Email, email),
                new Claim(ClaimTypes.Name, name)
            }),
            Expires = DateTime.UtcNow.AddHours(2),
            SigningCredentials = new SigningCredentials(new SymmetricSecurityKey(key), SecurityAlgorithms.HmacSha256Signature),
            Issuer = _configuration["JWT:Issuer"],
            Audience = _configuration["JWT:Audience"]
        };
        var token = tokenHandler.CreateToken(tokenDescriptor);
        var jwtToken = tokenHandler.WriteToken(token);

        return Ok(new { Token = jwtToken });
    }
}

3. 关键注意事项

  • 回调路径必须和Google开发者控制台中配置的授权重定向URI完全匹配(本地测试用https://localhost:xxxx/api/auth/google-callback,线上替换为你的域名)。
  • 确保appsettings.json中JWT的Issuer、Audience、Secret配置项正确。
  • 客户端拿到JWT后,后续请求需在Authorization头中携带Bearer {token}来访问需要认证的API接口。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.20 09:11:19