当前位置: 代码迷 >> JavaScript >> JS取得token
  详细解决方案

JS取得token

热度:395   发布时间:2012-08-15 16:57:16.0
JS获得token

使用struts有一个很奇怪的事情,就是你不知道在什么时候,就放进来一个bug,重复地提交,而且渗透到好几个方法,甚至整个action都会被污染。像打补丁似的,struts本身可以有一个可以用来防止重复提交的拦截器:<interceptor-ref name="token"/> 

<action name="*action" class="UserAction" method="{1}">
	<interceptor-ref name="token"/>   
	<interceptor-ref name="defaultStack" />  

</action>

虽然这个token能够有效地防止重复提交,但是也能够让你原来的架构一团糟。可以拿一个亲身经历作为教材。在一个搭了frame的框架里,左边的frame是第一次加载之后静态不刷新的,点击左边,把内容显示在右边。来段代码:

<frameset rows="64,600"  frameborder="NO" border="0" framespacing="0" >
	<frame src="top.jsp" noresize="noresize" frameborder="0" name="topFrame" scrolling="no" marginwidth="0" marginheight="0" target="main" />
	  <frameset cols="20%,80%"  rows="600,*" id="contentFrame" border="1" >
		<frame src="left.jsp" name="leftFrame" noresize="noresize" marginwidth="0" marginheight="0" frameborder="0" scrolling="no" target="main" />
		<frame src="right.jsp" name="main" marginwidth="0" marginheight="0" frameborder="0" scrolling="auto" target="_self" onload="rightOnload()" />
	 </frameset>
 </frameset>
<noframes>  
 <body></body>  
</noframes>


不管点击左边还是右边,leftFrame都不会刷新。也就是说,如果左边的按钮点击之后,右边的表单是重复提交的(事实上我的就是这样),那么我们应该如何拿到最新的token?

1、首先左边页面不提交本身,要怎样传token?
2、左边frame点击之后,右边加载的frame不一定每一个frame都有token,如何确保token是最新的,跟session中是匹配的?

实验了很多次之后,把方法在这里贴出来,分享一下。

一开始的想法几乎是对的,就是在右边的frame页面加载的时候,在onload事件里,把token值保存到左边的静态页面上面,再在左边的链接点击时,在其onclick事件上,拼装出带token的url来。

//右边的onload事件
//每一次刷新当前页面时,把左边的页面的token更新
function onloadEvent(){
	//拿到左边的frame的document
	var leftDoc = self.parent.frames["leftFrame"].document;
	var leftToken = leftDoc.getElementsByName("struts.token").item(0).value;
	var rightToken = document.getElementsByName("struts.token").item(0).value;
	leftToken = rightToken;
}


//左边的onclick事件
//a标签点击事件
function manageClick2(){
	//拿到右边的document
	var documentObj = self.parent.frames["main"].document;
	//通过document拿到"struts.token"的值
	var tokenObj = documentObj.getElementsByName("struts.token").item(0);
	//null表示当前右边frame没有token标签,没有token标签,就拿到自己本身
	//的token值,因为右边加载的时候,会把左边的token更新
	if(tokenObj == null) { 
		var tokenValue = document.getElementsByName("struts.token").item(0).value;
		document.getElementById("userManage").href = 
		"findAllaction.action?struts.token.name=struts.token&struts.token="+tokenValue;
	}else{//右边存在token标签
		//拿到右边的frame的token值,并把它更新到本身的token值
		var documentObj = self.parent.frames["main"].document;
		var tokenValue = documentObj.getElementsByName("struts.token").item(0).value;
		var tokenName = documentObj.getElementsByName("struts.token.name").item(0).value;
		document.getElementById("userManage").href = 
		"findAllaction.action?struts.token.name="+tokenName+"&struts.token="+tokenValue;
	}
}

可能有朋友会疑问为什么要这样拼url,对比源文件和编译之后在浏览器端看到的源代码吧:

<!-- jsp中的源码 -->
<form method="post" name="hiddenForm">
	<input type="hidden" id="hiddenTest" value="123456Test" >
	<s:token></s:token>
</form>

<!-- 浏览器端看到的源码 -->
<form method="post" name="hiddenForm">

		<input type="hidden" id="hiddenTest" value="123456Test" >

		<input type="hidden" name="struts.token.name" value="struts.token" />
<input type="hidden" name="struts.token" value="C294HLLE33EEH9UM6TIY1EI9ZA37CPIN" />

 	</form>

所谓的struts标签,就是帮你写了一个hidden的input元素而已,然后在客户端生成一个32位的离散值,再把这个值提交回去,struts拦截器拦截到之后,就在同一次请求中,对第一次拦截到的值的表单做操作,第二次提交的表单,因为两次hex的值几乎不会重复,所以,这个hex值相当于数据库中版本戳的思想,把版本戳不匹配的请求拒绝。所以,我们可以再前台用javascript来拼装这样的url作为token拦截器需要的参数。没有拼装去请求token拦截器的action时,会报一个缺少token参数的异常。


第一个想法看似很成功,但是测试之后有一个局限性,就是点击左边,右边的frame没有token值,再点回右边有token的链接时,拿到左边的token值就跟session中不匹配了,被服务器拒绝。为什么呢?我试着把这个想法放到全局上,就是在组装左右frame的index.jsp上面,写onload事件,马上就达到想要的效果了,找了半天没有发现为什么会有这差异,可能有些带token的jsp页面没有考虑到,让右边加载的所有页面都把token写到左边,确保了最新的token值吧,所以就过关了。


糊里糊涂的记录了一下,希望是有用的记录。



  相关解决方案