struts令牌机制

来源:互联网 发布:淘宝医药商城 编辑:程序博客网 时间:2024/06/04 19:14

struts令牌机制

在提交表单后,刷新或者后退重新点击保存,如果没有处理的话,可能会造成表单的重复提交,struts的令牌(token)机制可以方便的避免这种现象。


主要方法

  • 在表单提交页面增加一个隐藏域
    <input type="hidden" name="TOKEN" value="14c425db7657e2bd0b415b0661df5ef7"/>
  • 同时在session域中放置一份session.setAttribute("TOKEN", "14c425db7657e2bd0b415b0661df5ef7");
  • 提交表单时,获取session中TOKEN的值,同时获取隐藏域中的值request.getParameter("TOKEN");
    如果两者相同,则调用dao层对数据进行保存,并从session中删除TOKEN的值; 否则表单重复提交

struts令牌(Token)机制

  • 在跳转到表单提交页面之前,使用this.saveToken(request)方法,会自动在表单提交页面添加一个隐藏域,如下
    这里写图片描述
    同时该token的值也会在session中保存一份.
public ActionForward add(ActionMapping mapping, ActionForm form, HttpServletRequest request,            HttpServletResponse response) throws Exception {        System.out.println("执行EmployeeAction中add方法");        this.saveToken(request);        return mapping.findForward("add");}
  • 保存表单时, 使用this.TokenValid(request)来判断token是否有效,如果true, 则dao层对数据处理,并且调用this.resetToken(request) 从session中删除token, 如果返回false, 则说明表单重复提交,不再使用dao层对数据进行处理。
public ActionForward save(ActionMapping mapping, ActionForm form, HttpServletRequest request,            HttpServletResponse response) throws Exception {        // TODO Auto-generated method stub        System.out.println("> 执行EmployeeAction中save方法....");        if(!this.isTokenValid(request)){            System.out.println("表单重复提交");            //这里, 表单重复提交时,仍会跳转到保存成功页面,但是不会对数据进行处理            return mapping.findForward("success");        }        this.resetToken(request);        //dao层处理省略        return mapping.findForward("success");    }

源码查看

//org.apache.struts.action.Actionpublic class Action {    //这里只是复制了一些会用到的方法,详细的看源代码    private static TokenProcessor token = TokenProcessor.getInstance();    //给token赋值,就是那个很长的字符串    protected String generateToken(HttpServletRequest request) {        return token.generateToken(request);    }    //判断是否有效    protected boolean isTokenValid(HttpServletRequest request, boolean reset) {        return token.isTokenValid(request, reset);    }    //重置    protected void resetToken(HttpServletRequest request) {        token.resetToken(request);    }    //保存     protected void saveToken(HttpServletRequest request) {        token.saveToken(request);    }//org.apache.struts.util.TokenProcessorpublic class TokenProcessor {    //可以看出这个就是session和hidden中token的比较过程    public synchronized boolean isTokenValid(HttpServletRequest request,        boolean reset) {        // Retrieve the current session for this request        HttpSession session = request.getSession(false);        if (session == null) {            return false;        }        // Retrieve the transaction token from this session, and        // reset it if requested        String saved =            (String) session.getAttribute(Globals.TRANSACTION_TOKEN_KEY);        if (saved == null) {            return false;        }        if (reset) {            this.resetToken(request);        }         // Retrieve the transaction token included in this request        String token = request.getParameter(Globals.TOKEN_KEY);        if (token == null) {            return false;        }        return saved.equals(token);    }    //从session域中删除token    public synchronized void resetToken(HttpServletRequest request) {        HttpSession session = request.getSession(false);        if (session == null) {            return;        }        session.removeAttribute(Globals.TRANSACTION_TOKEN_KEY);    }   }public class Globals implements Serializable {//session保存时,域的key值    public static final String TRANSACTION_TOKEN_KEY = "org.apache.struts.action.TOKEN";//view层隐藏域hidden的name    public static final String TOKEN_KEY = "org.apache.struts.taglib.html.TOKEN"}

关于token的长字符串的生成过程,感觉这个算法,人家写的很有水平,这里复制过来,学习一下

public class TokenProcessor {        public synchronized String generateToken(String id) {        try {            long current = System.currentTimeMillis();            if (current == previous) {                current++;            }            previous = current;            byte[] now = new Long(current).toString().getBytes();            //MessageDigest是java自带的加密算法,经过update方法收集数据,之后使用digest完成哈希计算,生成固定长度的字节数组(本人测试的是16)            MessageDigest md = MessageDigest.getInstance("MD5");            md.update(id.getBytes());            md.update(now);            return toHex(md.digest());        } catch (NoSuchAlgorithmException e) {            return null;        }    }    //这里使用将字节数组转化为一个字符串(长度为原字节数组大小的2倍)    //byte大小为-128到127, 为8位二进制数据,这里将其高4位和低4位分别转化为一个字符char(范围是从0-f),比如字节92=0x5c,可以得到字符5和c    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();    }}//Character中的forDigit方法public static char forDigit(int digit, int radix) {        if ((digit >= radix) || (digit < 0)) {            return '\0';        }        if ((radix < Character.MIN_RADIX) || (radix > Character.MAX_RADIX)) {            return '\0';        }        if (digit < 10) {            return (char)('0' + digit);        }        return (char)('a' - 10 + digit);    }
0 0
原创粉丝点击