【问题标题】:.NET ASP.NET MVC application Facebook OAuth with defined scope具有已定义范围的 .NET ASP.NET MVC 应用程序 Facebook OAuth
【发布时间】:2013-02-05 22:15:35
【问题描述】:

用 Facebook OAuth 定义范围创建 .NET ASP.NET MVC 应用程序的最简单方法是什么?

我尝试了很多例子。 OAuthWebSecurity.RegisterClient 不支持添加更多 Facebook 范围。 Facebook application template 创建一个画布,我需要非画布应用程序。 FacebookScopedClient 未满,无法使用此修复程序。

你有什么建议?

我也支持 JavaScript/jQuery 解决方案。

【问题讨论】:

    标签: asp.net-mvc facebook asp.net-mvc-4 oauth


    【解决方案1】:

    可能我来晚了,但我前几天遇到了这个问题,我在网上发现只有几个糟糕的解决方案。所以我写了自己的解决方案,现在只能发布了。

    我利用了完成大部分工作的 DotNetOpenAuth.AspNet.Clients.OAuth2Client 类。我只是将其扩展到包括范围和额外的用户数据。

    public class FacebookExtendedClient : DotNetOpenAuth.AspNet.Clients.OAuth2Client
    {   
        protected FacebookClient facebookClient;
        protected string fields;
        protected string scope;
        protected Func<string, object, string> fieldTransformer;
        protected bool emailAsUsername;
        protected IDictionary<string, string> userData;
    
        private string[] splittedFields;
        private string[] splittedScope;
    
        protected const string serviceLoginBaseUrl = "https://www.facebook.com/dialog/oauth";
        protected const string serviceMeBaseUrl = "https://graph.facebook.com/me";
        protected const string serviceAccessTokenBaseUrl = "https://graph.facebook.com/oauth/access_token";
    
        /// <summary>
        /// Create an instrance of the class.
        /// </summary>
        /// <param name="appId">The App ID of the application used to connect to Facebook service.</param>
        /// <param name="appSecret">The App Secret of the application used to connect to Facebook service.</param>
        /// <param name="fields">
        /// String containing comma separated fields to add to the request.
        /// If empty the request will retrieve the default fields based of the specified scope.
        /// </param>
        /// <param name="fieldTransformer">
        /// Function to be applied to the values retrived from facebook.
        /// If null provided the method will try to cast values from object to string explicitly,
        /// an InvalidCastException will be thrown if the cast will not be possible.
        /// </param>
        /// <param name="scope">
        /// String containing comma separated permissions to add to the request.
        /// If empty the request will have the basic scope.
        /// </param>
        /// <param name="emailAsUsername">Makes the email of the facebook user used as authentication username.</param>
        public FacebookExtendedClient(string appId, string appSecret, string fields = "", Func<string, object, string> fieldTransformer = null, string scope = "", bool emailAsUsername = false)
            : base("facebook")
        {
            if (string.IsNullOrEmpty(appId))
                throw new ArgumentException("The appId argument can not be null or empty.", "appId");
            if (string.IsNullOrEmpty(appSecret))
                throw new ArgumentException("The appSecret argument can not be null or empty.", "appSecret");
    
            fields = fields.Replace(" ", "");
            scope = scope.Replace(" ", "");
            this.splittedFields = fields.Split(',');
            this.splittedScope = scope.Split(',');
    
            if (emailAsUsername == true && !this.splittedFields.Contains("email") && !this.splittedScope.Contains("email"))
                throw new ArgumentException("The scope argument must contain the 'email' permission and the 'email' field to allow emailAsUsername to true.", "scope");
    
            this.facebookClient = new FacebookClient();
            this.facebookClient.AppId = appId;
            this.facebookClient.AppSecret = appSecret;
            this.fields = fields;
            this.fieldTransformer = fieldTransformer;
            this.scope = scope;
            this.emailAsUsername = emailAsUsername;
        }
    
        public FacebookClient FacebookClient
        {
            get
            {
                return this.facebookClient;
            }
        }
    
        public IDictionary<string, string> UserData
        {
            get
            {
                return this.userData;
            }
        }
    
        protected override Uri GetServiceLoginUrl(Uri returnUrl)
        {
            Dictionary<string, object> parameters = new Dictionary<string, object>();
            parameters.Add("redirect_uri", returnUrl.AbsoluteUri);
    
            if (!string.IsNullOrEmpty(this.scope))
                parameters.Add("scope", this.scope);
    
            return this.facebookClient.GetLoginUrl(parameters);
        }
    
        protected override IDictionary<string, string> GetUserData(string accessToken)
        {
            // This method makes the AuthenticationResult's UserName property be the facebook username of the logged user,
            // but if the facebook username is missing the facebook id will be used.
            // If emailAsUsername is true then AuthenticationResult's UserName property is the email retrieved from facebook
            // and the facebook username can be retrieved by the key "fb_username" in this.userData
    
            FacebookClient facebookClient = new FacebookClient(accessToken);
    
            var getResult = facebookClient.Get<IDictionary<string, object>>("me", new { fields = this.fields });
            Dictionary<string, string> result = new Dictionary<string, string>();
    
            if (this.fieldTransformer != null)
            {
                foreach (var pair in getResult)
                    result.Add(pair.Key, this.fieldTransformer(pair.Key, pair.Value));
            }
            else
            {
                foreach (var pair in getResult)
                {
                    string value = pair.Value.ToString();
    
                    if (value == null)
                        throw new InvalidCastException("Cast not possible for the object associate to the key '" + pair.Key + "'.");
    
                    result.Add(pair.Key, value);
                }
            }
    
            if (this.splittedFields.Contains("username"))
                result["fb_username"] = result["username"];
    
            if (this.emailAsUsername)
                result["username"] = result["email"];
    
            this.userData = result;
    
            return result;
        }
    
        protected override string QueryAccessToken(Uri returnUrl, string authorizationCode)
        {
            UriBuilder builder = new UriBuilder(serviceAccessTokenBaseUrl);
            builder.Query = string.Format("client_id={0}&client_secret={1}&redirect_uri={2}&code={3}",
                this.facebookClient.AppId, this.facebookClient.AppSecret, HttpUtility.UrlEncode(Encoding.ASCII.GetBytes(returnUrl.AbsoluteUri)), authorizationCode);
    
            using (WebClient client = new WebClient())
            {
                string str = client.DownloadString(builder.Uri);
    
                if (string.IsNullOrEmpty(str))
                    return null;
    
                return HttpUtility.ParseQueryString(str)["access_token"];
            }
        }
    }
    

    您也可以像这样在 OAuthWebSecurity 中注册它(将 RegisterAuth 方法放在 Application_Start 中,就像在 InternetApplication 模板中一样):

    public static class AuthConfig
    {
        public static void RegisterAuth()
        {
            configuration.LoadFromAppSettings();
    
            OAuthWebSecurity.RegisterClient(new FacebookExtendedClient(
                "##YOUR_APP_ID##",
                "##YOUR_APP_SECRET##",
                "id,first_name,last_name,link,username,gender,email,age_range,picture.height(200)",
                new Func<string, object, string>(fieldsTransformer),
                "email"));
        }
    
        private static string fieldsTransformer(string key, object value)
        {
            switch (key)
            {
                case "picture":
                    var data = (value as IDictionary<string, object>)["data"] as IDictionary<string, object>;
                    return data["url"].ToString();
                case "age_range":
                    var min = (value as IDictionary<string, object>)["min"];
                    return min.ToString();
                default:
                    return value.ToString();
            }
        }
    }
    

    正如您在上面的示例中看到的,方法 fieldsTransformer 将获取所选字段的键和值,在这种情况下,它将 facebook 为图片检索的对象转换为图片的 url。这是一种方便的方法,如果为 Func 参数提供了 null,则会保存 values 对象的 JSON 表示。

    稍后检索客户端信息,登录后,可以这样做:

    [Authorize]
    public class HomeController : Controller
    {
        public ActionResult Index()
        {
            IDictionary<string, string> userData = (OAuthWebSecurity.GetOAuthClientData("facebook").AuthenticationClient as FacebookExtendedClient).UserData;
            string email = userData["email"];
    
            // If leave null the fieldTransform of the client you can access to complex properties like this:
            JObject picture = JObject.Parse(userData["picture"]);
            string url = (picture["data"] as JObject)["url"].ToString();
    
            ViewBag.Email = userData["email"];
            ViewBag.PictureUrl = url;
            return View();
        }
    }
    

    希望您喜欢这段代码,即使有点晚了! :)

    【讨论】:

    • 感谢分享这个 Dunadan。 FacebookExtendedClient 类中 FacebookClient 的命名空间是什么?如果我使用建议的 DotNetOpenAuth.AspNet.Clients 命名空间,我会收到大量使用 FacebookClient 的语法错误。
    • 啊,这是通过 NuGet 提供的 Facebook 客户端。
    • 是的,请注意使用正确的程序集:FacebookClient 是 Facebook 包中的一个,而不是 DotNetOpenAuth.AspNet.Clients,不幸的是它们具有相同的名称。
    • 我喜欢这种方法,并且在看到这篇文章之前使用了非常相似的东西。我担心的是,如果您的应用程序在不同的用户场景中的不同时间需要不同的权限,您如何参数化它而不会使 OAuth2Client 子类激增,每个子类都以不同的范围初始化?
    • @Dunadan,在 MVC 4 项目中我是这样实现的,首先在按钮名称(@p.Displayname)中显示“Facebook”,在 _ExternalLoginsListPartial.cshtml 中需要进行以下调整在 AuthConfig.cs 文件中: OAuthWebSecurity.RegisterClient(new FacebookExtendedClient(..."email"), "Facebook", null);第二:您在 HomeController/Index 中添加的代码(我猜是测试)必须在 Accountcontroller/ExternalLoginCallback 中放置/自定义。非常感谢您的代码,我非常喜欢这个实现!
    【解决方案2】:

    我正在 VS 2013 中使用 C# MVC 5 Web 应用程序编写我的第一个 Facebook 应用程序。

    在 AppStart\StartupAuth.cs 文件中,我做了如下修改。

    确保类中有以下 using 语句:

    using Microsoft.AspNet.Identity;
    using Microsoft.Owin;
    using Microsoft.Owin.Security.Cookies;
    using Microsoft.Owin.Security.Facebook;
    using Owin;`
    

    然后,在 ConfigureAuth 方法底部附近的 Facebook 身份验证部分:

    FacebookAuthenticationOptions fbOptions = new FacebookAuthenticationOptions();
    fbOptions.AppId="YOUR_APP_ID";
    fbOptions.AppSecret="YOUR_APP_SECRET";
    fbOptions.Scope.Add("user_about_me");
    fbOptions.Scope.Add("user_actions.books");
    fbOptions.Scope.Add("user_actions.music");
    //...more at the bottom
    fbOptions.Scope.Add("video_upload");
    fbOptions.Scope.Add("xmpp_login");
    app.UseFacebookAuthentication(fbOptions);
    

    就是这样!以下是截至 2014 年 2 月 1 日的完整列表:

    fbOptions.Scope.Add("user_about_me");
    fbOptions.Scope.Add("user_actions.books");
    fbOptions.Scope.Add("user_actions.music");
    fbOptions.Scope.Add("user_actions.news");
    fbOptions.Scope.Add("user_actions.video");
    fbOptions.Scope.Add("user_activities");
    fbOptions.Scope.Add("user_birthday");
    fbOptions.Scope.Add("user_checkins");
    fbOptions.Scope.Add("user_education_history");
    fbOptions.Scope.Add("user_events");
    fbOptions.Scope.Add("user_friends");
    fbOptions.Scope.Add("user_games_activity");
    fbOptions.Scope.Add("user_groups");
    fbOptions.Scope.Add("user_hometown");
    fbOptions.Scope.Add("user_interests");
    fbOptions.Scope.Add("user_likes");
    fbOptions.Scope.Add("user_location");
    fbOptions.Scope.Add("user_notes");
    fbOptions.Scope.Add("user_online_presence");
    fbOptions.Scope.Add("user_photo_video_tags");
    fbOptions.Scope.Add("user_photos");
    fbOptions.Scope.Add("user_questions");
    fbOptions.Scope.Add("user_relationship_details");
    fbOptions.Scope.Add("user_relationships");
    fbOptions.Scope.Add("user_religion_politics");
    fbOptions.Scope.Add("user_status");
    fbOptions.Scope.Add("user_subscriptions");
    fbOptions.Scope.Add("user_videos");
    fbOptions.Scope.Add("user_website");
    fbOptions.Scope.Add("user_work_history");
    
    fbOptions.Scope.Add("friends_about_me");
    fbOptions.Scope.Add("friends_actions.books");
    fbOptions.Scope.Add("friends_actions.music");
    fbOptions.Scope.Add("friends_actions.news");
    fbOptions.Scope.Add("friends_actions.video");
    fbOptions.Scope.Add("friends_activities");
    fbOptions.Scope.Add("friends_birthday");
    fbOptions.Scope.Add("friends_checkins");
    fbOptions.Scope.Add("friends_education_history");
    fbOptions.Scope.Add("friends_events");
    fbOptions.Scope.Add("friends_games_activity");
    fbOptions.Scope.Add("friends_groups");
    fbOptions.Scope.Add("friends_hometown");
    fbOptions.Scope.Add("friends_interests");
    fbOptions.Scope.Add("friends_likes");
    fbOptions.Scope.Add("friends_location");
    fbOptions.Scope.Add("friends_notes");
    fbOptions.Scope.Add("friends_online_presence");
    fbOptions.Scope.Add("friends_photo_video_tags");
    fbOptions.Scope.Add("friends_photos");
    fbOptions.Scope.Add("friends_questions");
    fbOptions.Scope.Add("friends_relationship_details");
    fbOptions.Scope.Add("friends_relationships");
    fbOptions.Scope.Add("friends_religion_politics");
    fbOptions.Scope.Add("friends_status");
    fbOptions.Scope.Add("friends_subscriptions");
    fbOptions.Scope.Add("friends_videos");
    fbOptions.Scope.Add("friends_website");
    fbOptions.Scope.Add("friends_work_history");
    
    fbOptions.Scope.Add("ads_management");
    fbOptions.Scope.Add("ads_read");
    fbOptions.Scope.Add("create_event");
    fbOptions.Scope.Add("create_note");
    fbOptions.Scope.Add("email");
    fbOptions.Scope.Add("export_stream");
    fbOptions.Scope.Add("manage_friendlists");
    fbOptions.Scope.Add("manage_notifications");
    fbOptions.Scope.Add("manage_pages");
    fbOptions.Scope.Add("photo_upload");
    fbOptions.Scope.Add("publish_actions");
    fbOptions.Scope.Add("publish_checkins");
    fbOptions.Scope.Add("publish_stream");
    fbOptions.Scope.Add("read_friendlists");
    fbOptions.Scope.Add("read_insights");
    fbOptions.Scope.Add("read_mailbox");
    fbOptions.Scope.Add("read_page_mailboxes");
    fbOptions.Scope.Add("read_requests");
    fbOptions.Scope.Add("read_stream");
    fbOptions.Scope.Add("rsvp_event");
    fbOptions.Scope.Add("share_item");
    fbOptions.Scope.Add("sms");
    fbOptions.Scope.Add("status_update");
    fbOptions.Scope.Add("video_upload");
    fbOptions.Scope.Add("xmpp_login");
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2013-01-27
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-10-24
      • 1970-01-01
      相关资源
      最近更新 更多