序:
      刚来第一天环境刚搞好,svn刚装完,账号都还没有,员工手册还没有学习完,就被要求开始撸代码。还好同事之间沟通狠顺畅,直接可以找到人问东西。
     今天主要比较紧急的待修复项:
     1.验证码只能使用一次的问题
     2.登录/修改密码过程中数据传输简单的decode不满足安全性要求
     首先说下这边的验证码是怎么生成的。一般我们已说到验证码只能用一次想到的肯定是利用redis存储生成的验证码,设置时效性。但是这里生成验证码用的是一个组件kaptcha,这个组件的核心思路就是生成了验证码放session里,没有设置有效时长这个说法。此外异步校验也是每次从session取比对,代码如下:
 

String  captchaCode = (String) request.getSession().getAttribute(com.google.code.kaptcha.Constants.KAPTCHA_SESSION_KEY);
         if(captchaCode.equalsIgnoreCase(verificationCode)){
             return "true";
         }


     如果比对一致就表示校验通过。这里权限是使用shiro,登陆也校验验证码,这时候是从token里取比对。重点来了,我处理这里保证验证码只能使用一次也是在这里做的逻辑,也就是在shiro的继承AuthorizingRealm里的doGetAuthenticationInfo方法里做文章,思路:就是在校验token里的验证码比对时从session删除验证码,这样再次传这个验证码就过不了了,具体逻辑:

String captcha = (String) SecurityUtils
                 .getSubject()
                 .getSession()
                 .getAttribute(com.google.code.kaptcha.Constants.KAPTCHA_SESSION_KEY);//删除session里的验证码        
 SecurityUtils.getSubject().getSession().removeAttribute(com.google.code.kaptcha.Constants.KAPTCHA_SESSION_KEY);
         if(StringUtils.isBlank(captcha)) {
             throw new IncorrectCaptchaException("验证码失效");
         }
 //比对token里与session里的验证码
         if (captcha != null && !captcha.equalsIgnoreCase(token.getCaptcha()) ){
             throw new IncorrectCaptchaException("验证码错误");
         }


另外做以上操作,注意需要配合页面的刷新做,比如页面提示验证失效或错误了需要重新获取ajax生成(保证新生成一个放session里备用)
     接下来该说第二个问题,那就是加密传输的问题。
     其实这里的思路受起来也狠简单,就是传统的加密配合自己写的加密使用。自己写的加密的思路:
     每个输入的字符生成一个的字符串,而这个字符串里某位是角标,真正的charAt(角标)位置才是输入内容,其他位则是随机生成,这个角标也是随机的,所以生成的串每次看起来不一样,这样抓到的包看到的就是每次发送的都不一样,让黑客难以分析出规律。前台自建加密js:

function randomsort(d, c) {
     return Math.random() > 0.5 ? -1 : 1
 }
 function encryption(e) {
     var g = e.split("");
     for (var f = 0; f < g.length; f++) {
         var h = Math.floor(Math.random() * 3) + 1;
         var c = (f + 1) + "0" + (h + 1);
         for (var d = 0; d < h; d++) {
             c += Math.floor(Math.random() * 9)
         }
         g[f] = c + g[f];
         var b = 9 - g[f].length;
         if (b > 2) {
             for (d = 0; d < b; d++) {
                 g[f] += Math.floor(Math.random() * 9)
             }
         }
     }
     g.sort(randomsort);
     var a = "";
     for (f = 0; f < g.length; f++) {
         a += g[f] + "$"
     }
     return a
 };


页面使用也超级简单:

var username = encode(encryption($("#username").val()));   
 $("#sendUsername").val(username);
 var password = encode(encryption($("#password").val()));
 $("#sendPassword").val(password);


其实就是获取用户输入的明文进行自建加密,然后再调用一次其他常规加密,这里的encode就是其他的常规加密。
这样因为自建加密生成的串随机,那么常规加密生成的就每次不一样,尽管真正的每次用户名密码都一样。
但是说实话,别人要是花店心思先获取这个自建加密js(浏览器打开debug就可以看到,不过这个js要想让用户看到但是不知道什么意思除了变量定义用abcd外还可以加混淆,这样真的绝大部分黑客就望而止步了),说远了。
再来说说后台的解密,其实根据这个js已经可以知道java的解密怎么写了,这里贴出的我故意留个bug吧,可以用,但是会有问题,不能告诉大家见谅,如果真需要知道,可以单独找我。

public static String decryp(String str){
         String[] strs = str.split("[$]");
         Map<Integer, String> map = new TreeMap<Integer, String>(
                 new Comparator<Integer>() {
                     public int compare(Integer obj1, Integer obj2) {
                         return obj2.compareTo(obj1);
                     }
          });
         for(String temp : strs){
             Integer number = Integer.parseInt(temp.substring(0, temp.indexOf("0")));
             temp = temp.substring(temp.indexOf("0") + 1, temp.length());
             Integer index = Integer.parseInt(temp.substring(0, 1));
             String[] temps = temp.split("");
             String value = temps[index];
             map.put(number, value);
         }
         Set<Integer> keySet = map.keySet();
         Iterator<Integer> iter = keySet.iterator();
         String result = "";
         while (iter.hasNext()) {
             Integer key = iter.next();
             result = map.get(key) + result;
         }
         return result;
     }


这是自建加密的后台解密,常规加密的解密就不说了。
到此私密数据的安全传输就提高一个档次了,加个混淆安全性就更上一层楼了。