2

作为这个问题的延续,我遇到了 dotnetopenauth 的问题。

我导航到我的依赖方代码并创建请求,但是当我的提供者收到请求OpenIdProvider.GetRequest()时返回 null。我浏览了代码,据我所知,这是因为我的依赖方没有交付 openid 有效负载(request.form);但我不知道为什么会这样。

代码:

依赖方:

public ActionResult Authenticate(string RuserName = "")
{
UriBuilder returnToBuilder = new UriBuilder(Request.Url);
returnToBuilder.Path = "/OpenId/Authenticate";
returnToBuilder.Query = null;
returnToBuilder.Fragment = null;

Uri returnTo = returnToBuilder.Uri;
returnToBuilder.Path = "/";
Realm realm = returnToBuilder.Uri;

var response = openid.GetResponse();

if (response == null) {
    if (Request.QueryString["ReturnUrl"] != null && User.Identity.IsAuthenticated) {

    } else {

    string strIdentifier = "http://localhost:3314/User/Identity/" + RuserName;
    var request = openid.CreateRequest(
        strIdentifier,
        realm,
        returnTo);

    var fetchRequest = new FetchRequest();
    request.AddExtension(fetchRequest);
    request.RedirectToProvider();
    }
} else {
    switch (response.Status) {
        case AuthenticationStatus.Canceled:
            break;
        case AuthenticationStatus.Failed:
            break;
        case AuthenticationStatus.Authenticated:
            //log the user in
            break;
    }
}

return new EmptyResult();

}

提供者:

public ActionResult Index()
{
    IRequest request = OpenIdProvider.GetRequest();

    if (request != null) {
        if (request.IsResponseReady) {
            return OpenIdProvider.PrepareResponse(request).AsActionResult();
        }

        ProviderEndpoint.PendingRequest = (IHostProcessedRequest)request;
        return this.ProcessAuthRequest();
    } else {
        //user stumbled on openid endpoint - 404 maybe?
        return new EmptyResult();
    }
 }

public ActionResult ProcessAuthRequest()
    {
        if (ProviderEndpoint.PendingRequest == null) {
            //there is no pending request
            return new EmptyResult();
        }

        ActionResult response;
        if (this.AutoRespondIfPossible(out response)) {
            return response;
        }

        if (ProviderEndpoint.PendingRequest.Immediate) {
            return this.SendAssertion();
        }

        return new EmptyResult();
    }

日志:

RP:1)http://pastebin.com/Pnih3ND7 2)http://pastebin.com/eBzGun9y

提供者:http: //pastebin.com/YAUTBzHk

有趣的是,RP 日志说 localhost 是不受信任的......但是我将它添加到我的 web.config 中的白名单主机中,并且它昨天“工作”......

编辑:好的,这很奇怪。昨天我正在逐步浏览 DNOA 源,试图找出问题所在。我启用了 log4net,它创建了日志文件并将其留空。今天我再次设置了 log4net——它记录得很好,但是我遇到了一个没有意义的错误(见上文)。我也无法进入 DNOA 来源。我删除并重新添加了对 dotnetopenauth.dll 的引用,然后我与白名单主机的“原始错误”消失了,我能够进入源代码,但日志文件又是空白的。而且我仍然有 request.form 没有被填充的问题......

EDIT2:我的两个控制器都被命名为“OpenIdController”(在 RP 和 EP 上)。我的 RP 在 localhost:1903 上运行,我的端点在 localhost:3314 上运行。


EDIT3:在我进行更改后,您建议事情开始工作。RP 可以很好地执行发现,但是当它实际发出请求时我遇到了问题。

