2013-04-11 25 views
2

希望得到的电子邮件ID一起与OpenID同时使用 互联网模板MVC4获取电子邮件ID如何使用asp.net mvc4的OpenID API

这是供身份验证使用谷歌,但没有为Facebook

知道怎么去/请求的电子邮件ID在字典而额外

http://aspnetwebstack.codeplex.com/在AspNetWebStack项目中的代码看,它看起来像

OAuthWebSecurity.RegisterFacebookClient() 

使得DotNetOpenAuth.AspNet.dll使用FacebookClient托管在https://github.com/AArnott/dotnetopenid

和FacebookClient.GetUserData()代码中有

    var userData = new NameValueCollection(); 
      userData.AddItemIfNotEmpty("id", graphData.Id); 
      userData.AddItemIfNotEmpty("username", graphData.Email); 
      userData.AddItemIfNotEmpty("name", graphData.Name); 
      userData.AddItemIfNotEmpty("link", graphData.Link == null ? null : graphData.Link.AbsoluteUri); 
      userData.AddItemIfNotEmpty("gender", graphData.Gender); 
      userData.AddItemIfNotEmpty("birthday", graphData.Birthday); 
      return userData; 

应在用户名返回的电子邮件ID,但它没有被退回

任何帮助表示赞赏

感谢

回答

1

ŧ他提供的Facebook OAuth客户端不会让你得到超出默认信息的任何东西。要获得其他任何内容,您需要能够更改scope参数的值,这是包含的客户端不允许的值。因此,为了解决这个问题并仍然使用Internet模板提供的其他样板代码,您需要实现一个遵循相同模式的自定义OAuth客户端。

由于整个ASP.NET源代码都是开源的,就像OAuth库DotNetOpenAuth一样,您实际上可以查看OAuth库并查看Facebook提供程序的构建方式。利用这一点,我能想出这样的:

using System; 
using System.Collections.Generic; 
using System.Diagnostics.CodeAnalysis; 
using System.Net; 
using System.Web; 

using DotNetOpenAuth.Messaging; 
using DotNetOpenAuth.AspNet; 
using DotNetOpenAuth.AspNet.Clients; 
using Validation; 

using Newtonsoft.Json; 

