AD FS 2.0 身份验证和 AJAX
Posted
技术标签:
【中文标题】AD FS 2.0 身份验证和 AJAX【英文标题】:AD FS 2.0 Authentication and AJAX 【发布时间】:2011-12-06 11:15:40 【问题描述】:我有一个网站试图在另一个网站上调用 MVC 控制器操作。这些站点都设置为 AD FS 2.0 中的信赖方信任。在两个站点之间的浏览器窗口中打开页面时,一切都经过身份验证并且工作正常。但是,当尝试使用 jQuery AJAX 方法从 javascript 调用控制器操作时,它总是会失败。这是我正在尝试做的代码 sn-p...
$.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 通信并处理此重定向的正确方法是什么?
【问题讨论】:
如果 ajax 调用正在返回 HTML,显然您不想使用 json 解析器对其进行解析。将 dataType 更改为“html”,并发布一个返回的 html 示例,这样我就可以向您展示如何编写一个将提交返回的表单的处理程序。 问题是我想取回 JSON。 AD FS 使用它要发布的新 HTML 表单进行重定向,以执行所需的握手。这在浏览器窗口中可以正常工作,但在这里不行。一旦握手发生,AJAX 请求就不会重定向,我会返回 JSON。我现在想出了一个解决方法来处理 IFRAME 中的 html 页面帖子,但这并不理想。 我知道您想取回 JSON,但您不会取回 JSON。 但是,如果您希望能够将返回的数据结构视为 是 JSON,请发布返回的 HTML 示例,我将向您展示如何编写一个处理程序,该处理程序将不使用使用 IFRAME 提交返回的表单。 为什么在我的情况下它给了我错误“没有访问控制允许访问允许来源”?这是一个 CORS 问题,这意味着我的浏览器阻止了重定向到我的 ADFS 服务器,为什么您的请求可以重定向到 ADFS 服务器? 【参考方案1】:你有两个选择。 更多信息here。
第一个是在入口应用程序(基于 HTML 的应用程序)和您的 API 解决方案之间共享会话 cookie。您将两个应用程序配置为使用相同的 WIF cookie。这仅在两个应用程序位于同一根域上时才有效。 请参阅上面的帖子或*** question。
另一个选项是禁用 AJAX 请求的被动重定向(如Gutek's answer)。这将返回一个 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);
);
);
【讨论】:
iFrame 解决方案仅对我有效,方法是不删除 iframe,而是获取其内容 iFrame.load(function () var content = iFrame.contents(); resolve(); ); 如果我使用 Ajax 执行 GET,第二个选项对我有用。但是,如果我执行 POST,则浏览器不会将 iFrame 加载的 cookie 与请求一起发送。知道为什么吗?【参考方案2】:在我目前使用的项目中,我们遇到了客户端 SAML 令牌过期的相同问题,并导致 ajax 调用出现问题。在我们的特殊情况下,我们需要在遇到第一个 401 之后将所有请求排入队列,并且在成功验证后,所有请求都可以重新发送。身份验证使用 Adam Mills 建议的 iframe 解决方案,但在需要输入用户凭据的情况下也会走得更远,这是通过显示一个对话框来通知用户在外部视图上登录来完成的(因为 ADFS 不允许显示登录iframe 中的页面至少不是默认配置),在此期间等待请求正在等待完成,但用户需要从外部页面登录。如果用户选择取消,等待的请求也可能被拒绝,在这种情况下,将为每个请求调用 jquery 错误。
这是一个带有示例代码的要点的链接:
https://gist.github.com/kaveh82/bb0d8e4a446496a6c05a
请注意,我的代码基于使用 jquery 处理所有 ajax 请求。如果您的 ajax 请求正在由 vanilla javascript、其他库或框架处理,那么您也许可以在这个示例中找到一些灵感。使用 jquery ui 只是因为对话框,它代表一小部分代码,可以很容易地换出。
【讨论】:
你能再分享一下你的代码吗,GitHub的链接失效了,你能不能详细点,我和你的情况一样,正在使用ADFS和SAML Token,我启用了我的网站的跨域。当我向另一个站点执行 ajax 请求“获取”时,它会显示 iframe,对我来说,我不应该执行 iframe,我必须获取令牌并使用,任何解决方案都可以在没有身份验证和 iframe 的情况下进行获取,注意:用户已经登录 @kaveh-hadjari 能否再次分享代码链接。【参考方案3】:如果您不想接收带有链接的 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 请求上编写特殊代码。
【讨论】:
在 JS 中处理 403 并在安全区域内加载一个带有 html 页面的 iframe,所有糟糕的 adfs 重定向都会发生,你会得到你的 cookie...然后你可以重试调用 这应该(理论上)在Application_Start()
的Global.asax.cs
方法中可以正常工作——但不会。但是,它在Application_BeginRequest()
中工作得很好。
查看 MSDN 文章关于在 ASP.NET 应用程序中设置 AuthorizationFailed 事件的推荐方法msdn.microsoft.com/en-us/library/…【参考方案4】:
您应该创建一个任何名称的文件,例如 json.php,然后连接到中继方网站,这应该可以
$.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);
);
【讨论】:
【参考方案5】:你只能做这种数据类型
"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,或者如果只有脚本代码,那么您可以使用脚本。
【讨论】:
【参考方案6】:也许this serie 的前 2 篇帖子会对你有所帮助。他们考虑 ADFS 和 AJAX 请求
我想我会尝试做的是查看为什么不通过 ajax 传输身份验证 cookie,并找到一种方法将它们与我的请求一起发送。或者将 ajax 调用包装在一个函数中,该函数通过检索 html 表单进行预身份验证,将其隐藏到 DOM,提交它(希望它会设置好的 cookie),然后发送您最初想要发送的适当请求
【讨论】:
Cookie 确实会在握手发生时被传输。这个问题是第一次尝试在 AJAX 请求上发生握手。【参考方案7】:首先你说你正在尝试对另一个网站进行ajax调用,你的调用符合same origin policy的网络浏览器吗?如果是这样,那么您期望 html 作为服务器的响应,请将 ajax 调用的datatype
更改为dataType: "html"
,然后将表单插入到您的 DOM 中。
【讨论】:
以上是关于AD FS 2.0 身份验证和 AJAX的主要内容,如果未能解决你的问题,请参考以下文章
使用 Azure AD 承载令牌时身份验证失败,返回容器列表 [Azure Blob] [Azure AD OAuth 2.0] [REST API]
如何在 ASP.NET Core 3 上同时使用 Azure AD 身份验证和身份?
使用 kerberos 和 AD 的 Oracle 数据库身份验证