该行IRequest i_request = OpenIdProvider.GetRequest();工作正常,但是当我尝试转换它时:IAuthenticationRequest iR = (IAuthenticationRequest)i_request;它给了我以下错误:

 System.InvalidCastException was unhandled by user code
 Message=Unable to cast object of type     'DotNetOpenAuth.OpenId.Provider.AutoResponsiveRequest' to type 'DotNetOpenAuth.OpenId.Provider.IAuthenticationRequest'.
 Source=Portal
 StackTrace:
   at Portal.Controllers.OpenIdController.Index() in Controllers\OpendIdController.cs:line 35
   at lambda_method(Closure , ControllerBase , Object[] )
   at System.Web.Mvc.ActionMethodDispatcher.Execute(ControllerBase controller, Object[] parameters)
   at System.Web.Mvc.ReflectedActionDescriptor.Execute(ControllerContext controllerContext, IDictionary`2 parameters)
   at System.Web.Mvc.ControllerActionInvoker.InvokeActionMethod(ControllerContext controllerContext, ActionDescriptor actionDescriptor, IDictionary`2 parameters)
   at System.Web.Mvc.Async.AsyncControllerActionInvoker.<>c__DisplayClass42.<BeginInvokeSynchronousActionMethod>b__41()
   at System.Web.Mvc.Async.AsyncResultWrapper.<>c__DisplayClass8`1.<BeginSynchronous>b__7(IAsyncResult _)
   at System.Web.Mvc.Async.AsyncResultWrapper.WrappedAsyncResult`1.End()
   at System.Web.Mvc.Async.AsyncControllerActionInvoker.EndInvokeActionMethod(IAsyncResult asyncResult)
   at System.Web.Mvc.Async.AsyncControllerActionInvoker.<>c__DisplayClass37.<>c__DisplayClass39.<BeginInvokeActionMethodWithFilters>b__33()
   at System.Web.Mvc.Async.AsyncControllerActionInvoker.<>c__DisplayClass4f.<InvokeActionMethodFilterAsynchronously>b__49()

这段代码在我发现相关的两个样本之间有点大杂烩。我想设置一个 SSO 类型环境,所以我使用的大部分代码来自\DotNetOpenAuth-4.1.0.12182\Samples\OpenIdWebRingSsoProvider\Code\Util.cs(ProcessAuthenticationChallenge 函数)。但是,由于该函数需要一个IAuthenticationRequest但 OpenIdProvider.GetRequest 返回一个,AutoResponsiveRequest我想我可以强制转换它以使用IAuthenticationRequest该类的属性和方法。显然我错了。

在这一点上,我不太确定如何处理事情。我应该使用 OpenIdProviderMVC 示例中的示例代码吗?关键是登录就像单点登录一样工作,并且从未真正提示用户输入 OpenId。我也只会有一个端点(尽管我会有多个 RP)。

这是最新 RP 日志的链接:http: //pastebin.com/enpwYqq3


EDIT4:我按照你的建议做了,并取得了一些进展。据我所知,我的 EP 收到响应并对其进行处理,但是当它重定向回领域 url 时,它会出错。

