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

6

我已经有一个能正常工作的解决方案了,但我想知道这是否是正确的做法。以下是我的进展情况。

我正在使用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.UseFacebookAuthentication(new FacebookOptions
        {
            AuthenticationScheme = "Facebook",
            AppId = Configuration["ExternalLoginProviders:Facebook:AppId"],
            AppSecret = Configuration["ExternalLoginProviders:Facebook:AppSecret"]
        });
    }

FacebookOptions是Microsoft.AspNetCore.Authentication.Facebook 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);

        byte[] thumbnailBytes = null;

        if (info.LoginProvider == "Facebook")
        {
            string nameIdentifier = info.Principal.FindFirstValue(ClaimTypes.NameIdentifier);
            string thumbnailUrl = $"https://graph.facebook.com/{nameIdentifier}/picture?type=large";
            using (HttpClient httpClient = new HttpClient())
            {
                thumbnailBytes = await httpClient.GetByteArrayAsync(thumbnailUrl);
            }
        }
        //...
    }

这段代码完全正常运行,但正如之前提到的那样,这是技术上正确的方法吗?(严格指技术层面,不涉及个人观点)

5个回答

7
从Facebook获取个人资料图片,你需要配置Facebook选项,并在OAuth的OnCreatingTicket事件中订阅。
services.AddAuthentication().AddFacebook("Facebook", options =>
{

    options.SignInScheme = IdentityServerConstants.ExternalCookieAuthenticationScheme;
    options.ClientId = configuration.GetSection("ExternalLogin").GetSection("Facebook").GetSection("ClientId").Value;
    options.ClientSecret = configuration.GetSection("ExternalLogin").GetSection("Facebook").GetSection("ClientSecret").Value;
    options.Fields.Add("picture");
    options.Events = new OAuthEvents
    {
        OnCreatingTicket = context =>
        {
            var identity = (ClaimsIdentity)context.Principal.Identity;
            var profileImg = context.User["picture"]["data"].Value<string>("url");
            identity.AddClaim(new Claim(JwtClaimTypes.Picture, profileImg));
            return Task.CompletedTask;
        }
    };
});

这个“identity”声明的生命周期是什么?在我的控制器方法中它是不“可观察”的。看起来这个身份只存在于与OAuth提供程序通信的上下文中。 - Roman Pokrovskij
它发生在成功登录Facebook后。默认方法是ExternalLoginCallback。从Asp.net Core模板创建。 - BrunoBrito
谢谢Buno。但是你如何在控制器中访问它们?即使成功登录,ClaimsPrincipal仍然为空。(不完全为空 - 它包含一些项目,但不包括那些项目)。 - Roman Pokrovskij
8.0提供的链接可以下载图片,如何在img中使用它? - Leandro Bardelli

3
在 ASP.NET Core 3.0 中,OAuthCreatingTicketContext 发生了重大变化,请参见 https://learn.microsoft.com/en-US/dotnet/core/compatibility/2.2-3.0
我修改了
var profileImg = context.User["picture"]["data"].Value<string>("url");

var profileImg = context.User.GetProperty("picture").GetProperty("data").GetProperty("url").ToString();

谢谢。这是完整的@BrunoBrito答案。 - Leandro Bardelli

1
在asp.net core 3.1中,我通过使用认证完成后返回的访问令牌直接调用Facebook API来实现此操作。下面是具体流程:
在控制器方法中,您可以发起挑战(challenge)。
        var auth = await Request.HttpContext.AuthenticateAsync("Facebook");

这将在浏览器中将用户重定向到Facebook登录页面。
如果验证成功,即: auth.Succeeded && auth.Principal.Identities.Any(id => id.IsAuthenticated) && ! string.IsNullOrEmpty(auth.Properties.GetTokenValue("access_token") 像这样检索Facebook提供的身份验证令牌: auth.Properties.GetTokenValue("access_token") 然后使用该令牌手动获取用户的个人资料图片,如下所示:
    public async Task<string> GetFacebookProfilePicURL(string accessToken)
    {
        using var httpClient = new HttpClient();
        var picUrl = $"https://graph.facebook.com/v5.0/me/picture?redirect=false&type=large&access_token={accessToken}";
        var res = await httpClient.GetStringAsync(picUrl);
        var pic = JsonConvert.DeserializeAnonymousType(res, new { data = new PictureData() });
        return pic.data.Url;
    }

这里的PictureData只是一个类,代表了从Facebook的图形API返回的有关图片的所有信息,包括高度、宽度、URL等。


1
请注意,此代码仅适用于图形 API 5.0,当下载图像时不适用于 8.0 版本。 - Leandro Bardelli
你好,请问当图片下载完成时,你的意思是什么? - Damien Doumer
使用API GRAPH 8.0时,图像在浏览器中损坏,只能下载,无法迁移到<img src = url>。 - Leandro Bardelli
请问您能否发送关于此事的文档链接?谢谢。 - Damien Doumer
关于这种行为没有文档,但是你可以在选择版本8.0(最新版本)的API GRAPH中进行测试。 这是正确的答案,所以我投票支持它,但很遗憾在新版本中不起作用 :( 我有一个问题,但没有答案 :( - Leandro Bardelli
https://stackoverflow.com/questions/64074325/force-facebook-graph-api-to-show-profile-url-image-instead-download-it - Leandro Bardelli

1

还有一种可能性是使用自定义声明操作将json用户内容映射到声明(要使用的声明类型取决于您)。因此,图像url将被添加到声明集合中,如果您不需要OAuthEvents(如果您不需要它们用于其他目的),则无需使用它们。

.AddFacebook("FbCustom", x =>
        {
            x.AppId = settings.FacebookAppId;
            x.AppSecret = settings.FacebookSecret;
            x.Scope.Add("email");
            x.Scope.Add("user_hometown");
            x.Scope.Add("user_birthday");
            x.Fields.Add("birthday");
            x.Fields.Add("picture");
            x.Fields.Add("name");
            x.Fields.Add("email");
            //HERE IS CUSTOM A MAPPING
            x.ClaimActions.MapCustomJson(CustomClaimTypes.AvatarUrl, 
            json => json.GetProperty("picture").GetProperty("data").GetProperty("url").GetString());
            
        })

不确定创建自定义声明是否是良好的实践,但无论如何,这对我来说完成了工作,谢谢! - James Wilkinson

1

我只使用标识符从图形 API 获取了图像。

$"https://graph.facebook.com/{identifier}/picture?type=large";

您正在使用即将过时的 API 版本。我不会下投您的答案,因为它是正确的,但请考虑更改您的代码。 - Leandro Bardelli

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