基于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调试
官方文档
模块分析
这个项目,我们可以主要学习一下怎么给表单的密码动态加密的,所以,我们先来研究一下注册和登录功能。
动态密钥加密
注册功能
在项目的根目录下,有个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看更多开源项目解析。 > markerhub.com,梳理Java知识,解析开源项目,欢迎关注公众号【MarkerHub】
低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。
持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。
转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。
- 上一篇
Java工程师该如何编写高效代码?| 12月18号云栖号夜读
点击订阅云栖夜读日刊,专业的技术干货,不容错过! 阿里专家原创好文 1.Java工程师该如何编写高效代码? 世界上只有两种物质:高效率和低效率;世界上只有两种人:高效率的人和低效率的人。——萧伯纳。同理,世界上只有两种代码:高效代码和低效代码;世界上只有两种人:编写高效代码的人和编写低效代码的人。如何编写高效代码,是每个研发团队都面临的一个重大问题。本文作者根据实际经验,查阅了大量资料,总结了"Java高效代码50例",让每一个Java程序员都能编写出"高效代码"。阅读更多》》 2.仅1年GitHub Star数翻倍,Flink 做了什么? Apache Flink 是公认的新一代开源大数据计算引擎,其流水线运行系统既可以执行批处理程序也可以执行流处理程序。目前,Flink 已成为 Apache 基金会和 GitHub 社区最为活跃的项目之一。在 Flink Forward Asia 2019 上,阿里巴巴资深技术专家,实时计算负责人王峰 (莫问)总结了 2019 年 Flink 在中国的发展和演进,阿里对 Flink 社区的贡献以及未来 Flink 的最新发展方向。阅读更多》》 3....
- 下一篇
D 语言(DLang)2.089.1 发布,细节改进提升可用性
D 语言 2.089.1 版本是一个修复版本,上个大版本 2.089.0 存在一些改进后的不兼容问题得到了修复,提升了稳定性。 DMD 编译器改进: Bugzilla 15069: [REG2.064] nonsense struct template instantiations still compile Bugzilla 19884: [regression] Error: alias op = op; cannot alias itself, use a qualified name to create an overload set Bugzilla 20348: [Reg 2.089.0-rc.1] Bad debug info for enum forward references Bugzilla 20349: [REG2.087] ICE with sqrt Bugzilla 20376: @disable this(ref return scope Foo rhs) enables broken binaries (out-of-bounds access) Bug...
相关文章
文章评论
共有0条评论来说两句吧...
文章二维码
点击排行
推荐阅读
最新文章
- CentOS7编译安装Gcc9.2.0,解决mysql等软件编译问题
- CentOS8安装Docker,最新的服务器搭配容器使用
- Docker使用Oracle官方镜像安装(12C,18C,19C)
- 设置Eclipse缩进为4个空格,增强代码规范
- Red5直播服务器,属于Java语言的直播服务器
- SpringBoot2整合Redis,开启缓存,提高访问速度
- SpringBoot2配置默认Tomcat设置,开启更多高级功能
- SpringBoot2更换Tomcat为Jetty,小型站点的福音
- CentOS7,8上快速安装Gitea,搭建Git服务器
- CentOS7安装Docker,走上虚拟化容器引擎之路