你好,这里是MarkerHub,今天,咱们来聊下bootshiro项目,先来看下简介:前端
基于springboot2+ shiro+jwt的真正rest api资源无状态认证权限管理框架,开发人员无需关注权限问题,后端开发完api,前端页面配置便可。mysql
前端: usthe、angular5git
后端: springboot、shiro、jwt、druid、swagger二、mybatis、mybatis-generator、pagehelper、redisredis
用户管理、 资源管理、 菜单管理、 API管理、 角色管理、 ...算法
这个项目,咱们能够主要学习一下怎么给表单的密码动态加密的,因此,咱们先来研究一下注册和登陆功能。spring
在项目的根目录下,有个postman_test_example.json,这是一个postman的导出文件,咱们把这个文件从新导入到postman中,而后进行联调。sql
分别对应着登陆,调用认证,注册3个接口。数据库
咱们先来看下注册功能的测试。json
由于是个post请求,参数是json数据,因此放在body中,其中password和userKey是个参数来的,那么这两个参数哪里来的呢?咱们看到Pre-request Script脚本中。segmentfault
这个脚本的大概意思是访问http://localhost:8080/account/register?tokenKey=get 连接,获取key和userKey,而后key通过AES算法加密以后获得了参数password,因此咱们刚才说注册接口中的body的两个参数就是这里注入进去的。
passwork明显通过了一层加密,这样传输的过程当中,即便表单的数据被别人截取到了,也不能得到密码,只有通过后端的AES解密以后,才能获取到密码。
那么有两个问题在这里
咱们先来看第一个问题:
咱们在过滤器中找到了PasswordFilter,是一个基于用户名密码的过滤器,继承AccessControlFilter,咱们看下代码:
@Override protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception { // 判断若为获取登陆注册加密动态秘钥请求 if (isPasswordTokenGet(request)) { //动态生成秘钥,redis存储秘钥供以后秘钥验证使用,设置有效期5秒用完即丢弃 String tokenKey = CommonUtil.getRandomString(16); String userKey = CommonUtil.getRandomString(6); try { redisTemplate.opsForValue().set("TOKEN_KEY_"+ IpUtil.getIpFromRequest(WebUtils.toHttp(request)).toUpperCase()+userKey.toUpperCase(),tokenKey,5, TimeUnit.SECONDS); // 动态秘钥response返回给前端 Message message = new Message(); message.ok(1000,"issued tokenKey success") .addData("tokenKey",tokenKey).addData("userKey", userKey.toUpperCase()); RequestResponseUtil.responseWrite(JSON.toJSONString(message),response); }catch (Exception e) { LOGGER.warn("签发动态秘钥失败"+e.getMessage(),e); Message message = new Message(); message.ok(1000,"issued tokenKey fail"); RequestResponseUtil.responseWrite(JSON.toJSONString(message),response); } return false; } // 判断是不是登陆请求 if(isPasswordLoginPost(request)){ ... } ... }
而咱们看下isPasswordTokenGet(request)方法就知道,其实就知足咱们的条件:
private boolean isPasswordTokenGet(ServletRequest request) { String tokenKey = RequestResponseUtil.getParameter(request,"tokenKey"); return (request instanceof HttpServletRequest) && "GET".equals(((HttpServletRequest) request).getMethod().toUpperCase()) && "get".equals(tokenKey); } 因此当咱们发起[http://localhost:8080/account/register?tokenKey=get](http://localhost:8080/account/register?tokenKey=get)请求的时候,就会进入到这个过滤器的这个条件中,就获取到了key和userKey,是随机生成的: String tokenKey = CommonUtil.getRandomString(16); String userKey = CommonUtil.getRandomString(6); redisTemplate.opsForValue().set("TOKEN_KEY_"+ IpUtil.getIpFromRequest(WebUtils.toHttp(request)).toUpperCase()+userKey.toUpperCase(),tokenKey,5, TimeUnit.SECONDS);
存到了redis中,有效期为5秒。因此这里动态生成了密钥,并redis存储秘钥供以后秘钥验证使用,设置有效期5秒用完即丢弃。 好了,咱们已经弄清楚了第一个问题,那么来看看第二个问题。
咱们找到com.usthe.bootshiro.controller.AccountController#accountRegister方法,其中最关键的代码以下:
// 从Redis取出密码传输加密解密秘钥 String tokenKey = redisTemplate.opsForValue().get("TOKEN_KEY_" + IpUtil.getIpFromRequest(WebUtils.toHttp(request)).toUpperCase()+userKey); String realPassword = AesUtil.aesDecode(password, tokenKey); String salt = CommonUtil.getRandomString(6); // 存储到数据库的密码为 MD5(原密码+盐值) authUser.setPassword(Md5Util.md5(realPassword + salt)); authUser.setSalt(salt); authUser.setCreateTime(new Date());
能够看出,tokenKey就是加密解密的重点key,因此AesUtil.aesDecode解密以后获得正在的密码,而后加盐保存到数据库中便可。 总结一下上面咱们的请求过程:
在注册以前,咱们先经过过滤器获取到了动态密钥,而后前端提交form注册表单以后先经过js给password进行AES加密,而后发送内容到达后台,后台在redis中获取动态密钥,而后进行解密获取到真实的密码,再进行注册。
完美!
同注册功能。
关于这个项目其余的内容大部分都是与shiro相关的,这里我就再也不多作分析啦。刚兴趣的同窗能够再去细看哈。
好啦,今天先到这里哈,若是你喜欢个人文章,能够来markerhub.com看更多开源项目解析。 > markerhub.com,梳理Java知识,解析开源项目,欢迎关注公众号【MarkerHub】