012-10-10 13:55:01,171 (GMT-4) [25] ERROR DotNetOpenAuth.Messaging - Protocol error: An HTTP request to the realm URL (http://localhost:1903/) resulted in a redirect, which is not allowed during relying party discovery.

Realm与 相对的功能究竟是什么ReturnTo?使用示例代码,Realm 最终成为http://localhost:1903/并且 ReturnTo 最终成为http://localhost:1903/OpenId/Authenticate看起来很好。为什么 EP 需要向领域提出请求?我原以为它应该只是在完成处理后将断言发送到 returnTo 。如果我手动将 Realm 设置为http://localhost:1903/OpenId/Authenticate然后relyingParty.GetResponse()返回 null。

我确实将我的应用程序设置为在有人访问基本 url ( http://localhost:1903) 时重定向 - 我应该在那里运行什么代码来拦截 DNOA EP 请求?

新日志:

RP: http: //pastebin.com/L9K5Yft4

EP: http: //pastebin.com/kBPWiUxp

我还更新了问题开头的代码,以更好地反映我所做的更改。


EDIT5:领域是否必须是应用程序的实际基本 URL?也就是说,( http://localhost:1903)? 鉴于现有的架构,很难删除重定向 - 我尝试将领域设置为基本 OpenId 控制器 ( http://localhost:1903/OpenId),并且手动测试确实生成了 XRDS 文档。但是,应用程序似乎冻结了,EP 日志显示以下错误:

2012-10-10 15:17:46,000 (GMT-4) [24] ERROR DotNetOpenAuth.OpenId - Attribute Exchange extension did not provide any aliases in the if_available or required lists.

4

2 回答 2

2

您的 RP 有非常可疑的奇怪代码。虽然 return_to 和 realm 都具有相同的 Uri 权限是正常的(实际上是必需的),但您作为第一个参数传入的用户提供的标识符OpenIdRelyingParty.CreateRequest与您的依赖具有相同的主机和端口这一事实派对奇怪。通常,您传入的标识符是提供者托管的 URL 。现在,我不知道端口 3314 是您的 RP 还是您的 OP,但无论哪种方式,您的 RP 代码中的这些端口号之一看起来都是错误的。

其次,用户标识符的发现失败并出现空引用异常(根据您的 RP 日志的 v2)。这将阻止登录请求到达您的提供商。您的 Provider调用但 OpenID 请求不存在的事实表明这http://localhost:3314/OpenId/实际上是您的 OP Endpoint(读取 OpenID 请求的 OpenID Provider 的操作方法的 URL)。那是不合适的。同样,您应该传递给OpenIdRelyingParty.CreateRequest方法的第一个参数的 URL 应该是用户的 OpenID URL——而不是 OP Endpoint。查看 OpenIdProviderMvc 示例的用户控制器,了解如何设置用户 OpenID URL 的示例。然后使用URL 作为第一个参数CreateRequest,我认为你会很好。

第三,一旦您的 Provider 收到一个非空请求,您不能总是将其转换为IAuthenticationRequest. 并非所有 OpenID 消息都是身份验证消息。有些是底层 OpenID 协议的一部分。如果您查看 OpenIdProviderMvc 示例的 OpenID 控制器,您应该注意到有条件转换来处理不同的消息类型。您的控制器中应该有类似的消息处理。

由于您要使用 SSO 场景,因此控制器的显着差异可能是:

  1. 您的控制器从不响应重定向到登录页面,而是“神奇地”确定用户是谁。
  2. 您的控制器应根据IAuthenticationRequest.Realm您的 SSO Web 环中包含的 RP 白名单检查该属性,并且仅在 Realm 符合条件时提供肯定的断言。这可以减轻攻击,一旦您的服务器设置好,任何人都可以设置一个站点,该站点悄悄地使用您的 OpenID 提供程序来识别随机 Internet 网站的用户(如果他们属于您的组织),这将侵犯他们的隐私。

第四,OP 向您的 RP 的“领域”URL 发送的 HTTP 请求是 OpenID 称为“RP 发现”的过程的一部分。它可以减轻“开放重定向器”攻击。您应该将您的 RP 的基本 URL 调整为重定向,而是在 RP 发现请求进来时返回一个 XRDS 文档。对于普通浏览器情况,您仍然可以重定向。您可以在 OpenIdRelyingPartyMvc 示例的 HomeController 中看到如何执行此操作的示例。

于 2012-10-06T14:22:02.400 回答
1

从依赖方日志中可以看到:

ERROR DotNetOpenAuth.Messaging - Protocol error: The URL 'http://localhost:3314/OpenId/' is rated unsafe and cannot be requested this way.

您的 Provider 托管在 localhost 上,在生产服务器上它不是安全的 OpenID。所以默认情况下 localhost 是禁用的。您可以通过将 localhost 添加到您的白名单中来允许它进行本地测试,方法是将其添加到您的 web.config 文件(configSections顶部带有相应的):

<dotNetOpenAuth>
    <messaging>
        <untrustedWebRequest>
            <whitelistHosts>
                <add name="localhost" />
            </whitelistHosts>
        </untrustedWebRequest>
    </messaging>
</dotNetOpenAuth>
于 2012-09-28T00:20:11.207 回答