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

Posted

技术标签:

【中文标题】如何使用 ASP.Net Core Identity 从登录用户中检索 Google 个人资料图片?【英文标题】:How to retrieve Google profile picture from logged in user with ASP.Net Core Identity? 【发布时间】:2018-02-01 23:24:33 【问题描述】:

好的...我目前正在使用 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);
        //
    

所以,到目前为止一切正常。在这里我被困住了。我阅读了很多关于 accesstokens 和称为 pictureUrl 等声明的文章。但是 Principal 不包含任何这些。

所以问题是:如何在 ExternalLoginCallback 函数中检索一次个人资料图像?

【问题讨论】:

【参考方案1】:

我在 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");

【讨论】:

这个答案是更新的,并且是 2019 年的有效解决方案。这个问题应该以某种方式解决,因为随着技术的变化,同一主题的答案也应该改变。因为它需要越来越多的时间才能让正确的答案通过选票排在首位。没有专业知识的人没有时间尝试每一个以前的解决方案来找到适用于该软件最新版本的解决方案。 对于那些使用这种技术的人来说,来自谷歌的图像的 JSON 属性名称实际上称为“图片”。这是我用来检索它并设置声明的内容: ctx.Identity.AddClaim(new Claim("image", ctx.User.GetValue("picture").ToString()));【参考方案2】:

对于.net core 3.0+,微软使用System.Text.Json来处理google返回的对象,所以我们需要使用这个新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】:

Google 最早于 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");
            );

【讨论】:

【参考方案4】:

我发现无法从声明中获取图片网址。最后,我找到了一个使用名称标识符的解决方案,它与声明一起提供。

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 密钥:

    转到 Google API 控制台。 从项目下拉列表中,选择一个项目,或创建一个新项目。 启用 Google+ API 服务:

一个。在 Google API 列表中,搜索 Google+ API 服务。

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

c。按启用 API 按钮。

该过程完成后,Google+ API 会出现在启用的 API 列表中。到 访问,选择左侧边栏菜单上的 APIs & Services,然后选择 已启用 API 选项卡。

    在“API 和服务”下的侧边栏中,选择凭据。 在凭据选项卡中,选择新凭据下拉列表,然后选择 API 密钥。 从“创建新密钥”弹出窗口中,为您的项目选择合适的密钥类型:服务器密钥、浏览器密钥、android 密钥或 ios 密钥。 输入密钥名称,按照说明填写任何其他字段,然后选择创建。

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

【讨论】:

您是否获得了 API 密钥,因为当您向 Google 注册新应用时,他们不会为您提供 API 密钥? @CalinCosmin 我将其添加到答案中。 谢谢。这绝对帮助了我 @CalinCosmin 如果问题和/或答案对您有帮助,您可以通过点赞来表达您的感激之情。 立即完成。抱歉我没有早点投票。谢谢【参考方案5】:

您可以在 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");
    

【讨论】:

以上是关于如何使用 ASP.Net Core Identity 从登录用户中检索 Google 个人资料图片?的主要内容,如果未能解决你的问题,请参考以下文章

ASP.NET Core运行两个TestServer进行集成测试

ASP.NET Core中的缓存[1]:如何在一个ASP.NET Core应用中使用缓存

如何在 ASP.Net Core 中使用 IHostedService

如何在 ASP.Net Core 使用 内存缓存

如何使用 EF Core 在 ASP.NET Core 中取消应用迁移

如何使用 dotnet cli 命令以 VB.NET 语言创建 ASP.NET Core 项目?