序: 刚来第一天环境刚搞好,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; } 这是自建加密的后台解密,常规加密的解密就不说了。 到此私密数据的安全传输就提高一个档次了,加个混淆安全性就更上一层楼了。