ColdFusion:会话变量不粘在网站从CF 8迁移到CF 10



我管理一个基于colfusion的网站,该网站最近从CF 8迁移到CF 10。该站点要求用户登录并在会话变量中保留某些值,这些值在整个站点中用于验证等。

自从迁移到CF 10以来,我一直有很多麻烦,会话不"粘"从一页到另一页,特别是在登录过程之后。在迁移之前,我没有使用cookie来跟踪客户端的值,也没有为我的cflocation标签使用addtoken="yes"(我更愿意将CFIDCFTOKEN值从URL中保留出来)。

我对此做了很多研究,但一直在努力寻找解决方案。

  • 我试过设置立即过期的"假"cookie(参见在Coldfusion中失去会话),但这并没有提供一个一致的解决方案。
  • 我也读过cflocation处理请求的方式在CF 9中发生了变化(具体来说,9.0.1;见http://www.horisk.com/blog/index.cfm/2011/5/19/session -问题-后-安装coldfusion - 901更新- onrequestend行为改变),但即使在改变我的cflocation添加令牌作为在测试中,会话一直被重置。
  • 如果我包含的页面出现在用户作为cfinclude登录之后,我的会话也出现了坚持,但一旦我点击到网站上的另一个页面,会话再次重置(我的猜测是由于服务器检查Application.cfm)。

我做错了什么或错过了什么?我不应该让Application.cfm设置默认会话值吗?我应该在登录页面上这样做吗?

作为参考,当前Application.cfm的读数如下(出于安全目的更改了某些值):

<cfapplication name="APPLICATIONNAME" 
applicationtimeout="#createtimespan(0,6,0,0)#" 
clientmanagement="yes" 
datasource="DATASOURCENAME" 
loginstorage="session" 
scriptprotect="all" 
sessionmanagement="yes" 
sessiontimeout="#createtimespan(0,1,0,0)#" 
setclientcookies="yes">
<cfif not structKeyExists(cookie,"cfid")>
    <cfcookie name="cfid" value="#session.cfid#" expires="never" domain="#cgi.SERVER_NAME#" path="/PATH">
    <cfcookie name="cftoken" value="#session.cftoken#" expires="never" domain="#cgi.SERVER_NAME#" path="/PATH">
</cfif>
<cflock timeout="5" throwontimeout="no" type="exclusive" scope="session">
        <cfif structKeyExists(session,"SESSIONVARA")>
            <cfscript>StructUpdate(session,"SESSIONVARA","DEFAULTVALUE");</cfscript>
        <cfelse>
            <cfscript>StructInsert(session,"SESSIONVARA","DEFAULTVALUE");</cfscript>
        </cfif>
        <cfif structKeyExists(session,"SESSIONVARB")>
            <cfscript>StructUpdate(session,"SESSIONVARB","DEFAULTVALUE");</cfscript>
        <cfelse>
            <cfscript>StructInsert(session,"SESSIONVARB","DEFAULTVALUE");</cfscript>
        </cfif>
        . . .
</cflock>

我当前的login.cfm页面如下所示(同样,某些值发生了变化):

<!--- check whether this variable was passed to this page --->
<cfif isdefined("form.username") and form.username is not "">
    <!--- generate a hashed password from the user's entry --->
    <cfset HashedPassword = hash(form.Password,"SHA-1")>
    [ SQL QUERY TO CHECK USER'S CREDENTIALS ]
    <cfif SQLQUERY.RecordCount is not 0>
        <cfset sessionRotate()>
        <cflock scope="session" type="exclusive" timeout="2">
            <cfset session.SESSIONVARA = QUERYVALUEA>
            <cfset session.SESSIONVARB = QUERYVALUEB>
            . . . 
        </cflock>
        <!--- put after sessionRotate() to keep IDs consistent --->
        <cfif structKeyExists(cookie,"cfid")>
            <cfset cookie.cfid = session.cfid>
            <cfset cookie.cftoken = session.cftoken>
        <cfelse>
            <cfcookie name="cfid" value="#session.cfid#" expires="never" domain="#cgi.SERVER_NAME#" path="/PATH">
            <cfcookie name="cftoken" value="#session.cftoken#" expires="never" domain="#cgi.SERVER_NAME#" path="/PATH">
        </cfif>
        <cflocation url="main.cfm" addtoken="no">
    <cfelse>
        <!--- no matching credentials --->
        <cflocation url="login-failed.cfm" addtoken="no">
    </cfif>
<!--- if there is no user name defined in the set of form variables, this is probably a spider or bot; reject it --->
<cfelse>
    <cflocation url="index.cfm" addtoken="no">
</cfif>

Edit:最后,这里是检查每个用户并在未授权时将其踢到超时页面的包含:

<cflock scope="session" type="readonly" timeout="2">
    <cfif not structKeyExists(session,"SESSIONVARA")>
        <cflocation url="TIMEOUTPAGE" addtoken="no">
    </cfif>
</cflock> 

应用程序中的代码。CFM将始终设置默认值,而不管之前是否设置过。由于您正在检查会话变量是否存在以确定用户是否已登录,因此我建议完全删除Application中的代码。设置/更新会话变量的CFM。这样,它将不存在,直到登录成功。

现在,如果应用程序中的其他地方也想使用这些相同的会话变量,我建议只在它们不存在的情况下设置为默认值。这很容易用下面的代码完成(不需要锁):

<cfparam name="session.sessionvara" default="defaultvalue">
<cfparam name="session.sessionvarb" default="defaultvalue">

然后更改您的安全检查,检查这些变量的值,而不是检查它们是否存在

感谢所有回答这个问题的人。我相信我已经解决了最初的问题。这个谜题有几个部分,我想在这里详细说明一下,以供将来参考。

首先,正如@BradWood所说,将我的默认会话声明移出application.cfm非常有帮助。我基本上是在每一页上重新设置我自己的值。我的application.cfm现在看起来像这样:

<cfapplication name="SITENAME" 
applicationtimeout="#createtimespan(0,6,0,0)#" 
clientmanagement="no" 
datasource="DATASOURCENAME" 
loginstorage="session" 
scriptprotect="all" 
sessionmanagement="yes" 
sessiontimeout="#createtimespan(0,1,0,0)#" 
setclientcookies="no">

我对cookie进行了更多的挖掘,并看到了Ben Nadel关于使用仅会话cookie的文章:http://www.bennadel.com/blog/1131-ask-ben-ending-coldfusion-session-when-user-closes-browser.htm。我喜欢这种方法,因为我宁愿让cookie在会话结束时结束,而且我的站点不需要记住任何人(人们大约每六个月访问一次以进行评估)。考虑到这一点,我将clientmanagementsetclientcookies属性都设置为"no"。

我的login.cfm页面现在看起来像这样。我把所有的会话和cookie初始化从application.cfm移到了这里:

<!--- check whether this variable was passed to this page --->
<cfif isdefined("form.username") and form.username is not "">
    <!--- generate a hashed password from the user's entry --->
    <cfset HashedPassword = hash(form.Password,"SHA-1")>
    [ SQL QUERY TO CHECK USER'S CREDENTIALS ]
    <cfif SQLQUERY.RecordCount is not 0>
        <cfset sessionRotate()>
        <cflock scope="session" type="exclusive" timeout="5">
            <!--- check to see if a user session has been started by looking for one of the variables --->
            <cfif structKeyExists(session,"SESSIONVARA")>
                <cfscript>
                    StructUpdate(session,"SESSIONVARA",VALUEA);
                    StructUpdate(session,"SESSIONVARB",VALUEB);
                    StructUpdate(session,"SESSIONVARC",VALUEC);
                    StructUpdate(session,"SESSIONVARD",VALUED);
                    StructUpdate(session,"SESSIONVARE",VALUEE);
                </cfscript>
            <cfelse>
                <cfscript>
                    StructInsert(session,"SESSIONVARA",VALUEA);
                    StructInsert(session,"SESSIONVARB",VALUEB);
                    StructInsert(session,"SESSIONVARC",VALUEC);
                    StructInsert(session,"SESSIONVARD",VALUED);
                    StructInsert(session,"SESSIONVARE",VALUEE);
                </cfscript>
            </cfif>
        </cflock>
        <!--- Set session-only cookies to help the site remember credentials. 
        Don't set an expire value in order to prevent CF from creating client cookies.
        If a cookie already exists, let's use that one and update the CFID and CFTOKEN 
        values to match the new session set in sessionRotate(). Also, don't declare the 
        "path" attribute in cfcookie: it will create a duplicate set of cookies and 
        confuse the site as to which is the right one. --->
        <cfif structKeyExists(cookie,"cfid")>
            <cfset cookie.cfid = session.cfid>
            <cfset cookie.cftoken = session.cftoken>
        <cfelse>
            <cfcookie name="cfid" value="#session.cfid#" domain="#cgi.SERVER_NAME#">
            <cfcookie name="cftoken" value="#session.cftoken#" domain="#cgi.SERVER_NAME#">
        </cfif>
        <cflocation url="main.cfm" addtoken="no">
    <cfelse>
        <cflocation url="login-failed.cfm" addtoken="no">
    </cfif>
<!--- if there is no user name defined in the set of form variables, this is probably a spider or bot; reject it --->
<cfelse>
    <cflocation url="index.cfm" addtoken="no">
</cfif>

最后,这是我的logout.cfm页面,它清除了仅会话的cookie:

<cfscript>
    StructDelete(cookie,"cfid",true);
    StructDelete(cookie,"cftoken",true);
</cfscript>
<!--- force cookies to expire --->
<cfcookie name="cfid" domain="#cgi.SERVER_NAME#" expires="now">
<cfcookie name="cftoken" domain="#cgi.SERVER_NAME#" expires="now">
<!--- force session to end --->
<cfset sessionInvalidate() />
<cflocation url="index.cfm" addtoken="no">

到目前为止,所有这些都很好地解决了我的会话问题。我希望这些信息能对有类似问题的人有所帮助。

Update (7-27-2015):我已经修改了这个额外的解释,包括两个强制cookie立即过期的cfcookie行。自从我发布这个信息以来的一年里,我注意到用户会因为重复的cookie而遇到问题。如果用户在没有清除浏览器cookie或完全关闭浏览器的情况下重新登录,则会保留以前会话的cookie。这导致了一个冲突,系统会看到两对具有相同名称的cookie并感到困惑。强制设置"现在"的过期日期有效地消灭了旧cookie,并恢复了站点的正常运行。