提取 Facebook 身份验证令牌并代表用户上传照片的极其简单的方法
自定义身份验证提供程序,提取 Facebook 用户身份验证令牌和一个模拟 Facebook 用户的网站示例代码。
引言
如果您曾想知道像 Hootsuite 或 KontentKloud 这样的社交发布公司是如何保存用户 Facebook 登录数据并随后代表该用户发布消息到 Facebook 的,本文将为您解答。您还可以下载此处讨论的完整项目。
背景
Microsoft Visual Studio 的最新更新使得创建允许用户使用外部登录提供程序(如 Facebook)登录的网站变得非常容易。您所需要做的就是从 Facebook 开发者网站获取应用程序密钥。不幸的是,Microsoft 提供的默认 Facebook 身份验证提供程序不允许保存用户身份验证令牌以供以后重用。
在本文中,我将展示如何创建一个非常简单的身份验证提供程序,它能够实现这一点,允许您将 Facebook 身份验证令牌保存到数据库中,然后登录并模拟另一个用户上传图片到 Facebook。
Using the Code
以下是构建示例网站的 5 个步骤
- 创建默认 MVC 网站
- 添加自定义身份验证提供程序以提取用户令牌
- 在用户配置文件中添加令牌字段,用于保存令牌
- 自定义账户控制器,以便在注册期间实际将令牌保存到用户配置文件中
- 添加使用持久化令牌上传图片到 Facebook 的代码
这听起来可能很复杂,但实际上只需几行代码即可完成所有这些操作。
步骤 1
打开 Visual Studio 2013 并使用个人用户账户进行身份验证来创建默认 MVC 网站。
将项目命名为 PersistentFacebookAuth
。
第二步
向项目添加新类,命名为 MyFacebookAuthenticationProvider.cs。将此代码添加到类中
using System.Security.Claims;
using System.Threading.Tasks;
using Microsoft.Owin.Security.Facebook;
namespace PersistentFacebookAuth
{
public class MyFacebookAuthenticationProvider : FacebookAuthenticationProvider
{
public const string MyTokenClaimName = "my:FacebookToken";
public override Task Authenticated(FacebookAuthenticatedContext context)
{
context.Identity.AddClaim(new Claim(MyTokenClaimName, context.AccessToken));
return base.Authenticated(context);
}
}
}
此代码用一行代码扩展了默认的 FacebookAuthenticationProvider
,它从身份验证上下文中获取 AccessToken
并将其保存在 Claims
集合中。稍后,在 Account 控制器中,我们将使用声明名称 "my:FacebookToken
" 在 Claims
集合中找到该令牌。这是一个任意名称,您可以自己想一个,它必须在集合中是唯一的 - 为了方便起见,我们将其存储在 MyTokenClaimName
常量中。
我们现在准备好配置我们的网站,以利用新的身份验证提供程序进行 Facebook 登录。
在 App_Start 文件夹中,找到并打开 Startup.Auth.cs 文件。在代码中找到此处
//app.UseFacebookAuthentication(
// appId: "",
// appSecret: "");
如果您取消注释这些行,将使用默认提供程序。我们不需要那个。相反,我们将添加代码来指导应用程序使用我们的自定义提供程序
var fbOptions = new Microsoft.Owin.Security.Facebook.FacebookAuthenticationOptions
{
SignInAsAuthenticationType = DefaultAuthenticationTypes.ExternalCookie,
AppId = "--your id here --",
AppSecret = "-- your key here --",
Provider = new MyFacebookAuthenticationProvider()
};
fbOptions.Scope.Add("email");
fbOptions.Scope.Add("read_stream");
fbOptions.Scope.Add("publish_stream");
app.UseFacebookAuthentication(fbOptions);
不要忘记从 Facebook 开发者网站复制 AppId
和 AppSecret
密钥。(有关该过程的简单描述可以在此处找到)。
您还可以更改 Scope
集合 - 它定义了您的应用程序在身份验证时将向用户请求的权限。
步骤 3
现在,我们需要一个地方来存储身份验证令牌。任何可以容纳长 string
s 的地方都可以。在此示例中,我们在持有用户配置文件的 AspNetUsers
数据库表中添加了另一列。您真的不需要对该数据库了解太多。只需添加一行代码,项目中的默认后端将完成其余的工作。
在 Models 文件夹中,找到并打开 IdentityModels.cs 文件。将 FacebookToken
字段声明添加到 ApplicationUser
类中
public string FacebookToken { get; set; }
ApplicationUser
类现在应该像这样
public class ApplicationUser : IdentityUser
{
public string FacebookToken { get; set; }
public async Task<ClaimsIdentity> GenerateUserIdentityAsync(UserManager<ApplicationUser> manager)
{
// Note the authenticationType must match the one defined
// in CookieAuthenticationOptions.AuthenticationType
var userIdentity = await manager.CreateIdentityAsync
(this, DefaultAuthenticationTypes.ApplicationCookie);
// Add custom user claims here
return userIdentity;
}
}
在您网站首次运行时,将生成数据库并将 FacebookToken
列添加到 AspNetUsers
表中。
步骤 4
现在,我们需要修改 Account 控制器,以便在注册过程中实际将令牌保存到用户配置文件中。这发生在用户通过 Facebook 身份验证并确认注册之后。
在 Controllers 文件夹中,找到并打开 AccountController.cs 文件。找到 ExternalLoginConfirmation
函数
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<ActionResult> ExternalLoginConfirmation(ExternalLoginConfirmationViewModel model,
string returnUrl)
在函数内部,找到构造新的 ApplicationUser
的行
var user = new ApplicationUser { UserName = model.Email, Email = model.Email };
在这里,我们将添加我们自定义身份验证提供程序已经提取的身份验证令牌。在 ApplicationUser
构造函数上方添加一行新代码
var tokenClaim = info.ExternalIdentity.Claims.FirstOrDefault
(c => c.Type == MyFacebookAuthenticationProvider.MyTokenClaimName);
在这里,我们搜索 Claims
集合以找到我们自定义的持有身份验证令牌的声明。
现在,修改 ApplicationUser
构造函数以添加 FacebookToken
值
var user = new ApplicationUser
{ UserName = model.Email, Email = model.Email, FacebookToken = tokenClaim.Value };
保存令牌到用户配置文件的结果代码片段应该如下所示
var tokenClaim = info.ExternalIdentity.Claims.FirstOrDefault
(c => c.Type == MyFacebookAuthenticationProvider.MyTokenClaimName);
var user = new ApplicationUser
{ UserName = model.Email, Email = model.Email, FacebookToken = tokenClaim.Value };
var result = await UserManager.CreateAsync(user);
这完成了提取和保存 Facebook 用户身份验证令牌的部分。
下一步,我们将更改默认主页视图和控制器,以演示如何在无需用户登录的情况下,使用令牌将图片上传到用户的 Facebook 帐户。
步骤 5 - 上传示例
此步骤的目标是修改主页以显示文件上传按钮和已注册用户列表,允许您将图片上传到所选用户的 Facebook 个人资料。
为了节省您的时间,代码使用了可以从 NuGet 获取的 Facebook 库。右键单击项目名称,选择“管理 NuGet 包...”选项,然后在搜索框中输入“facebook
”
找到 Facebook
包并单击安装。您现在可以完成我们的项目了。
在 Controllers 文件夹中,找到并打开 HomeController.cs 文件。
我们这里要做的第一件事是显示当前已注册并拥有 Facebook 登录信息的用户的列表。像这样修改 Index
函数
public ActionResult Index()
{
var users = HttpContext.GetOwinContext().GetUserManager
<ApplicationUserManager>().Users.Where(u => u.FacebookToken != null).ToList();
if (users != null && users.Count > 0) ViewBag.Users = users;
return View();
}
在这里,我们将用户列表放入 ViewBag
。现在让我们修改主页 Index
视图。在 Views/Home 文件夹中,找到并打开 Index.cshtml 文件。
在任何你喜欢的地方添加这段 HTML 代码
@if (ViewBag.Users != null)
{
<div class="col-md-8">
@using (Html.BeginForm("Upload", "Home",
FormMethod.Post, new { enctype = "multipart/form-data" }))
{
<div class="form-horizontal">
<div class="form-group">
<label class="col-md-6 control-label">Select picture to upload:</label>
<div class="col-md-6">
<input type="file" name="file" />
</div>
</div>
<div class="form-group">
<label class="col-md-7 control-label">Choose Facebook account to upload
</label>
</div>
@foreach (PersistentFacebookAuth.Models.ApplicationUser user in ViewBag.Users)
{
<div class="form-group">
<label class="col-md-6 control-label">@user.Email</label>
<div class="col-md-6">
<button class="btn btn-success" id="userID"
name="userID" value="@user.Id">Upload</button>
</div>
</div>
}
</div>
}
</div>
}
这里最重要的是 HTML 表单、输入类型="file
" 和列出注册用户电子邮件以及相应提交按钮的 foreach
语句。当您点击按钮时,将调用 home 控制器的 Upload
操作,并传入相应的 userID
值。
现在让我们创建这个 Upload
动作函数,它将完成所有上传工作。
再次打开 HomeController.cs 文件。在那里添加此代码
[HttpPost]
public async Task<ActionResult> Upload(string userID)
{
if (Request.Files.Count > 0)
{
var selectedUser = await HttpContext.GetOwinContext().GetUserManager
<ApplicationUserManager>().FindByIdAsync(userID);
var facebookUserID = selectedUser.Logins.Where
(u => u.LoginProvider == "Facebook").Select(u => u.ProviderKey).FirstOrDefault();
var facebookUserToken = selectedUser.FacebookToken;
var imgFile = Request.Files[0];
if (imgFile != null && imgFile.ContentLength > 0)
{
var pictureBytes = new byte[imgFile.ContentLength];
imgFile.InputStream.Read(pictureBytes, 0, imgFile.ContentLength);
await uploadToFacebook(facebookUserID,
facebookUserToken, pictureBytes, Path.GetFileName(imgFile.FileName));
return Redirect("<a abp="9652"
href="https://#/">https://#/" + facebookUserID);
}
}
return RedirectToAction("Index");
}
首先,我们获取提供的 userID
并从配置文件数据库表中检索 selectedUser
数据。对于该用户,我们填充 facebookUserID
和 facebookUserToken
变量 - 我们将在上传图片到 Facebook 时使用这些值。
之后,我们从请求中检索图片文件名和图片字节数组,并将所有数据发送到 uploadToFacebook
函数。
以下是 uploadToFacebook
函数的代码,它实际上会将图片上传到 Facebook,并模拟选定的用户。
private async Task<bool> uploadToFacebook(string facebookUserID,
string token, byte[] pictureBytes, string fileName)
{
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls;
var mediaObj = new FacebookMediaObject
{
FileName = fileName,
ContentType = "image/" + Path.GetExtension(fileName).Substring(1),
};
mediaObj.SetValue(pictureBytes);
var args = new Dictionary<string, object>();
args.Add("source", mediaObj);
var client = new FacebookClient(token);
await client.PostTaskAsync(string.Format("/{0}/photos", facebookUserID), args);
return true;
}
在这里的第一行,我们告诉 .NET 库使用 TLS 安全协议 - Facebook 停止响应默认的 SSL3 请求。
然后,我们将图像字节打包到 FacebookMediaObject
中,并将该对象添加到参数集合中。
现在,我们来到在 FacebookClient
构造函数中使用身份验证令牌的地方。这实际上是整个练习的重点 - 使用注册过程中保存的令牌来模拟 Facebook 用户。
最后,我们将上传请求提交给 Facebook,并将请求者重定向到用户的图像库。
就是这样。您现在拥有一个可以正常运行的网站,允许您将图片上传到其他用户的 Facebook 相册。
下载源代码以查看所有代码并尝试一下。
请记住,这是一个示例,而不是生产质量的网站。此外,您绝不应允许陌生人发布到其他用户的 Facebook 个人资料(请参阅 Facebook 网站上的许可证和其他文档)。
关注点
类似的代码可用于连接到 Twitter 或 LinkedIn。如果您想知道如何实现,请在此处留言,我将发布另一篇包含更多详细信息的文章。
您可以在此处查看此代码在实际生产系统中的工作方式:https://my.kontentkloud.com/account/register/010basic
我计划撰写另一篇文章,介绍如何将您的网站连接到 Microsoft Office 365 商业云。如果您对此主题有具体问题,请给我留言。