您的位置:首页 > 编程语言 > Java开发

[置顶] Struts2 的token原理

2016-03-10 16:05 387 查看
在页面加载时,<s: token />产生一个GUID(Globally Unique Identifier,全局唯一标识符)值的隐藏输入框如:

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

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

同时,将GUID放到会话(session)中;在执行action之前,“token”拦截器将会话token与请求token比较,如果两者相同,则
将会话中的token删除并往下执行,否则向actionErrors加入错误信息。如此一来,如果用户通过某种手段提交了两次相同的请求,两个
token就会不同。
当加载页面的时候会调用<s: token /> 标签相应的tag

/*    */
/*    */ public class TokenTag extends AbstractUITag
/*    */ {
/*    */   private static final long serialVersionUID = 722480798151703457L;
/*    */
/*    */   public Component getBean(ValueStack stack, HttpServletRequest req, HttpServletResponse res)
/*    */   {
/* 41 */     return new Token(stack, req, res);
/*    */   }
/*    */ }


 然后条用父类相应帮助方法

public class TokenHelper
/*     */ {
//默认的token名字可以自己定义
/*     */    public static final String DEFAULT_TOKEN_NAME = "struts.token";
/*     */    public static final String TOKEN_NAME_FIELD = "struts.token.name";
/*  48 */   private static final Logger LOG = LoggerFactory.getLogger(TokenHelper.class);
/*  49 */   private static final Random RANDOM = new Random();
/*     */
/*     */   public static String setToken()
/*     */   {
/*  58 */     return setToken("struts.token");
/*     */   }
/*     */   //当页面初始化的时候调用setToken/*     */
public static String setToken(String tokenName)
/*     */   {
/*  68 */     Map session = ActionContext.getContext().getSession();
/*  69 */     String token = generateGUID();
/*     */     try {
//放入session中,键为struts.token:值token为一个随机数值
/*  71 */       session.put(tokenName, token);
/*     */     }
/*     */     catch (IllegalStateException e)
/*     */     {
/*  75 */       String msg = "Error creating HttpSession due response is commited to client. You can use the CreateSessionInterceptor or create the HttpSession from your action before the result is rendered to the client: " + e.getMessage();
/*  76 */       LOG.error(msg, e, new String[0]);
/*  77 */       throw new IllegalArgumentException(msg);
/*     */     }
/*     */
/*  80 */     return token;
/*     */   }
/*     */   //所得token的值
/*     */   public static String getToken()
/*     */   {
/*  90 */     return getToken("struts.token");
/*     */   }
/*     */
/*     */   public static String getToken(String tokenName)
/*     */   {
/* 100 */     if (tokenName == null) {
/* 101 */       return null;
/*     */     }
/* 103 */     Map params = ActionContext.getContext().getParameters();
/* 104 */     String[] tokens = (String[])(String[])params.get(tokenName);
/*     */
/* 107 */     if ((tokens == null) || (tokens.length < 1)) {
/* 108 */       LOG.warn("Could not find token mapped to token name " + tokenName, new String[0]);
/*     */
/* 110 */       return null;
/*     */     }
/*     */
/* 113 */     String token = tokens[0];
/*     */
/* 115 */     return token;
/*     */   }
/*     */    //所得struts.token.name 隐藏表达欲的值 token.name
/*     */   public static String getTokenName()
/*     */   {
/* 124 */     Map params = ActionContext.getContext().getParameters();
/*     */
/* 126 */     if (!params.containsKey("struts.token.name")) {
/* 127 */       LOG.warn("Could not find token name in params.", new String[0]);
/*     */
/* 129 */       return null;
/*     */     }
/*     */
/* 132 */     String[] tokenNames = (String[])(String[])params.get("struts.token.name");
/*     */
/* 135 */     if ((tokenNames == null) || (tokenNames.length < 1)) {
/* 136 */       LOG.warn("Got a null or empty token name.", new String[0]);
/*     */
/* 138 */       return null;
/*     */     }
/*     */
/* 141 */     String tokenName = tokenNames[0];
/*     */
/* 143 */     return tokenName;
/*     */   }
/*     */    //验证token是否重复提交
/*     */   public static boolean validToken()
/*     */   {
/* 153 */     String tokenName = getTokenName();
/*     */
/* 155 */     if (tokenName == null) {
/* 156 */       if (LOG.isDebugEnabled())
/* 157 */         LOG.debug("no token name found -> Invalid token ", new String[0]);
/* 158 */       return false;
/*     */     }
/*     */         //通过tokenName获得页面初始化token的值
/* 161 */     String token = getToken(tokenName);
/*     */
/* 163 */     if (token == null) {
/* 164 */       if (LOG.isDebugEnabled())
/* 165 */         LOG.debug("no token found for token name " + tokenName + " -> Invalid token ", new String[0]);
/* 166 */       return false;
/*     */     }
/*     */       //获取session中的token...
/* 169 */     Map session = ActionContext.getContext().getSession();
/* 170 */     String sessionToken = (String)session.get(tokenName);
/*     */       //判断session中的token值和页面上的token值是否相等
/* 172 */     if (!token.equals(sessionToken)) {
/* 173 */       LOG.warn(LocalizedTextUtil.findText(TokenHelper.class, "struts.internal.invalid.token", ActionContext.getContext().getLocale(), "Form token {0} does not match the session token {1}.", new Object[] { token, sessionToken }), new String[0]);
/*     */
/* 177 */       return false;
/*     */     }
/*     */
/* 181 */     session.remove(tokenName);
/*     */
/* 183 */     return true;
/*     */   }
/*     */   //产生随机数
/*     */   public static String generateGUID() {
/* 187 */     return new BigInteger(165, RANDOM).toString(36).toUpperCase();
/*     */   }
/*     */ }

 

    当一个表单提交的时候。。。经过token拦截器。就相当于filter.Spring Aop 一样的类,具体配置如下

<action name="token" class="com.bhr.ssh.json.action.TokenAction">
<interceptor-ref name="defaultStack" />
<interceptor-ref name="token" />
<result name="invalid.token">/token.jsp</result>
<result>/token.jsp</result>
</action>

   然后在拦截器里面 获得页面上这个标签的value ; String tokenName =  getToken();

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

 然后在session里面找看是否有这个保存值,看是否session.getAttribute(tokenName);validToken();验证
一般来说第一次提交如果找到了,就把session.removeAttribute(tokenName);删除了。所以当重复提交的时候,tokenName 已经在session里面被清除了。没有保存在session里面,所以就。。。。。。。。。。。
总结:
通过Session Token :当客户端请求页面时,服务器会通过token标签生成一个随机数,并且将该随机数放到sesiion里面,然后将该随机数放到客户端,就是隐藏表单域,如果客户第一次提交,那么会将该随机数往服务器。被拦截。服务器接受到该随机数并且与session中的被保存的随机数进行对比这两者相同服务器认为是第一次提交。

 

 
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: