AD FS 2.0 身份验证和 AJAX

问题描述 投票:0回答:7

我有一个网站试图调用另一个网站上的 MVC 控制器操作。这些站点都设置为 AD FS 2.0 中的信赖方信任。在两个站点之间的浏览器窗口中打开页面时,一切都经过身份验证并且工作正常。但是,当尝试使用 jQuery AJAX 方法从 JavaScript 调用控制器操作时,它总是失败。这是我正在尝试做的代码片段......

$.ajax({
  url: "relyingPartySite/Controller/Action",
  data: { foobar },
  dataType: "json",
  type: "POST",
  async: false,
  cache: false,
  success: function (data) {
    // do something here
  },
  error: function (data, status) {
    alert(status);
  }
});

问题是 AD FS 使用 JavaScript 向依赖方发布隐藏的 html 表单。 使用 Fiddler 进行跟踪时,我可以看到它到达 AD FS 站点并返回此 html 表单,该表单应发布并重定向到经过身份验证的控制器操作。问题是这种形式作为 ajax 请求的结果返回并且显然会因解析器错误而失败,因为 ajax 请求需要来自控制器操作的 json。这似乎是一种常见的情况,那么从 AJAX 与 AD FS 通信并处理这种重定向的正确方法是什么?

javascript jquery ajax adfs2.0
7个回答
4
投票

你有两个选择。 更多信息这里.

第一个是在入口应用程序(基于 HTML 的应用程序)和您的 API 解决方案之间共享会话 cookie。您将这两个应用程序配置为使用相同的 WIF cookie。这仅在两个应用程序位于同一根域时才有效。 请参阅上面的帖子或这个 stackoverflow 问题.

另一个选项是为 AJAX 请求禁用 passiveRedirect(作为 Gutek 的回答)。这将返回 401 的 http 状态代码,您可以在 Javascript 中处理它。 当您检测到 401 时,您会在 iFrame 中加载一个虚拟页面(或一个“身份验证”对话框,如果需要再次提供凭据,它可以兼作登录对话框)。当 iFrame 完成后,您可以再次尝试调用。这次会话 cookie 将出现在调用中,它应该会成功。

//Requires Jquery 1.9+
var webAPIHtmlPage = "http://webapi.somedomain/preauth.html"

function authenticate() {
    return $.Deferred(function (d) {
        //Potentially could make this into a little popup layer 
        //that shows we are authenticating, and allows for re-authentication if needed
        var iFrame = $("<iframe></iframe>");
        iFrame.hide();
        iFrame.appendTo("body");
        iFrame.attr('src', webAPIHtmlPage);
        iFrame.load(function () {
            iFrame.remove();
            d.resolve();
        });
    });
};

function makeCall() {
    return $.getJSON(uri)
                .then(function(data) {
                        return $.Deferred(function(d) { d.resolve(data); });
                    },
                    function(error) {
                        if (error.status == 401) {
                            //Authenticating, 
                            //TODO:should add a check to prevnet infinite loop
                            return authenticate().then(function() {
                                //Making the call again
                                return makeCall();

                            });
                        } else {
                            return $.Deferred(function(d) {
                                d.reject(error);
                            });
                        }
                });
}

2
投票

如果您不想接收带有链接的 HTML,您可以在

AuthorizationFailed
上处理
WSFederationAuthenticationModule
并仅在 Ajax 调用中将
RedirectToIdentityProvider
设置为
false

例如:

FederatedAuthentication.WSFederationAuthenticationModule.AuthorizationFailed += (sender, e) =>
{
    if (Context.Request.RequestContext.HttpContext.Request.IsAjaxRequest())
    {
        e.RedirectToIdentityProvider = false;
    }
};

带有

Authorize
属性的这个将返回状态代码
401
如果你想要不同的东西,那么你可以实现自己的
Authorize
属性并在 Ajax 请求上编写特殊代码。


2
投票

在我目前使用的项目中,我们遇到了与客户端 SAML 令牌过期相同的问题,并导致 ajax 调用出现问题。在我们的特殊情况下,我们需要在遇到第一个 401 之后将所有请求排入队列,并且在成功验证后可以重新发送所有请求。身份验证使用 Adam Mills 建议的 iframe 解决方案,但在需要输入用户凭据的情况下也更进一步,这是通过显示一个对话框通知用户在外部视图上登录来完成的(因为 ADFS 不允许显示登录信息iframe 中的页面至少不是默认配置)在此期间等待请求正在等待完成,但用户需要从外部页面登录。如果用户选择取消,也可以拒绝等待的请求,在这种情况下,将为每个请求调用 jquery 错误。

这是一个带有示例代码的要点链接:

https://gist.github.com/kavhad/bb0d8e4a446496a6c05a

注意我的代码基于使用 jquery 来处理所有 ajax 请求。如果您的 ajax 请求由 vanilla javascript、其他库或框架处理,那么您或许可以在此示例中找到一些灵感。 jquery ui 的使用只是因为对话框,代表一小部分代码,可以很容易地换掉。

更新 抱歉,我更改了我的 github 帐户名,这就是链接不起作用的原因。它现在应该可以工作了。


0
投票

首先,您说您正在尝试对 another 网站进行 ajax 调用,您的调用是否符合 Web 浏览器的 same origin policy?如果是这样,那么您期望 html 作为服务器的响应,将 ajax 调用的

datatype
更改为
dataType: "html"
,然后将表单插入到您的 DOM 中。


0
投票

也许this serie的前两篇文章会对你有所帮助。他们考虑 ADFS 和 AJAX 请求

我想我会尝试做的是查看为什么不通过 ajax 传输身份验证 cookie,并找到一种方法将它们与我的请求一起发送。或者将 ajax 调用包装在一个函数中,该函数通过检索 html 表单进行预验证,将其隐藏到 DOM,提交它(它有望设置好的 cookie),然后发送您最初想要发送的适当请求


0
投票

只能做这种数据类型

"xml": Treat the response as an XML document that can be processed via jQuery. 

"html": Treat the response as HTML (plain text); included script tags are evaluated. 

"script": Evaluates the response as JavaScript and evaluates it. 

"json": Evaluates the response as JSON and sends a JavaScript Object to the success callback. 

如果您在提琴手中看到仅返回 html,则将您的数据类型更改为 html,或者如果只有脚本代码,则您可以使用脚本。


-1
投票

您应该创建一个任意名称的文件,如 json.php,然后将连接连接到 relayparty 网站,这应该可以工作

$.ajax({
            url: "json.php",
            data: { foobar },
            dataType: "json",
            type: "POST",
            async: false,
            cache: false,
            success: function (data) {
                // do something here
            },
            error: function (data, status) {
                alert(status);
            }
        });

© www.soinside.com 2019 - 2024. All rights reserved.