asp.net-mvc-4 – MVC4的DotNetOpenAuth TwitterClient示例不尊重先前的登录
|
如果我使用Internet应用程序模板创建ASP.NET MVC 4 Web应用程序,它会预先安装使用一系列OAuth和OpenID提供程序实现身份验证所需的所有组件和配置.只需将我的Twitter消费者密钥和秘密添加到AuthConfig.cs即可通过Twitter激活身份验证. 但是,它似乎没有像我期望的那样工作. 如果我尝试使用Twitter进行身份验证,则无论我是否已登录Twitter,它都会显示Twitter登录页面.它还让我退出Twitter,因此我不得不在下次浏览器访问Twitter时重新进行身份验证. 这是一个错误,还是需要一些额外的配置才能将其转换为更常见的无缝工作流程(对于像Google这样的其他提供商而言正常工作)? 提前致谢. 蒂姆 解决方法如果其他人遇到这个问题,我会在这里介绍我发现的内容(以及一个相当丑陋的解决方法).使用Fiddler检查DotNetOpenAuth和Twitter之间的HTTP流量,很明显,身份验证请求包含force_login = false查询字符串参数,这表明DNOA正常工作.但是,如果我使用Fiddler的脚本功能来修改出站请求并完全删除force_login参数,那么一切都会正常运行.我猜测Twitter的实现在这里是错误的,通过将任何force_login参数的存在视为等同于force_login = true. 由于我不认为可以让Twitter修改其API的行为,因此我调查了是否有更易于访问的解决方案. 查看DNOA代码,我发现dotNetOpenAuthWebConsumer.RequestAuthentication()方法无条件地将force_login = false参数添加到HTTP请求中(并在需要时随后修改为true). 因此,理想的解决方案是让DNOA对其身份验证请求参数提供更细粒度的控制,并使TwitterClient明确删除force_login = false参数.不幸的是,当前的DNOA代码库不直接支持这一点,但可以通过创建两个自定义类来实现相同的效果. 第一个是IOAuthWebWorker的自定义实现,它是原始DotNetOpenAuthWebConsumer类的直接副本,除了将重定向参数字典初始化为空字典的单行更改: using System;
using System.Collections.Generic;
using System.Net;
using DotNetOpenAuth.AspNet.Clients;
using DotNetOpenAuth.Messaging;
using DotNetOpenAuth.OAuth;
using DotNetOpenAuth.OAuth.ChannelElements;
using DotNetOpenAuth.OAuth.Messages;
namespace CustomDotNetOpenAuth
{
public class CustomDotNetOpenAuthWebConsumer : IOAuthWebWorker,IDisposable
{
private readonly WebConsumer _webConsumer;
public CustomDotNetOpenAuthWebConsumer(ServiceProviderDescription serviceDescription,IConsumerTokenManager tokenManager)
{
if (serviceDescription == null) throw new ArgumentNullException("serviceDescription");
if (tokenManager == null) throw new ArgumentNullException("tokenManager");
_webConsumer = new WebConsumer(serviceDescription,tokenManager);
}
public HttpWebRequest PrepareAuthorizedRequest(MessageReceivingEndpoint profileEndpoint,string accessToken)
{
return _webConsumer.PrepareAuthorizedRequest(profileEndpoint,accessToken);
}
public AuthorizedTokenResponse ProcessUserAuthorization()
{
return _webConsumer.ProcessUserAuthorization();
}
public void RequestAuthentication(Uri callback)
{
var redirectParameters = new Dictionary<string,string>();
var request = _webConsumer.PrepareRequestUserAuthorization(callback,null,redirectParameters);
_webConsumer.Channel.PrepareResponse(request).Send();
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
if (disposing)
{
_webConsumer.Dispose();
}
}
}
}
另一个要求是基于原始TwitterClient类的自定义OAuthClient类.请注意,这需要比原始TwitterClient类多一些代码,因为它还需要复制DNOA基类或其他实用程序类内部的几个方法: using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Xml;
using System.Xml.Linq;
using DotNetOpenAuth.AspNet;
using DotNetOpenAuth.AspNet.Clients;
using DotNetOpenAuth.Messaging;
using DotNetOpenAuth.OAuth;
using DotNetOpenAuth.OAuth.ChannelElements;
using DotNetOpenAuth.OAuth.Messages;
namespace CustomDotNetOpenAuth
{
public class CustomTwitterClient : OAuthClient
{
private static readonly string[] UriRfc3986CharsToEscape = new[] { "!","*","'","(",")" };
public static readonly ServiceProviderDescription TwitterServiceDescription = new ServiceProviderDescription
{
RequestTokenEndpoint = new MessageReceivingEndpoint("https://api.twitter.com/oauth/request_token",HttpDeliveryMethods.GetRequest | HttpDeliveryMethods.AuthorizationHeaderRequest),UserAuthorizationEndpoint = new MessageReceivingEndpoint("https://api.twitter.com/oauth/authenticate",AccessTokenEndpoint = new MessageReceivingEndpoint("https://api.twitter.com/oauth/access_token",TamperProtectionElements = new ITamperProtectionChannelBindingElement[] { new HmacSha1SigningBindingElement() },};
public CustomTwitterClient(string consumerKey,string consumerSecret)
: this(consumerKey,consumerSecret,new AuthenticationOnlyCookieOAuthTokenManager())
{
}
public CustomTwitterClient(string consumerKey,string consumerSecret,IOAuthTokenManager tokenManager)
: base("twitter",new CustomDotNetOpenAuthWebConsumer(TwitterServiceDescription,new SimpleConsumerTokenManager(consumerKey,tokenManager)))
{
}
protected override AuthenticationResult VerifyAuthenticationCore(AuthorizedTokenResponse response)
{
var accessToken = response.AccessToken;
var userId = response.ExtraData["user_id"];
var userName = response.ExtraData["screen_name"];
var profileRequestUrl = new Uri("https://api.twitter.com/1/users/show.xml?user_id=" + EscapeUriDataStringRfc3986(userId));
var profileEndpoint = new MessageReceivingEndpoint(profileRequestUrl,HttpDeliveryMethods.GetRequest);
var request = WebWorker.PrepareAuthorizedRequest(profileEndpoint,accessToken);
var extraData = new Dictionary<string,string> { { "accesstoken",accessToken } };
try
{
using (var profileResponse = request.GetResponse())
{
using (var responseStream = profileResponse.GetResponseStream())
{
var document = xLoadXDocumentFromStream(responseStream);
AddDataIfNotEmpty(extraData,document,"name");
AddDataIfNotEmpty(extraData,"location");
AddDataIfNotEmpty(extraData,"description");
AddDataIfNotEmpty(extraData,"url");
}
}
}
catch
{
// At this point,the authentication is already successful. Here we are just trying to get additional data if we can. If it fails,no problem.
}
return new AuthenticationResult(true,ProviderName,userId,userName,extraData);
}
private static XDocument xLoadXDocumentFromStream(Stream stream)
{
const int maxChars = 0x10000; // 64k
var settings = new XmlReaderSettings
{
MaxCharactersInDocument = maxChars
};
return XDocument.Load(XmlReader.Create(stream,settings));
}
private static void AddDataIfNotEmpty(Dictionary<string,string> dictionary,XDocument document,string elementName)
{
var element = document.Root.Element(elementName);
if (element != null)
{
AddItemIfNotEmpty(dictionary,elementName,element.Value);
}
}
private static void AddItemIfNotEmpty(IDictionary<string,string key,string value)
{
if (key == null)
{
throw new ArgumentNullException("key");
}
if (!string.IsNullOrEmpty(value))
{
dictionary[key] = value;
}
}
private static string EscapeUriDataStringRfc3986(string value)
{
var escaped = new StringBuilder(Uri.EscapeDataString(value));
for (var i = 0; i < UriRfc3986CharsToEscape.Length; i++)
{
escaped.Replace(UriRfc3986CharsToEscape[i],Uri.HexEscape(UriRfc3986CharsToEscape[i][0]));
}
return escaped.ToString();
}
}
}
(编辑:安卓应用网) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |
- asp.net – 如何配置IIS以便在连接到SQL Server时使用用户的
- asp-classic – 检索ADO Recordset字段名称(经典ASP)
- 运行命令(CMD)大全
- asp.net-mvc – 使用MVC使用List填充@ Html.DropDownList
- asp.net-mvc-3 – 通过URL导致控制器的DateTime导致ASP .NE
- asp.net – Request.Url.AbsoluteUri和重写的URL
- ASP.NET MVC AJAX Sys是未定义的错误
- 全局导入/使用.NET中的别名
- asp.net-mvc – 命名约定 – 控制器的一个规则,没有模型和视
- asp.net-mvc-3 – 如何判断我的动作是否被RenderAction调用
- ASP.NET会话已过期或无法找到 – 因为Session.Se
- asp.net-mvc-5 – 如何让Glimpse与EF6合作?
- asp.net-mvc – 为什么LINQ to Entities不能识别
- asp.net-mvc – asp.net mvc单选按钮状态
- asp.net – 跟踪像素或javascript包括?
- asp.net-mvc – 在MVC Action中将SSRS报告导出为
- Asp.Net MVC3到MVC4升级工具?
- asp.net-mvc – 部署同一应用程序的2个版本
- asp.net-mvc-3 – asp.net mvc3返回原始html来查
- asp.net-mvc – JsonSerializer – 使用’N2’格