namespace OAuthProviders 
{ 
    /// <summary> 
    /// The facebook client. 
    /// </summary> 
    [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Facebook", Justification = "Brand name")] 
    public sealed class FacebookScopedClient : OAuth2Client 
    { 
     #region Constants and Fields 

     /// <summary> 
     /// The authorization endpoint. 
     /// </summary> 
     private const string AuthorizationEndpoint = "https://www.facebook.com/dialog/oauth"; 

     /// <summary> 
     /// The token endpoint. 
     /// </summary> 
     private const string TokenEndpoint = "https://graph.facebook.com/oauth/access_token"; 

     /// <summary> 
     /// The _app id. 
     /// </summary> 
     private readonly string appId; 

     /// <summary> 
     /// The _app secret. 
     /// </summary> 
     private readonly string appSecret; 

     private readonly string scope; 

     #endregion 

     #region Constructors and Destructors 

     /// <summary> 
     /// Initializes a new instance of the <see cref="FacebookScopedClient"/> class. 
     /// </summary> 
     /// <param name="appId"> 
     /// The app id. 
     /// </param> 
     /// <param name="appSecret"> 
     /// The app secret. 
     /// </param> 
     /// <param name="scope"> 
     /// The scope (requested Facebook permissions). 
     /// </param> 
     public FacebookScopedClient(string appId, string appSecret, string scope) 
      : base("facebook") 
     { 
      Requires.NotNullOrEmpty(appId, "appId"); 
      Requires.NotNullOrEmpty(appSecret, "appSecret"); 
      Requires.NotNullOrEmpty(scope, "scope"); 

      this.appId = appId; 
      this.appSecret = appSecret; 
      this.scope = scope; 
     } 

     #endregion 

     #region Methods 

     /// <summary> 
     /// The get service login url. 
     /// </summary> 
     /// <param name="returnUrl"> 
     /// The return url. 
     /// </param> 
     /// <returns>An absolute URI.</returns> 
     protected override Uri GetServiceLoginUrl(Uri returnUrl) 
     { 
      // Note: Facebook doesn't like us to url-encode the redirect_uri value 
      var builder = new UriBuilder(AuthorizationEndpoint); 
      builder.AppendQueryArgument("client_id", this.appId); 
      builder.AppendQueryArgument("redirect_uri", returnUrl.AbsoluteUri); 
      builder.AppendQueryArgument("scope", this.scope); 

      return builder.Uri; 
     } 

     /// <summary> 
     /// The get user data. 
     /// </summary> 
     /// <param name="accessToken"> 
     /// The access token. 
     /// </param> 
     /// <returns>A dictionary of profile data.</returns> 
     protected override IDictionary<string, string> GetUserData(string accessToken) 
     { 
      FacebookGraphData graphData; 
      var request = 
      WebRequest.Create("https://graph.facebook.com/me?access_token=" + UriDataStringRFC3986(accessToken)); 
      using (var response = request.GetResponse()) 
      { 
       using (var responseStream = response.GetResponseStream()) 
       { 
        graphData = OAuthJsonHelper.Deserialize<FacebookGraphData>(responseStream); 
       } 
      } 

      // this dictionary must contains 
      var userData = new Dictionary<string, string>(); 
      if (!string.IsNullOrEmpty(graphData.Id)) { userData.Add("id", graphData.Id); } 
      if (!string.IsNullOrEmpty(graphData.Email)) { userData.Add("username", graphData.Email); } 
      if (!string.IsNullOrEmpty(graphData.Name)) { userData.Add("name", graphData.Name); } 

      if (graphData.Link != null && !string.IsNullOrEmpty(graphData.Link.AbsoluteUri)) { userData.Add("link", graphData.Link == null ? null : graphData.Link.AbsoluteUri); } 

      if (!string.IsNullOrEmpty(graphData.Gender)) { userData.Add("gender", graphData.Gender); } 
      if (!string.IsNullOrEmpty(graphData.Birthday)) { userData.Add("birthday", graphData.Birthday); } 

      return userData; 
     } 

     /// <summary> 
     /// Obtains an access token given an authorization code and callback URL. 
     /// </summary> 
     /// <param name="returnUrl"> 
     /// The return url. 
     /// </param> 
     /// <param name="authorizationCode"> 
     /// The authorization code. 
     /// </param> 
     /// <returns> 
     /// The access token. 
     /// </returns> 
     protected override string QueryAccessToken(Uri returnUrl, string authorizationCode) 
     { 
      // Note: Facebook doesn't like us to url-encode the redirect_uri value 
      var builder = new UriBuilder(TokenEndpoint); 
      builder.AppendQueryArgument("client_id", this.appId); 
      builder.AppendQueryArgument("redirect_uri", NormalizeHexEncoding(returnUrl.AbsoluteUri)); 
      builder.AppendQueryArgument("client_secret", this.appSecret); 
      builder.AppendQueryArgument("code", authorizationCode); 
      builder.AppendQueryArgument("scope", this.scope); 

      using (WebClient client = new WebClient()) 
      { 
       string data = client.DownloadString(builder.Uri); 
       if (string.IsNullOrEmpty(data)) 
       { 
        return null; 
       } 

       var parsedQueryString = HttpUtility.ParseQueryString(data); 
       return parsedQueryString["access_token"]; 
      } 
     } 

     /// <summary> 
     /// Converts any % encoded values in the URL to uppercase. 
     /// </summary> 
     /// <param name="url">The URL string to normalize</param> 
     /// <returns>The normalized url</returns> 
     /// <example>NormalizeHexEncoding("Login.aspx?ReturnUrl=%2fAccount%2fManage.aspx") returns "Login.aspx?ReturnUrl=%2FAccount%2FManage.aspx"</example> 
     /// <remarks> 
     /// There is an issue in Facebook whereby it will rejects the redirect_uri value if 
     /// the url contains lowercase % encoded values. 
     /// </remarks> 
     private static string NormalizeHexEncoding(string url) 
     { 
      var chars = url.ToCharArray(); 
      for (int i = 0; i < chars.Length - 2; i++) 
      { 
       if (chars[i] == '%') 
       { 
        chars[i + 1] = char.ToUpperInvariant(chars[i + 1]); 
        chars[i + 2] = char.ToUpperInvariant(chars[i + 2]); 
        i += 2; 
       } 
      } 
      return new string(chars); 
     } 

     /// <summary> 
     /// The set of characters that are unreserved in RFC 2396 but are NOT unreserved in RFC 3986. 
     /// </summary> 
     private static readonly string[] UriRfc3986CharsToEscape = new[] { "!", "*", "'", "(", ")" }; 

     internal static string UriDataStringRFC3986(string value) 
     { 
      // Start with RFC 2396 escaping by calling the .NET method to do the work. 
      // This MAY sometimes exhibit RFC 3986 behavior (according to the documentation). 
      // If it does, the escaping we do that follows it will be a no-op since the 
      // characters we search for to replace can't possibly exist in the string. 
      var escaped = new StringBuilder(Uri.EscapeDataString(value)); 

      // Upgrade the escaping to RFC 3986, if necessary. 
      for (int i = 0; i < UriRfc3986CharsToEscape.Length; i++) 
      { 
       escaped.Replace(UriRfc3986CharsToEscape[i], Uri.HexEscape(UriRfc3986CharsToEscape[i][0])); 
      } 

      // Return the fully-RFC3986-escaped string. 
      return escaped.ToString(); 
     } 

     #endregion 
    } 
} 

有做这项工作,是需要一些依赖库,所有这些都可以在的NuGet。你已经有了DotNetOpenAuth;另一个是http://nuget.org/packages/Validation/。该OAuthJsonHelper是DotNetOpenAuth使用的内部类的副本 - 让这个供应商的工作,我不得不在自己的命名空间中重新实现它:

using System; 
using System.IO; 
using System.Runtime.Serialization.Json; 
using Validation; 

namespace OAuthProviders 
{ 
    /// <summary> 
    /// The JSON helper. 
    /// </summary> 
    internal static class OAuthJsonHelper 
    { 
     #region Public Methods and Operators 

