在页面加载时,<s: token />产生一个GUID(Globally Unique Identifier,全局唯一标识符)值的隐藏输入框如:
<
input
type
="hidden"
name
="struts.token.name"
value
="struts.token"
/>
< input type ="hidden" name ="struts.token" value ="BXPNNDG6BB11ZXHPI4E106CZ5K7VNMHR" />
< 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中的被保存的随机数进行对比这两者相同服务器认为是第一次提交。