Base Admin一套Java实现的简单通用的后台管理系统

这套简单通用的后台管理系统,前端使用layui,数据库为mysql,主要功能有:权限管理、菜单管理、用户管理,系统设置、实时日志,实时监控,API加密,以及登录用户修改密码、配置个性菜单等。

这个只是一个比较简单通用的后台系统,如果加入工作流,就可以升级成基础平台,为简化业务开发,将部分通用系统功能整理成独立项目,具体业务功能通过iframe嵌入。

技术栈

前端:layui
java后端:SpringBoot + Thymeleaf + WebSocket + Spring Security + SpringData-Jpa + MySql

工程结构说明

java部分、html、js、css部分都是大目录下面按单表一个子目录存放

下面图片为工程结构图

1353055-20190917151158458-484473204.png

1353055-20190917151447667-796897556.png

1353055-20200610184928026-556521385.png

运行预览

效果先睹为快,具体介绍在下方,按功能点进行详情介绍

1353055-20200717112315040-830760246.png

功能演示

登录

支持七天免登陆,同时支持多种登录限制
1、允许/禁止账号多人在线
2、软删除
3、限制登录IP地址
4、账号过期
更多登录限制,还可以继续扩展

dev环境无需输入验证码(为了方便演示,密码输入框的类型改成text)

1353055-20190917115549987-1875069808.gif

允许/禁止账号多人在线

同时支持多种登录限制,更多登录限制,还可以继续扩展。

1353055-20190917120139928-961415583.gif

软删除

1353055-20190917120454587-588574990.gif

限制IP地址

1353055-20190917120759467-2071205563.gif

账号过期

1353055-20190917121025912-1939445659.gif

系统设置

1、系统名称
2、系统logo图片
3、系统公告
4、系统底部信息
5、是否开启API加密
6、是否开启OpenAPI限流
7、系统颜色
8、用户管理:初始、重置密码

简单的系统属性设置,想支持更多的配置可自行扩展(比如这里的:用户管理初始、重置密码)

1353055-20190917121133590-362300733.png

系统设置新增系统颜色,头部、左侧菜单的颜色可按心情切换(SQL文件已同步更新)

1353055-20200516175012845-1963105858.png

菜单管理

菜单管理是一棵layui的Tree,支持N层菜单结构、支持按权重排序

设置好菜单后记得要在用户管理给用户配置菜单,给用户配什么菜单用户就能看见什么菜单

1353055-20190917115210905-1262864719.gif

菜单

系统菜单、用户个性菜单新增排序字段,可自定义排序:

private Integer sortWeight;//同级排序权重:0-10

1353055-20201231144712854-67925321.png

1353055-20201231144736155-1443567111.png

1353055-20210518172549395-1167820552.png

权限管理

增删改查

1353055-20190917113851313-806820342.gif

动态权限加载

