繁体   English   中英

AD FS 2.0身份验证和AJAX

[英]AD FS 2.0 Authentication and AJAX

我有一个网站试图在另一个网站上调用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进行通信并处理此重定向的正确方法是什么?

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

第一种是在条目应用程序(基于HTML的应用程序)和API解决方案之间共享会话cookie。 您将两个应用程序配置为使用相同的WIF cookie。 这仅适用于两个应用程序位于同一根域的情况。 请参阅上面的帖子或此stackoverflow问题

另一种选择是禁用AJR请求的passiveRedirect(作为Gutek的答案 )。 这将返回一个可以在Javascript中处理的http状态代码401。 当您检测到401时,您会在iFrame中加载虚拟页面(或“Authenticating”对话框,如果需要再次提供凭据,则该对话框可以兼作登录对话框)。 当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);
                            });
                        }
                });
}

如果您不希望接收带有链接的HTML,则可以在WSFederationAuthenticationModule上处理AuthorizationFailed ,并仅在Ajax调用上将RedirectToIdentityProvider设置为false

例如:

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

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

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

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

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

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

首先,你说你正在尝试对另一个网站进行ajax调用,你的调用是否符合Web浏览器的相同原始策略 如果确实如此,那么您希望将html作为服务器的响应,将ajax调用的datatype更改为dataType: "html" ,然后将表单插入到DOM中。

也许这个系列的2个帖子会帮助你。 他们考虑ADFS和AJAX请求

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

您只能执行此类数据类型

"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. 

如果您可以在fiddler中看到只返回html然后将数据类型更改为html,或者如果只有脚本代码,那么您可以使用脚本。

您应该创建一个文件任何名称,如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); } });

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM