栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java > SpringBoot

基于Jwt资源无状态认证权限管理系统bootshiro

SpringBoot 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

基于Jwt资源无状态认证权限管理系统bootshiro

基本信息

你好,这里是MarkerHub,今天,我们来聊下bootshiro项目,先来看下简介:

基于springboot2+ shiro+jwt的真正rest api资源无状态认证权限管理框架,开发人员无需关注权限问题,后端开发完api,前端页面配置即可。

  • git链接:https://gitee.com/tomsun28/bootshiro
  • 项目作者:tomsun28
技术栈

前端: usthe、angular5

后端: springboot、shiro、jwt、druid、swagger2、mybatis、mybatis-generator、pagehelper、redis

功能大纲

用户管理、
资源管理、
菜单管理、
API管理、
角色管理、

学习目的
  • restful接口设计
  • 数据传输动态秘钥加密
  • jwt过期自动刷新
  • 预防Xss攻击
安装教程
  • fork 项目到自己的仓库(欢迎star.)
  • clone 项目到本地
  • 用idea导入
  • 更改开发环境mysql数据库和redis地址(前提安装数据库并导入usthe.sql创建数据库usthe)
  • 运行BootshiroApplication
  • bootshiro就可以提供api了 http://localhost:8080
  • 推荐使用postman进行api调试
官方文档
  • 前后端分离实践
  • api权限管理系统与前后端分离实践
  • 基于shiro的改造集成真正支持restful请求
  • 签发的用户认证token超时刷新策略
  • 传输密码动态加密解密
模块分析

这个项目,我们可以主要学习一下怎么给表单的密码动态加密的,所以,我们先来研究一下注册和登录功能。

动态密钥加密 注册功能

在项目的根目录下,有个postman_test_example.json,这是一个postman的导出文件,我们把这个文件重新导入到postman中,然后进行联调。

分别对应着登录,调用认证,注册3个接口。

我们先来看下注册功能的测试。

因为是个post请求,参数是json数据,所以放在body中,其中password和userKey是个参数来的,那么这两个参数哪里来的呢?我们看到Pre-request script脚本中。

这个脚本的大概意思是访问http://localhost:8080/account/register?tokenKey=get 链接,获取key和userKey,然后key经过AES算法加密之后得到了参数password,所以我们刚才说注册接口中的body的两个参数就是这里注入进去的。

passwork明显经过了一层加密,这样传输的过程中,即使表单的数据被别人截取到了,也不能获得密码,只有经过后端的AES解密之后,才能获取到密码。

那么有两个问题在这里

  • 获取key和userKey的方法在哪?
  • 后端如何解密的?

我们先来看第一个问题:

我们在过滤器中找到了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看更多开源项目解析。

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/234858.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号