权限的加载并不是写死在代码,而是动态从数据库读取,每次调用save方法时更新权限集合

  1. 妲己是ROLE_USER权限,权限内容为空,无权访问/sys/下面的路径(http://localhost:8888/sys/sysUser/get/1)
  2. 使用sa超级管理员进行权限管理编辑,给ROLE_USER的权限内容添加 /sys/**,妲己立即有权限访问(http://localhost:8888/sys/sysUser/get/1)

1353055-20190917111943507-31961761.gif

用户管理

主要包括用户信息、登录限制的维护,菜单、权限的分配等

修改用户权限是下一次登录生效,修改用户菜单是刷新系统即可生效

1353055-20190917141611193-1374177557.gif

修改用户权限是下一次登录生效:

1353055-20190917142755275-852348900.gif

修改用户菜单是刷新系统即可生效:

1353055-20190917142904312-962958600.gif

当前在线用户,可实时查看当前在线用户,以及对当前在线用户进行强制下线操作:

1353055-20200702153541116-1976770548.gif

登录用户信息

基本信息

登录用户只能修改部分信息,例如名称、修改密码

1353055-20190917143002080-1344724137.png

修改密码

密码使用的是MD5加密并转换为16进制字符串存储,用户除了能主动修改密码外,还能叫管理员重置密码

1353055-20190917143128516-136222876.png

1353055-20190917143444653-886762886.png

个性菜单

用户可以自行配置自己的个性化快捷菜单

1353055-20190917143757872-1848622944.gif

实时日志

使用websocket,实时将日志输出到web页面,1秒刷新一次。

实时日志中,主要是用到正则去匹配读取出来的日志,达到对日志进行换行、着色等操作。

注意:这里的日志配置只配置了dev环境,prod环境尚未为空,发布生产环境前记得先配置,否则生成的日志文件将不会输入日志内容!

1353055-20200516175725335-255178444.png

1353055-20190917143841270-1600976561.png

实时监控

实时监控的是系统硬件环境、以及jvm运行时内存,使用websocket,实时将数据输出到web页面,1秒刷新一次。
使用ManagementFactory类获取信息,通过WebSocket每隔1秒推送前端页面刷新展示

Windows环境:

1353055-20200610185112519-659512719.gif

Linux环境:

1353055-20200724141108681-1459007961.png

API加密

AES + RSA 混合加密发送和响应。系统设置新增API加密开关,可一键关闭、开启API加密。

1353055-20190523171207110-431742561.png

说人话就是前、后端各自生成自己的RSA秘钥对(公钥、私钥),然后交换公钥(后端给前端的是正常的明文公钥,前端给后端的是用后端公钥加密后的密文公钥;PS:其实我觉得直接交换两个明文公钥就行了),后端生成AES的明文key,用明文key进行AES加密得到密文数据,用前端的公钥进行RSA加密得到密文key,API交互时并将密文数据与密文key进行传输,前端用自己的私钥进行RAS解密的到明文key,用明文key进行AES解密得到明文数据;前端给后端发送数据时同理,这样一来,传输的数据都是密文,且只有秘钥才能解密

思路总结

  1. 前端:重写$.ajax方法(或者封装一个ajax),发送数据前用AES加密数据(key随机生成),用后端的RSA公钥加密AES的key,将加密后的data数据、加密后的AES的key、前端RSA公钥发送到后端;触发回调后,先用前端RSA私钥解密AES的key,在用明文key去解密
  2. 后端:写两个自定义注解Encrypt、Decrypt,AOP拦截所有带自定义注解的post请求进行加密解密,有@Encrypt需要对返回值进行加密,有@Decrypt需要对参数进行解密,加密解密过程与前端的操作同理
  3. API加密中,由于登录校验是Spring Security做的,因此我们要在UsernamePasswordAuthenticationFilter获取账号、密码之前完成解密操作,正好我们的校验验证码操作就是在它之前,同时要做响应数据的加密操作,所以登录部分的API加密光使用AOP来还是不够的,需要在CaptchaFilterConfig进行解密操作,解密后new一个自定义RequestWrapper设置Parameter,并将这个新对象传到doFilter交由下一步处理,登录成功LoginSuccessHandlerConfig/失败LoginFailureHandlerConfig处理也要进行加解密处理

要注意的是:

  1. 我们在aop只设置了第一个参数,因此controller方法需要是实体接参且第一个参数就是,所有要求,要么有一个实体Vo参数,要么没有参数;
  2. 对于返回值,需要是统一的返回值,因为我们目前是按统一的返回值设置值的,例如本例中的Result,是我们约定好的统一返回值(后续升级可以用反射来设置值);
  3. 还有一个需要注意的地方,method方法必须是要public修饰的才能设置方法的形参值,private的设置不了;

请求参数加密

1353055-20190917143928654-548189551.png

响应数据加密

1353055-20190917143958364-1128612216.png

关闭API加密

1353055-20200604120034222-1818979193.png

接口限流

为对外开放的open api接口做了限流处理,当然这个功能也可以在系统设置中进行开启、关闭

1353055-20210715171715550-1384145747.png

1353055-20210625161917851-660941644.gif

代码生成器

逆向工程从数据库表直接生成代码,是日常开发中常用的敏捷开发手段,常见的例如:mybatis-plus的代码生成器等
为什么要自己写代码生成器呢?MP的生成器不香吗?香!
但是自己写的工具用起来最顺手,可以随意扩展,想怎么玩就怎么玩,只要自己有想法,玩出花来都没问题,当然了,能力有限,现在还只能实现简单版本,更多骚操作自己发挥!

思路:

  1. 建立jdbc连接,执行查询sql,获取表结构信息。
  2. 在指定的路径上创建文件。
  3. 按照我们的布局排版要求,根据表结构信息拼接文件的内容。
  4. 将字符输出到文件中。

以上即可完成一个文件的自动生成

file工具类:创建、读取文件
字符串工具类:驼峰标识、下划线互转,首字母大写,数据库字段类型转java类型等
jdbc连接:连接数据库

1353055-20210624162737232-1235043317.png

表注释、表结构信息实体类、执行sql获取表结构信息的方法
1353055-20210624163110069-29531697.png

大部分项目的代码都是可以复用的,特别是像我们这种封装了一套通用代码,单表直接继承实现CRUD、分页等功能,每个模块高度相似的代码,代码生成器就成了敏捷开发中重要的一步,直接根据数据库表生成我们想要的代码,省去了一步步创建文件、复制粘贴文件内容的繁琐步骤,实现快速开发!
自己写的代码生成器,扩展性更强,满足每个业务模块的代码要求不成问题

关键点讲解

  1. 定制url访问权限,动态权限读取,需要自定义配置认证数据源、认证管理器、拦截器,详情步骤请参考:https://www.jianshu.com/p/0a06496e75ea;
  2. API加密中,由于登录校验是Spring Security做的,因此我们要在UsernamePasswordAuthenticationFilter获取账号、密码之前完成解密操作,正好我们的校验验证码操作就是在它之前,同时要做响应数据的加密操作,所以登录部分的API加密光按照我们之前的博客来还是不够的,需要在CaptchaFilterConfig进行解密操作,解密后new一个自定义RequestWrapper设置Parameter,并将这个新对象传到doFilter交由下一步处理
  3. 还是API加密问题,我们是在程序启动的时候生成后端RSA秘钥对,正常来说我们在访问登录页面进行登录的时候前端获取一下就可以了,但在开发环境中,我们通常开启热部署功能,改完代码程序可能会自动重启,但登录用户信息仍然保持在本地线程,系统依旧处于登录状态没有跳转到登录页面,导致后端公钥已经改变,但前端依旧用的是旧的后端公钥,所有导致加解密失败;解决:在访问index首页时也获取一下后端公钥,这样在开发的时候idea热部署后刷新页面就可以了(已提交最新代码,解决热部署后刷新页面还是API加解密失败问题;现在热部署后刷新页面即可)
  4. 好多人都不知道,项目有工具类CodeDOM.java可以生成一套单表的完整增删改查后台代码,具体详情可以看之前的博客:SpringBoot系列——Spring-Data-JPA(究极进化版) 自动生成单表基础增、删、改、查接口

配置好数据库,指定代码生成父位置

1353055-20200723170423143-836921540.png

运行main函数即可一键生成一套单表增删改查后台代码

1353055-20200723170510542-1336891809.png

1353055-20200723170611234-1424613583.png

代码开源

注:数据库文件在resources/static/sql目录下面

代码已经开源、托管到我的GitHub、码云:

GitHub:https://github.com/huanzi-qch/base-admin
码云:https://gitee.com/huanzi-qch/base-admin

常见问题

0、maven下载jar包长时间无反应?

原因:网络原因连不上maven仓库或其他未知原因导致IDE间接性抽风,导致无法下载联网下载jar包
解决:网络原因自行解决,如果网络没问题就不要一直傻傻的等了,重启IDE,让它重新联网下载

1、IDE编译报错,识别不到实体类的set、get方法?

原因:项目使用lombok开发,lombok会在生成class字节码文件帮我们生成set、get等方法,java文件没有set、get等方法,IDE索引不到set、get方法所以编译报错
解决:IDE安装lombok插件即可能识别到对应set、get方法,重启生效

2、数据库文件在哪?

原因:没有好好看文档,建议先好好看下博客介绍,博客文末“代码开源”处已经早有说明
解决:base_admin.sql文件在resources/static/sql下面

3、如何启动程序?

原因:对springboot项目不熟,建议先去了解一下springboot,感兴趣的可以去看我的springBoot开源项目
解决:等待IDE识别成springboot项目后,在BaseAdminApplication.java中运行main函数启动程序

4、测试账号/密码是多少? PS:dev分支环境,默认关闭验证码校验

账号/密码
sa/123456

5、如何逆向工程生成后端代码?我封装有一个工具类AutoGenerator.java用于生成单表全套后端代码

首先建好数据表,在该类中配置好数据源以及项目所在路径,在main函数的tables数组指定要生成代码表,运行main函数即可生成全套后端增删改查、分页代码
一套通用common代码,每个单表去继承从而实现这套基础代码,使用AutoGenerator.java代码自动生成一套单表的基础增、删、改、查接口,大大提高开发效率,
详情见博客介绍:https://www.cnblogs.com/huanzi-qch/p/10281773.html

2021-06-24更新:我优化了AutoGenerator.java的代码,并升级了V2.0版本的代码生成器,支持使用模板文件生成代码:AutoGeneratorPlus.java

6、如何跳过登录,直接测试接口?

场景:有的同学发现在“无需权限访问”那里配置了测试接口,但还是跳转到了登录页面,例如:/sys/sysUser/getUserById
原因:这是因为跟“权限管理”那里配置的url接口冲突了(例如:/sys/**),权限管理那里的配置优先级更高,因此还是会跳去登录页面
解决:暂时删除所有跟测试接口冲突的“权限管理”配置的url接口(例如:/sys/**,/sys/sysUser/*等)

2021-01-25更新:还是有人搞不定无需登录接口,我在项目中新增了一个OpenApi模块,以供大家参考!启动项目后,访问OpenApi测试接口:http://localhost:8888/openApi/test,即可看到效果

7、ORM框架不想用JPA,如何快速转MyBatis-Plus?

JPA、MyBatis-Plus我都有进行封装,编码风格高度统一,都是单表继承基础通用的代码,有代码自动生成工具,本项目用的就是JPA的封装,快速切换可看之前的博客

MP:SpringBoot系列——MyBatis-Plus整合封装(https://www.cnblogs.com/huanzi-qch/p/13561164.html)
JPA:SpringBoot系列——Spring-Data-JPA(究极进化版) 自动生成单表基础增、删、改、查接口(https://www.cnblogs.com/huanzi-qch/p/10281773.html)

8、我想升级成前后端分离项目,麻烦吗?应当如何下手?

难度肯定还是有的,如何你对这个项目比较熟悉,相信你已早有思路,如果没有可以参考下面的博客文章

springboot+spring security +oauth2.0 demo搭建(password模式)(认证授权端与资源服务端分离的形式)(https://www.cnblogs.com/hetutu-5238/p/10022963.html)
GitHub地址(https://github.com/hetutu5238/zmc_security_oauth2)

另外,我封装开源了一套极简的前后端分离项目脚手架:Fast Scaffold,包含一个portal前端、一个admin后端,可用于快速的搭建前后端分离项目进行二次开发
详情请看博客:https://www.cnblogs.com/huanzi-qch/p/13933461.html
GitHub地址:https://github.com/huanzi-qch/fast-scaffold

9、运行jar包,启动失败?

十有八九是打的jar包有问题,参考博客重新打包,SpringBoot系列——jar包与war包的部署:https://www.cnblogs.com/huanzi-qch/p/9948060.html

发现还是有很多同学打的包不对,我已经修改了pom.xml的打包配置,直接指定了打包生成路径:package,
直接运行maven打包命令:package,就会在pom.xml的同级目录下生成package文件夹,打的jar包就在文件夹里面
另外说一下:
../package pom.xml文件的上一层目录下面的package
/package C盘下面的package文件夹
package pom.xml文件的同级目录下面的package