session防止重复提交

http://blog.chinaunix.net/uid-26284395-id-3044216.html
针对这个博文进行了优化,将servlet改写为拦截器,其它action需要防止重复提交时只需在TokenInterceptor中的if下添加一个actionString;在页面中添加EL表达式就ok了
struts2 token不仅可以防止重复提交还可以防御CSRF,不过是在不存在XSS漏洞的情况下;使用struts2 token防止CSRF攻击(http://www.ibm.com/developerworks/cn/web/1102_niugang_csrf/),要保证token不会泄露,一般的做法是在内存中保存一个唯一的token,有提交操作的表单页面是使用CGI,PHP,JSP等生成的,保证只有你的页面才能通过内存取到这个token,这样跨站的网页是无法伪造的。
最安全的防止CSRF就是图片验证码了


1)TokenInterceptor.java
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.struts2.ServletActionContext;
import com.cmcc.db.session.TokenProcessor;
import com.opensymphony.xwork2.ActionInvocation;
import com.opensymphony.xwork2.ActionProxy;

public class TokenInterceptor extends org.apache.struts2.interceptor.TokenInterceptor {

 private static final long serialVersionUID = 1L;

    @Override
    protected String doIntercept(ActionInvocation invocation) throws Exception {
        int count = 0;
        HttpServletRequest req = (HttpServletRequest) invocation
                .getInvocationContext().get(ServletActionContext.HTTP_REQUEST);
        HttpServletResponse resp = (HttpServletResponse) invocation
                .getInvocationContext().get(ServletActionContext.HTTP_RESPONSE);
        resp.setContentType("text/html;charset=GBK");
        ActionProxy proxy = invocation.getProxy();
        String actionString = proxy.getNamespace() + "/"
                + proxy.getActionName() + "!" + proxy.getMethod() + ".action";
        if (actionString.startsWith("/user/user!add.action")) {
            TokenProcessor processor = TokenProcessor.getInstance();
            if (processor.isTokenValid(req)) {
                try {
                    Thread.sleep(5000);
                } catch (InterruptedException e) {
                    System.out.println(e);
                }
                System.out.println("submit : " + count);
                if (count % 2 == 1)
                    count = 0;
                else
                    count++;
                System.out.println("success");
                return invocation.invoke();
            } else {
                processor.saveToken(req);
                System.out.println("你已经提交了表单,同一表单不能提交两次。");
                return null;
            }

        } else {
            return invocation.invoke();
        }
    }
}

2)TokenProcessor.java
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
public class TokenProcessor {

static final String TOKEN_KEY = "org.sunxin.token";

private static TokenProcessor instance = new TokenProcessor();

public static TokenProcessor getInstance()

{

    return instance;

}

/**
 * 
 * 最近一次生成令牌值的时间戳。
 */

private long previous;

/**
 * 
 * 判断请求参数中的令牌值是否有效。
 */
public synchronized boolean isTokenValid(HttpServletRequest request) {
    // 得到请求的当前Session对象。
    HttpSession session = request.getSession(false);
    if (session == null) {
        return false;
    }
    // 从Session中取出保存的令牌值。
    String saved = (String) session.getAttribute(TOKEN_KEY);
    if (saved == null) {
        return false;
    }
    // 清除Session中的令牌值。
    resetToken(request);

    // 得到请求参数中的令牌值。

    String token = request.getParameter(TOKEN_KEY);
    if (token == null) {
        return false;
    }
    return saved.equals(token);
}
/**
 * 清除Session中的令牌值。
 */
public synchronized void resetToken(HttpServletRequest request)
{
    HttpSession session = request.getSession(false);
    if (session == null) {
        return;
    }
    session.removeAttribute(TOKEN_KEY);
}

/**
 * 产生一个新的令牌值,保存到Session中, 如果当前Session不存在,则创建一个新的Session。
 */
public synchronized void saveToken(HttpServletRequest request) {

    HttpSession session = request.getSession();
    String token = generateToken(request);
    if (token != null) {
        session.setAttribute(TOKEN_KEY, token);
    }
}

/**
 * 根据用户会话ID和当前的系统时间生成一个唯一的令牌。
 */
public synchronized String generateToken(HttpServletRequest request) {

    HttpSession session = request.getSession();
    try {
        byte id[] = session.getId().getBytes();
        long current = System.currentTimeMillis();
        if (current == previous) {
            current++;
        }
        previous = current;
        byte now[] = new Long(current).toString().getBytes();
        MessageDigest md = MessageDigest.getInstance("MD5");
        md.update(id);
        md.update(now);
        return toHex(md.digest());
    } catch (NoSuchAlgorithmException e) {
        return null;
    }
}

/**
 * 将一个字节数组转换为一个十六进制数字的字符串。
 */
private String toHex(byte buffer[]) {
    StringBuffer sb = new StringBuffer(buffer.length * 2);
    for (int i = 0; i < buffer.length; i++) {
        sb.append(Character.forDigit((buffer[i] & 0xf0) >> 4, 16));
        sb.append(Character.forDigit(buffer[i] & 0x0f, 16));
    }
    return sb.toString();
}

/**
 * 从Session中得到令牌值,如果Session中没有保存令牌值,则生成一个新的令牌值。
 */
public synchronized String getToken(HttpServletRequest request) {
    HttpSession session = request.getSession(false);
    if (null == session)
        return null;
    String token = (String) session.getAttribute(TOKEN_KEY);
    if (null == token) {
        token = generateToken(request);
        if (token != null) {
            session.setAttribute(TOKEN_KEY, token);
            return token;
        } else
            return null;
    } else
        return token;
}

}
3)其它代码
①需要防止重复提交的页面

