[置顶] Struts2 的token原理

在页面加载时,<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中的被保存的随机数进行对比这两者相同服务器认为是第一次提交。

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值