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

时间:2014-07-23 11:53:07

标签: cookies coldfusion session-variables coldfusion-10 coldfusion-8

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

自从迁移到CF 10以来,我遇到了很多麻烦,而不是"坚持"从页面到页面,特别是在登录过程之后。在迁移之前,我没有使用Cookie来跟踪客户端的值,也没有使用addtoken="yes"来查看cflocation标记(我更喜欢保留{{1}和URL之外的CFID值。)

我一直在对此进行大量研究,但我正在努力寻找解决方案。

我做错了什么或错过了什么?我应该没有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>

2 个答案:

答案 0 :(得分:3)

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

现在,如果您的应用程序中的其他位置也想使用这些相同的会话变量,我建议仅在它们不存在时才设置为默认值。使用以下代码可以轻松完成此操作(无需锁定):

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

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

答案 1 :(得分:0)

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

首先,正如@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">

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

我的login.cfm页面现在看起来像这样。我从application.cfm

移动了所有会话和cookie初始化
<!--- 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">

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

更新(2015年7月27日):我已修改此附加说明,以包含两条强制Cookie立即过期的cfcookie行。自从我发布这些信息以来的一年中,我注意到用户会因重复的cookie而遇到问题。如果用户在不清除浏览器cookie或完全关闭浏览器的情况下重新登录,则会保留其上一个会话中的cookie。这导致了一个冲突,系统会看到两对具有相同名称的cookie并感到困惑。强制“now”的到期日期有效地杀掉了旧的cookie并恢复了网站的健全性。