如何使用 ASP.NET Identity (OWIN) 訪問 Facebook 私人資訊?
我正在用 ASP.NET MVC 5 開發一個網站(目前使用 RC1 版本)。該站點將使用 Facebook 進行使用者身份驗證和檢索初始配置文件數據。
對於身份驗證系統,我正在使用基於 OWIN 的新 ASP.NET 身份引擎(http://blogs.msdn.com/b/webdev/archive/2013/07/03/understanding-owin-forms-authentication-in-mvc -5.aspx),因為它極大地簡化了與外部提供商進行身份驗證的過程。
問題是,一旦使用者首次登錄,我想從 Facebook 個人資料中獲取其電子郵件地址,但此數據不包含在生成的聲明中。所以我考慮了這些替代方法來獲取地址:
- 指示 ASP.NET 身份引擎將電子郵件地址包含在從 Facebook 檢索然後轉換為聲明的數據集中。我不知道這是否可能。
- 使用 Facebook 圖形 API ( <https://developers.facebook.com/docs/getting-started/graphapi> ) 通過 Facebook 使用者 ID(包含在聲明數據中)檢索電子郵件地址。但是,如果使用者將他的電子郵件地址設置為私人,這將不起作用。
- 使用 Facebook 圖形 API,但指定“我”而不是 Facebook 使用者 ID ( <https://developers.facebook.com/docs/reference/api/user> )。但是需要訪問令牌,而且我不知道如何(或者是否可能)檢索 ASP.NET 用於獲取使用者數據的訪問令牌。
所以問題是:
- 如何指示 ASP.NET 身份引擎從 Facebook 檢索其他資訊並將其包含在聲明數據中?
- 或者,我怎樣才能檢索生成的訪問令牌,以便我可以自己詢問 Facebook?
謝謝!
注意:對於身份驗證系統,我的應用程序使用基於此 SO 答案中連結的範例項目的程式碼:https ://stackoverflow.com/a/18423474/4574
要從 facebook 檢索其他資訊,您可以在配置 facebook 身份驗證選項時指定要包含的範圍。可以通過實現提供者的 OnAuthenticated 方法來獲取檢索到的附加資訊,如下所示:
var facebookOptions = new Microsoft.Owin.Security.Facebook.FacebookAuthenticationOptions() { Provider = new FacebookAuthenticationProvider() { OnAuthenticated = (context) => { // All data from facebook in this object. var rawUserObjectFromFacebookAsJson = context.User; // Only some of the basic details from facebook // like id, username, email etc are added as claims. // But you can retrieve any other details from this // raw Json object from facebook and add it as claims here. // Subsequently adding a claim here will also send this claim // as part of the cookie set on the browser so you can retrieve // on every successive request. context.Identity.AddClaim(...); return Task.FromResult(0); } } }; //Way to specify additional scopes facebookOptions.Scope.Add("..."); app.UseFacebookAuthentication(facebookOptions);根據此處的程式碼,如果 facebook 已發送,我看到電子郵件已被檢索並在此處添加為聲明。你看不出來嗎?
在 Startup.ConfigureAuth (StartupAuth.cs) 中創建一個新的 Microsoft.Owin.Security.Facebook.AuthenticationOptions 對象,將 FacebookAppId、FacebookAppSecret 和一個新的 AuthenticationProvider 傳遞給它。您將使用 lambda 表達式向 OnAuthenticated 方法傳遞一些程式碼,以將聲明添加到包含您從 context.Identity 中提取的值的標識中。預設情況下,這將包括access_token。您必須將電子郵件添加到範圍。其他使用者屬性可從 context.User 獲得(例如,請參見底部的連結)。
啟動.Auth.cs
// Facebook : Create New App // https://dev.twitter.com/apps if (ConfigurationManager.AppSettings.Get("FacebookAppId").Length > 0) { var facebookOptions = new Microsoft.Owin.Security.Facebook.FacebookAuthenticationOptions() { AppId = ConfigurationManager.AppSettings.Get("FacebookAppId"), AppSecret = ConfigurationManager.AppSettings.Get("FacebookAppSecret"), Provider = new Microsoft.Owin.Security.Facebook.FacebookAuthenticationProvider() { OnAuthenticated = (context) => { context.Identity.AddClaim(new System.Security.Claims.Claim("urn:facebook:access_token", context.AccessToken, XmlSchemaString, "Facebook")); context.Identity.AddClaim(new System.Security.Claims.Claim("urn:facebook:email", context.Email, XmlSchemaString, "Facebook")); return Task.FromResult(0); } } }; facebookOptions.Scope.Add("email"); app.UseFacebookAuthentication(facebookOptions); }在 AccountController 中,我使用外部 cookie 從 AuthenticationManager 中提取 ClaimsIdentity。然後我將它添加到使用應用程序 cookie 創建的身份中。我忽略了任何以“…schemas.xmlsoap.org/ws/2005/05/identity/claims”開頭的聲明,因為它似乎破壞了登錄。
AccountController.cs
private async Task SignInAsync(CustomUser user, bool isPersistent) { AuthenticationManager.SignOut(DefaultAuthenticationTypes.ExternalCookie); var identity = await UserManager.CreateIdentityAsync(user, DefaultAuthenticationTypes.ApplicationCookie); // Extracted the part that has been changed in SignInAsync for clarity. await SetExternalProperties(identity); AuthenticationManager.SignIn(new AuthenticationProperties() { IsPersistent = isPersistent }, identity); } private async Task SetExternalProperties(ClaimsIdentity identity) { // get external claims captured in Startup.ConfigureAuth ClaimsIdentity ext = await AuthenticationManager.GetExternalIdentityAsync(DefaultAuthenticationTypes.ExternalCookie); if (ext != null) { var ignoreClaim = "http://schemas.xmlsoap.org/ws/2005/05/identity/claims"; // add external claims to identity foreach (var c in ext.Claims) { if (!c.Type.StartsWith(ignoreClaim)) if (!identity.HasClaim(c.Type, c.Value)) identity.AddClaim(c); } } }最後,我想顯示任何不是來自地方當局的值。我創建了顯示在/Account/Manage 頁面上的部分視圖 _ExternalUserPropertiesListPartial 。我從 AuthenticationManager.User.Claims 中獲取了之前儲存的聲明,然後將其傳遞給視圖。
AccountController.cs
[ChildActionOnly] public ActionResult ExternalUserPropertiesList() { var extList = GetExternalProperties(); return (ActionResult)PartialView("_ExternalUserPropertiesListPartial", extList); } private List<ExtPropertyViewModel> GetExternalProperties() { var claimlist = from claims in AuthenticationManager.User.Claims where claims.Issuer != "LOCAL AUTHORITY" select new ExtPropertyViewModel { Issuer = claims.Issuer, Type = claims.Type, Value = claims.Value }; return claimlist.ToList<ExtPropertyViewModel>(); }只是為了徹底,觀點:
_ExternalUserPropertiesListPartial.cshtml
@model IEnumerable<MySample.Models.ExtPropertyViewModel> @if (Model != null) { <legend>External User Properties</legend> <table class="table"> <tbody> @foreach (var claim in Model) { <tr> <td>@claim.Issuer</td> <td>@claim.Type</td> <td>@claim.Value</td> </tr> } </tbody> </table> }工作範例和完整程式碼在 GitHub 上: https ://github.com/johndpalm/IdentityUserPropertiesSample
我們將不勝感激任何回饋、更正或改進。