如何使用ASP.Net Core Identity从已登录的用户中检索Google个人资料图片?

9

好的,我目前正在使用ASP.Net Core 1.1.2和ASP.NET Core Identity 1.1.2。

Startup.cs中的重要部分如下:

    public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
    {
        //...
        app.UseGoogleAuthentication(new GoogleOptions
        {
            AuthenticationScheme = "Google",
            SignInScheme = "Identity.External", // this is the name of the cookie middleware registered by UseIdentity()
            ClientId = Configuration["ExternalLoginProviders:Google:ClientId"],
            ClientSecret = Configuration["ExternalLoginProviders:Google:ClientSecret"]
        });
    }

GoogleOptions是Microsoft.AspNetCore.Authentication.Google nuget包中的一部分。

AccountController.cs中的回调函数如下:

    [HttpGet]
    [AllowAnonymous]
    public async Task<IActionResult> ExternalLoginCallback(string returnUrl = null, string remoteError = null)
    {
        //... SignInManager<User> _signInManager; declared before
        ExternalLoginInfo info = await _signInManager.GetExternalLoginInfoAsync();
        SignInResult signInResult = await _signInManager.ExternalLoginSignInAsync(info.LoginProvider, info.ProviderKey, isPersistent: false);
        string email = info.Principal.FindFirstValue(ClaimTypes.Email);
        string firstName = info.Principal.FindFirstValue(ClaimTypes.GivenName);
        string lastName = info.Principal.FindFirstValue(ClaimTypes.Surname);
        //
    }

到目前为止,一切都很顺利。但是我卡在这里了。我阅读了许多关于访问令牌和称为pictureUrl等的声明的文章。但是Principal不包含其中任何一个。

所以问题是:如何在ExternalLoginCallback函数中检索配置文件图像?

5个回答

20

我也遇到了同样的问题,不过是在ASP.NET Core 2.0上。 在你的startup.cs中,在OnCreatingTicket事件中检索图片有更好的方法。在你的情况下,你需要将特定的声明"picture"添加到标识中。

    public void ConfigureServices(IServiceCollection services)
    {
        services
            .AddAuthentication()
            .AddCookie()
            .AddGoogle(options =>
            {
                options.ClientId = Configuration["Google.LoginProvider.ClientId"];
                options.ClientSecret = Configuration["Google.LoginProvider.ClientKey"];
                options.Scope.Add("profile");
                options.Events.OnCreatingTicket = (context) =>
                {
                    context.Identity.AddClaim(new Claim("image", context.User.GetValue("image").SelectToken("url").ToString()));

                    return Task.CompletedTask;
                };
            });
    }

然后在您的 AccountController 中,您可以从外部登录信息方法中选择图像。

var info = await _signInManager.GetExternalLoginInfoAsync();

var picture = info.Principal.FindFirstValue("image");

4
这个答案比较新,在2019年仍然是可行的解决方案。这个问题应该得到一定程度的解决,因为随着技术的变化,相同主题的答案也应该随之改变。因为需要更多的时间才能通过投票找到正确的答案,并且没有时间让不具备专业知识的人去尝试每一个旧的解决方案,以找到最新软件版本的适用方案。 - Daniel Leiszen
对于那些使用这种技术的人来说,从谷歌获取图像的JSON属性名称实际上被称为 "picture"。这就是我用来检索并设置声明的内容:ctx.Identity.AddClaim(new Claim("image", ctx.User.GetValue("picture").ToString())); - Blair Holmes

10

对于 .net core 3.0+,微软使用 System.Text.Json 处理从谷歌返回的对象,因此我们需要使用这个新 API 的 GetProperty 方法来获取图片。

https://devblogs.microsoft.com/dotnet/try-the-new-system-text-json-apis/

services.AddAuthentication()
                .AddGoogle(options =>
                {
                    IConfigurationSection googleAuthNSection = Configuration.GetSection("Authentication:Google");

                    options.ClientId = googleAuthNSection["ClientId"];
                    options.ClientSecret = googleAuthNSection["ClientSecret"];
                    options.Scope.Add("profile");
                    options.Events.OnCreatingTicket = (context) =>
                    {                      
                        var picture = context.User.GetProperty("picture").GetString();

                        context.Identity.AddClaim(new Claim("picture", picture));

                        return Task.CompletedTask;
                    };
                });

3

谷歌将从2019年1月28日开始逐步关闭应用程序中的Google+登录。

https://github.com/aspnet/AspNetCore/issues/6486

因此,我们需要对@mtrax所接受的答案进行以下更改:

.AddGoogle(o =>
            {
                o.ClientId = Configuration["Authentication:Google:ClientId"];
                o.ClientSecret = Configuration["Authentication:Google:ClientSecret"];
                o.UserInformationEndpoint = "https://www.googleapis.com/oauth2/v2/userinfo";
                o.ClaimActions.Clear();
                o.ClaimActions.MapJsonKey(ClaimTypes.NameIdentifier, "id");
                o.ClaimActions.MapJsonKey(ClaimTypes.Name, "name");
                o.ClaimActions.MapJsonKey(ClaimTypes.GivenName, "given_name");
                o.ClaimActions.MapJsonKey(ClaimTypes.Surname, "family_name");
                o.ClaimActions.MapJsonKey("urn:google:profile", "link");
                o.ClaimActions.MapJsonKey(ClaimTypes.Email, "email");
                o.ClaimActions.MapJsonKey("image", "picture");
            });

2

我找不到从声明中获取图片URL的方法。最终,我发现可以使用带有声明的nameidentifier来解决此问题。

string googleApiKey = "{your google api key}";
ExternalLoginInfo info = await _signInManager.GetExternalLoginInfoAsync();
string nameIdentifier = info.Principal.FindFirstValue(ClaimTypes.NameIdentifier);
string jsonUrl = $"https://www.googleapis.com/plus/v1/people/{nameIdentifier}?fields=image&key={googleApiKey}";
using (HttpClient httpClient = new HttpClient())
{
    string s = await httpClient.GetStringAsync(jsonUrl);
    dynamic deserializeObject = JsonConvert.DeserializeObject(s);
    string thumbnailUrl = (string)deserializeObject.image.url;
    byte[] thumbnail = await httpClient.GetByteArrayAsync(thumbnailUrl);
}

你只需要一个Google API密钥。
创建API密钥的步骤如下:
  1. 进入Google API控制台。
  2. 从项目下拉菜单中选择一个项目或创建一个新项目。
  3. 启用Google+ API服务:

a. 在Google APIs列表中搜索Google+ API服务。

b. 从结果列表中选择Google+ API。

c. 按下启用API按钮。

完成后,Google+ API将出现在已启用API列表中。要访问,请选择左侧边栏菜单上的API和服务,然后选择已启用的API选项卡。

  1. 在“API和服务”侧边栏下,选择凭据。
  2. 在凭据选项卡中,选择新凭据下拉列表,并选择API密钥。
  3. 从“创建新密钥”弹出窗口中,为您的项目选择适当类型的密钥:服务器密钥、浏览器密钥、Android密钥或iOS密钥。
  4. 输入密钥名称,按照指示填写任何其他字段,然后选择创建。

https://developers.google.com/+/web/api/rest/oauth


如何获取API密钥?因为当您向Google注册新应用程序时,他们不会提供API密钥。 - cozmin-calin
1
@CalinCosmin 我已将其添加到答案中。 - Norman
谢谢。那确实帮了我。 - cozmin-calin
如果这个问题和/或答案对您有帮助,您可以通过点赞来表达感激之情。 - Norman
刚刚完成了。很抱歉我之前没有投票。谢谢。 - cozmin-calin
什么是 _configurationRoot? - Hamza Dahmoun

0

您可以在 GoogleOptions 设置中提供您想要请求的其他范围。您正在寻找的范围是 profile 范围,通过额外的声明授予访问名字、姓氏和个人资料图片的权限: https://developers.google.com/identity/protocols/OpenIDConnect#discovery

public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
    {
        //...
        app.UseGoogleAuthentication(new GoogleOptions
        {
            AuthenticationScheme = "Google",
            SignInScheme = "Identity.External", // this is the name of the cookie middleware registered by UseIdentity()
            ClientId = Configuration["ExternalLoginProviders:Google:ClientId"],
            ClientSecret = Configuration["ExternalLoginProviders:Google:ClientSecret"]});
            Scopes = { "profile" };
    }

在身份验证成功后,您可以在控制器中检索图片声明:

    [HttpGet]
    [AllowAnonymous]
    public async Task<IActionResult> ExternalLoginCallback(string returnUrl = null, string remoteError = null)
    {
        //... SignInManager<User> _signInManager; declared before
        ExternalLoginInfo info = await _signInManager.GetExternalLoginInfoAsync();
        SignInResult signInResult = await _signInManager.ExternalLoginSignInAsync(info.LoginProvider, info.ProviderKey, isPersistent: false);
        string email = info.Principal.FindFirstValue(ClaimTypes.Email);
        string firstName = info.Principal.FindFirstValue(ClaimTypes.GivenName);
        string lastName = info.Principal.FindFirstValue(ClaimTypes.Surname);

        // profile claims
        string picture = info.Principal.FindFirstValue("picture");
        string firstName = info.Principal.FindFirstValue("given_name");
        string lastName = info.Principal.FindFirstValue("family_name");
    }

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接