我管理一个最近从CF 8迁移到CF 10的基于ColdFusion的网站。该网站要求用户登录并在会话变量中保留某些值,这些值在整个站点中用于验证等。
自从迁移到CF 10以来,我遇到了很多麻烦,而不是"坚持"从页面到页面,特别是在登录过程之后。在迁移之前,我没有使用Cookie来跟踪客户端的值,也没有使用addtoken="yes"
来查看cflocation
标记(我更喜欢保留{{1}和URL之外的CFID
值。)
我一直在对此进行大量研究,但我正在努力寻找解决方案。
CFTOKEN
处理CF 9中更改请求的方式(具体来说,
9.0.1;见http://www.horisk.com/blog/index.cfm/2011/5/19/Session-issues-after-installing-Coldfusion-901-update--OnRequestEnd-behaviour-change),
但即使在更改我的cflocation
以添加令牌之后也是如此
测试,会话不断重置。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>
答案 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。我喜欢这种方法,因为我宁愿让饼干以会话结束,而我的网站也不需要记住任何人(人们每六个月左右才会访问一次以进行评估)。考虑到这一点,我将clientmanagement
和setclientcookies
属性设置为“否”。
我的login.cfm
页面现在看起来像这样。我从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">
到目前为止,所有这些都很好地解决了我的会话问题。我希望所有这些信息对于有类似问题的其他人都有用。
更新(2015年7月27日):我已修改此附加说明,以包含两条强制Cookie立即过期的cfcookie行。自从我发布这些信息以来的一年中,我注意到用户会因重复的cookie而遇到问题。如果用户在不清除浏览器cookie或完全关闭浏览器的情况下重新登录,则会保留其上一个会话中的cookie。这导致了一个冲突,系统会看到两对具有相同名称的cookie并感到困惑。强制“now”的到期日期有效地杀掉了旧的cookie并恢复了网站的健全性。