     /// <summary> 
     /// The deserialize. 
     /// </summary> 
     /// <param name="stream"> 
     /// The stream. 
     /// </param> 
     /// <typeparam name="T">The type of the value to deserialize.</typeparam> 
     /// <returns> 
     /// The deserialized value. 
     /// </returns> 
     public static T Deserialize<T>(Stream stream) where T : class 
     { 
      Requires.NotNull(stream, "stream"); 

      var serializer = new DataContractJsonSerializer(typeof(T)); 
      return (T)serializer.ReadObject(stream); 
     } 

     #endregion 
    } 
} 

这一切提供原样,没有保证它可以通过复制/粘贴工作 - 取决于你如何将它整合到你的项目中。

+0

谢谢我一直在研究代码,但不太清楚如何将它们连接在一起,它看起来像内置的facebookclient.cs中有一个“范围”,它被设置为电子邮件,而你的构造函数有一个范围参数,我应该假设它应该设置为字符串“电子邮件”?再次感谢 – Kumar 2013-04-11 08:05:20

+0

@Kumar'scope'值需要是API中列出的一个或多个预定义的“权限”。 'email'是默认的,但您只会看到用户选择公开的信息。其余的权限可以在这里找到:https://developers.facebook.com/docs/reference/login/extended-permissions/ – 2013-04-11 08:36:18

+0

@Kumar它发生在我身上,你并不真的需要一个自定义的客户端来获取电子邮件info,因为它是默认信息的一部分。正如我刚才提到的,您可以访问的内容取决于用户的配置文件设置。此提供程序允许您请求许可执行诸如发布到用户时间表的内容(假设他们授权您的应用程序)。 – 2013-04-11 08:39:50