gpt4 book ai didi

ColdFusion 2021 - 如何处理同一服务器上多个应用程序的 SAML/SSO

转载 作者:行者123 更新时间:2023-12-05 05:47:05 25 4
gpt4 key购买 nike

我们有一个服务器,其中包含大约十几个小应用程序,每个应用程序都位于服务器自己的子文件夹中(//URL/app1、//URL/app2 等)。

我的基本 SSO 身份验证往返工作正常。我使用我的 IDP 设置我的帐户,并将响应设置为转到通用登录页面 (ACS URL)。由于登录页面当前与所有应用程序共享,因此它位于与应用程序不同的单独文件夹中 (//URL/sso/acsLandingPage.cfm)

我现在正在开发我的第一个应用程序。我可以检测到用户未登录,所以我执行 initSAMLAuthRequest(idp, sp, relayState: "CALLING_PAGE_URL")然后它会出去,进行身份验证,然后返回到着陆页。

但我如何重定向回我的目标应用程序并告诉它用户已通过身份验证?

如果我只做 <cflocation url="CALLING_PAGE_URL" />原始应用不知道 SAML 请求。

我可以在原始应用程序中调用一个函数来判断当前浏览器/用户是否有打开的 session 吗?

我是否需要为每个应用程序设置单独的 SP,而不是一个公共(public)登录页面,每个应用程序都有自己的登录页面,以便它可以设置 session 变量以传回主应用程序? (IDP 将我们的应用程序视为“一个服务器”,如果这是处理此问题的最佳方式,我可以获得单独的 key )。

我目前对 ACS 登录页面的工作想法是解析 relayState URL 以找出哪个应用程序启动了 init 请求,然后执行如下操作:

ACSLandingPage.cfm

<cfset response = processSAMLResponse(idp, sp) />
<cfif find(response.relaystate, 'app1')>
<cfapplication name="app1" sessionmanagement="true" />
<cfelseif find(response.relaystate, 'app2')>
<cfapplication name="app2" sessionmanagement="true" />
</cfif>

<cfset session.authenticated_username = response.nameid />
<cflocation url="#response.relaystate#" />

不太理想,但我认为它可能会起作用。

我希望我只是忽略了一些简单的事情,非常感谢我能得到的任何帮助。

编辑:我上面在 ACSLandingPage 中使用

最佳答案

好的,这就是我最终解决这个问题的方法。可能不是“正确”的解决方案,但它对我有用。

完整的代码解决方案太长太复杂,并且依赖太多没有意义的本地调用,所以我试图将其简化为一些代码片段,这些代码片段将有意义地展示我的解决方案有效。

在每个应用程序中,Application.cfc 看起来有点像这样。每个应用程序的名称都设置为 Application.cfc 的路径。我们这样做是因为我们经常会在同一台服务器上运行代码库的“训练实例”,这些实例指向备用数据库架构,这样用户就可以在不破坏生产数据的情况下进行操作。

component {
this.name = hash(getCurrentTemplatePath());
...

在应用程序的 onRequestStart 函数中,它有点像这样:

    cfparam(session.is_authenticated, false);
cfparam(session.auth_username, '');
cfparam(application._auth_struct, {}); // will be important later

// part 1
// there will be code in this block later in the description

// part 2
if (NOT session.is_authenticated OR session.auth_username EQ '') {
var returnURL = '#getPageContext().getRequest().getScheme()#://#cgi.server_name#/#cgi.http_url#'; // points back to this calling page
// start the call
InitSAMLAuthRequest({
'idp' : 'IDP_NAME',
'sp' : 'SP_NAME',
'relayState': returnURL
});
}

// log them in
if (session.is_authenticated AND session.auth_username NEQ '' AND NOT isUserLoggedIn()) {
... do cflogin stuff here ...
}

// throw problems if we are not logged in by this point
if (NOT isUserLoggedIn()) {
... if we don't have a logged in user by this point do error handling and redirect them somewhere safe ...
}

这将启动与我们的 ID 提供商的 SAML 连接。提供商执行其任务并将用户返回到文件“https://myserver/sso/ProcessSAMLResponse.cfm”。

processSAMLResponse 使用 relayState 中设置的 returnURL 来确定哪个应用程序发起了请求,以便它可以获得应用程序的 Application.cfc 的路径。

<cfset response = ProcessSAMLResponse(idpname:"IDP_NAME", spname:"SP_NAME") />
<cfset returnURL = response.RELAYSTATE />

<cfif findNoCase("/app1", returnURL)>
<cfset appPath = "PHYSICAL_PATH_TO_APP1s_APPLICATION.CFC" />
<cfelseif findNoCase("/app2", returnURL)>
<cfset appPath = "PHYSICAL_PATH_TO_APP2s_APPLICATION.CFC" />
<cfelseif findNoCase("/app3", returnURL)>
<cfset appPath = "PHYSICAL_PATH_TO_APP3s_APPLICATION.CFC" />
...
</cfif>

<!--- initiate application --->
<cfapplication name="#hash(appPath)#" sessionmanagement="true"></cfapplication>

<!--- create a token (little more than a random string and a bit prettier than a UUID) --->
<cfset auth_token = hash(response.NAMEID & dateTimeFormat(now(), 'YYYYmmddHHnnssL'))/>

<cfset application._auth_struct[auth_token] = {
"nameid": lcase(response.NAMEID),
"expires": dateAdd('n', 5, now())
} />

<!--- append token (can also be done with a ?: if you are inclined) --->
<cfif NOT find("?", returnURL)>
<cfset returnURL &= "?auth_token=" & encodeForURL(auth_token) />
<cfelse>
<cfset returnURL &= "&auth_token=" & encodeForURL(auth_token) />
</cfif>

<!--- return to the calling page --->
<cflocation url="#returnURL#" addToken="No"/>

这会将它返回给应用程序。所以我们回到应用程序的 onRequestStart 以从上面填写第 1 部分块:

    cfparam(session.is_authenticated, false);
cfparam(session.auth_username, '');

// part 1
// look for an auth token
if (NOT session.is_authenticated AND session.auth_username EQ '' AND structKeyExists(URL, 'auth_token')) {
var auth_token = URL.auth_token;

// see if it exists in our auth struct (and has all fields)
if ( structKeyExists(application, "_auth_struct")
AND structKeyExists(application._auth_struct, auth_token)
AND isStruct(application._auth_struct[auth_token])
AND structKeyExists(application._auth_struct[auth_token], 'nameid')
AND structKeyExists(application._auth_struct[auth_token], 'expires')) {

// only load if not expired
if (application._auth_struct[auth_token].expires GT now()) {
session.is_authenticated = true;
session.auth_username = application._auth_struct[auth_token].nameid;
}
// remove token from struct to prevent replays
structDelete(application._auth_struct, auth_token);

} // token in auth struct?

// remove expired tokens
application._auth_struct = structFilter(application._auth_struct, function(key, value) {
return value.expires GT now();
});
} // auth_token?

// part 2
// .... from earlier

这就是我如何解决多个应用试图使用单个 IDP/SP 组合的问题。

重要注意事项:

  1. 这一切都是在 Intranet 服务器上完成的,因此我的安全性比在面向公众的服务器上要宽松得多。 (特别是,使用应用程序变量存储身份验证 token 可能容易受到大规模 DDOS 类型的攻击,这种攻击会淹没新 session 并填满可用内存)。
  • 1 的子集 - 这些应用程序每天在所有应用程序中获得数百个用户,如果您的网站每天获得数千次点击,像我这样将 token 存储在应用程序中可能对您来说内存效率不够.
  1. 我的 IDP 非常受限。如果我可以为每个应用程序创建不同的 SP 设置并让返回调用直接返回调用应用程序,那就更好了。

  2. 我跳过了一些检查和错误处理以保持示例简单。您应该对值进行更多测试,尤其是在实际调用 cflogin 之前确保 nameID 是有效用户。

  3. 在调用 initSAMLAuthRequest 之前,您可能需要添加一个 session 计数器,以防止出现错误时身份验证调用的无限循环(通过艰难的方式了解到)。

关于ColdFusion 2021 - 如何处理同一服务器上多个应用程序的 SAML/SSO,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/71085348/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com