<%@ page import="com.cmcc.db.session.TokenProcessor" language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
**<%
   //获取令牌类实例
    TokenProcessor processor = TokenProcessor.getInstance();    //获取令牌值
    String token = processor.getToken(request);
%>**

<head>
<title>添加用户信息界面</title>
</head>
<body>
<form method="post" action="user!add.action" name="form">

</form>
<form action="user!add.action" method="post">
         <%--使用隐藏域存储生成的token--%>
         <%--
             <input type="hidden" name="token" value="<%=session.getAttribute("token") %>">
         --%>
         <%--使用EL表达式取出存储在session中的token--%>

        <table>
            <tr>
                <td>用户名:<input type="text" name="userName" id="userName"></td>
            </tr>
            <tr>
                <td>年龄:<input type="text" name="userAge" id="userAge"></td>
            </tr>
            <tr>
                <td>地址:<input type="text" name="userAddress" id="userAddress"></td>
            </tr>

        <tr>
        <td>**<input type="hidden" name="org.sunxin.token" value="<%=token%>"/>**
        <!-- 作为hidden提交,request的token -->
        <input type="submit" value="提交" style="background-color: pink">
        <input type="reset" value="重置" style="background-color: red"></td>
            </tr>
        </table>
    </form>
</body>
</html>

②struts.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 2.3//EN" "http://struts.apache.org/dtds/struts-2.3.dtd">
<struts>
    <constant name="struts.convention.default.parent.package"
        value="cmcc-default" />
    <constant name="struts.convention.package.locators" value="web" />
    <constant name="struts.convention.package.locators.basePackage"
        value="com.cmcc.db.web" />
    <constant name="struts.convention.result.path" value="/WEB-INF/jsp/"/>
    <constant name="struts.i18n.encoding" value="utf-8" />
    <constant name="struts.enable.DynamicMethodInvocation" value="true" />
    <package name="cmcc-default" extends="convention-default">
        <interceptors>
            <interceptor name="loginInter" class="com.cmcc.db.base.LoginInterceptor" />
            <interceptor name="token" class="com.cmcc.db.base.TokenInterceptor" /> 
            <interceptor-stack name="webStack">
                <interceptor-ref name="store">
                    <param name="operationMode">AUTOMATIC</param>
                </interceptor-ref>
                <interceptor-ref name="paramsPrepareParamsStack" />
                <!-- <interceptor-ref name="loginInter" /> -->
                 <interceptor-ref name="token"/> 
                <interceptor-ref name="defaultStack"></interceptor-ref>
            </interceptor-stack>
        </interceptors>

        <default-interceptor-ref name="webStack" />

        <global-exception-mappings>
            <exception-mapping exception="java.lang.Exception"
                result="error" />
        </global-exception-mappings>
    </package>

    <!-- 使用Convention插件,实现约定大于配置的零配置文件风格. 特殊的Result路径在Action类中使用@Result设定. -->
</struts>
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值