我正在尝试将 google 身份验证集成到我的 ASP.NET Core 2.0 Web api 中,但我不知道如何让它工作。
我的 Startup.cs 中有此代码
ConfigureServices
:
services.AddIdentity<ApplicationUser, IdentityRole>()
.AddDefaultTokenProviders();
services.AddAuthentication()
.AddGoogle(googleOptions =>
{
googleOptions.ClientId = Configuration["Authentication:Google:ClientId"];
googleOptions.ClientSecret = Configuration["Authentication:Google:ClientSecret"];
});
这在
Configure(IApplicationBuilder app, IHostingEnvironment env)
:
app.UseAuthentication();
当我导航到
Authorized
端点时,结果是 302 Found
,因为可能它正在重定向到某个登录端点(我从未创建过)。如何防止重定向并让 API 期待令牌并在未提供令牌时返回 401
?
为后代发布我的终极方法。
正如 Tratcher 指出的那样,
AddGoogle
中间件实际上并不是用于 JWT 身份验证流程。经过更多研究后,我意识到我最终想要的是这里描述的内容:
https://developers.google.com/identity/sign-in/web/backend-auth
所以我的下一个问题是
经过更多挖掘,我发现使用此类和方法将 JWT 验证支持添加到 C# here:
Google.Apis.Auth.Task<GoogleJsonWebSignature.Payload> ValidateAsync(string jwt, GoogleJsonWebSignature.ValidationSettings validationSettings)
接下来我需要弄清楚如何替换内置的 JWT 验证。从这个问题我想出了一个方法: ASP.NET Core JWT 承载令牌自定义验证
这是我的自定义 GoogleTokenValidator:
public class GoogleTokenValidator : ISecurityTokenValidator
{
private readonly JwtSecurityTokenHandler _tokenHandler;
public GoogleTokenValidator()
{
_tokenHandler = new JwtSecurityTokenHandler();
}
public bool CanValidateToken => true;
public int MaximumTokenSizeInBytes { get; set; } = TokenValidationParameters.DefaultMaximumTokenSizeInBytes;
public bool CanReadToken(string securityToken)
{
return _tokenHandler.CanReadToken(securityToken);
}
public ClaimsPrincipal ValidateToken(string securityToken, TokenValidationParameters validationParameters, out SecurityToken validatedToken)
{
validatedToken = null;
var payload = GoogleJsonWebSignature.ValidateAsync(securityToken, new GoogleJsonWebSignature.ValidationSettings()).Result; // here is where I delegate to Google to validate
var claims = new List<Claim>
{
new Claim(ClaimTypes.NameIdentifier, payload.Name),
new Claim(ClaimTypes.Name, payload.Name),
new Claim(JwtRegisteredClaimNames.FamilyName, payload.FamilyName),
new Claim(JwtRegisteredClaimNames.GivenName, payload.GivenName),
new Claim(JwtRegisteredClaimNames.Email, payload.Email),
new Claim(JwtRegisteredClaimNames.Sub, payload.Subject),
new Claim(JwtRegisteredClaimNames.Iss, payload.Issuer),
};
try
{
var principle = new ClaimsPrincipal();
principle.AddIdentity(new ClaimsIdentity(claims, AuthenticationTypes.Password));
return principle;
}
catch (Exception e)
{
Console.WriteLine(e);
throw;
}
}
}
在
Startup.cs
中,我还需要清除默认的 JWT 验证,并添加我的自定义验证:
services.AddAuthentication(options =>
{
options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
options.DefaultScheme = JwtBearerDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
})
.AddJwtBearer(o =>
{
o.SecurityTokenValidators.Clear();
o.SecurityTokenValidators.Add(new GoogleTokenValidator());
}
也许有一种更简单的方法,但这就是我着陆的地方,它似乎工作得很好!为了简单起见,我还做了一些额外的工作,例如,检查我的用户数据库中是否已经有一个用户与谷歌提供的声明相匹配,所以如果上面的代码不能 100% 工作,我深表歉意我可能无意中删除了一些东西。
我刚刚发布了一个 NuGet 包 来处理 Google OpenID Connect 令牌的验证。
该包依赖于来自 Microsoft.AspNetCore.Authentication.JwtBearer 的 Microsoft JWT 验证和身份验证处理程序,并围绕托管域添加了一些验证。
它在
UseGoogle
上包含一个公共扩展方法 JwtBearerOptions
,可让您配置处理程序以验证 Google OpenID Connect 令牌,而无需其他依赖项:
services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(jwt => jwt.UseGoogle(
clientId: "<client-id-from-Google-API-console>",
hostedDomain: "<optional-hosted-domain>"));
如果您想查看源代码,可以在这里找到它。
Mikeyg36 的回答非常棒,最终帮助我解决了我的 jwt 令牌问题。但是,我添加了 clientId,我认为这很重要,因为您不想验证传入的任何 id 令牌。我还在 AddIdentity 中添加了“JwtBearerDefaults.AuthenticationScheme”。
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.IdentityModel.Tokens;
using Google.Apis.Auth;
namespace Some.Namespace
{
public class GoogleTokenValidator : ISecurityTokenValidator
{
private readonly string _clientId;
private readonly JwtSecurityTokenHandler _tokenHandler;
public GoogleTokenValidator(string clientId)
{
_clientId = clientId;
_tokenHandler = new JwtSecurityTokenHandler();
}
public bool CanValidateToken => true;
public int MaximumTokenSizeInBytes { get; set; } = TokenValidationParameters.DefaultMaximumTokenSizeInBytes;
public bool CanReadToken(string securityToken)
{
return _tokenHandler.CanReadToken(securityToken);
}
public ClaimsPrincipal ValidateToken(string securityToken, TokenValidationParameters validationParameters, out SecurityToken validatedToken)
{
validatedToken = null;
try {
var payload = GoogleJsonWebSignature.ValidateAsync(securityToken, new GoogleJsonWebSignature.ValidationSettings() { Audience = new[] { _clientId }}).Result; // here is where I delegate to Google to validate
var claims = new List<Claim>
{
new Claim(ClaimTypes.NameIdentifier, payload.Name),
new Claim(ClaimTypes.Name, payload.Name),
new Claim(JwtRegisteredClaimNames.FamilyName, payload.FamilyName),
new Claim(JwtRegisteredClaimNames.GivenName, payload.GivenName),
new Claim(JwtRegisteredClaimNames.Email, payload.Email),
new Claim(JwtRegisteredClaimNames.Sub, payload.Subject),
new Claim(JwtRegisteredClaimNames.Iss, payload.Issuer),
};
var principle = new ClaimsPrincipal();
principle.AddIdentity(new ClaimsIdentity(claims, JwtBearerDefaults.AuthenticationScheme));
return principle;
}
catch (Exception e)
{
Debug.WriteLine(e);
throw;
}
}
}
}
经过两天的努力寻找最佳答案,因为我想将其实现为后端 api,将前端重定向到 google 登录页面, 它使用 jwt 身份验证作为默认身份验证方法 并使用谷歌仅用于特定方法,如果我愿意,它将适用于特定方法。
(点网7)
所以我像这样配置了我的program.cs:
var configuration = builder.Configuration;
//add authentication
builder.Services.AddAuthentication(options =>
{
options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
options.DefaultScheme = JwtBearerDefaults.AuthenticationScheme;
})
.AddJwtBearer(opt =>
{
opt.SaveToken = true;
opt.RequireHttpsMetadata = true;
opt.TokenValidationParameters = new TokenValidationParameters()
{
ValidateIssuer = false,
ValidateAudience = false,
ValidateLifetime = true,
ValidateIssuerSigningKey = true,
ValidAudience = configuration["JWT:ValidAudience"],
ValidIssuer = configuration["JWT: ValidIssuer"],
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(configuration["JWT:Secret"]))
};
})
.AddGoogle(googleOptions =>
{
googleOptions.ClientId = configuration["Authentication:Google:ClientId"];
googleOptions.ClientSecret = configuration["Authentication:Google:ClientSecret"];
});
并像这样配置您的应用程序设置:
"Authentication": {
"Google": {
"ClientId": "google client id from google",
"ClientSecret": "google client secret from google",
"CallbackPath": "/signin-google"
}
},
"Jwt": {
"ValidAudience": "http://localhost:5157/",
"ValidIssuer": "http://localhost:5157/",
"Secret": "your jwt secret"
}
现在所有api都需要jwt授权 您也可以像这样通过谷歌授权:
[Authorize(AuthenticationSchemes = GoogleDefaults.AuthenticationScheme)]
[HttpGet("GoogleAuth")]
public void Get()
{
// user info from google
var x = this.User;
// find the user info in your db if exists
// user exist // generate jwt token
// user doesnt exists lets create it
// auto activate the user
// generate jwt token
}