25 KiB
感谢
[!important]
前端项目整体都由此模板开发
项目由小铭开源权限模板Pure-admin
Pure-admin文档:https://pure-admin.github.io/pure-admin-doc
项目预览
线上地址
正式线上预览地址:http://bunny-web.site/#/welcome
如果发现网站打开白屏很有可能是因为更新造成的,因为需要维护删除不需要的功能可能会导致这个情况,但基本不会遇到。
谷歌方式如下:
Edge方式如下:
打包视频
https://www.bilibili.com/video/BV1AYm8YSEKY/
Github地址
Gitee
地址
环境搭建
安装docker内容
centos或rocky
# 更新yum 和 dnf
yum update -y
dnf update -y
# 安装必要依赖
yum install -y yum-utils device-mapper-persistent-data lvm2
# 设置镜像源
sudo yum-config-manager --add-repo http://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo
yum list docker-ce --showduplicates | sort -r
# 安装docker
yum -y install docker-ce.x86_64
# 开机启动docker
systemctl enable docker
systemctl start docker
Ubuntu环境搭建
sudo apt-get remove docker docker-engine docker.io containerd runc
sudo apt update
sudo apt upgrade
sudo apt-get install ca-certificates curl gnupg lsb-release
# 添加Docker官方GPG密钥
sudo curl -fsSL http://mirrors.aliyun.com/docker-ce/linux/ubuntu/gpg | sudo apt-key add -
sudo add-apt-repository "deb [arch=amd64] http://mirrors.aliyun.com/docker-ce/linux/ubuntu $(lsb_release -cs) stable"
# 安装docker
sudo apt-get install docker-ce docker-ce-cli containerd.io
# 默认情况下,只有root用户和docker组的用户才能运行Docker命令。我们可以将当前用户添加到docker组,以避免每次使用Docker时都需要使用sudo,设置完成后退出当前用户之后再进入既可
sudo usermod -aG docker $USER
# 运行docker
sudo systemctl start docker
# 安装工具
sudo apt-get -y install apt-transport-https ca-certificates curl software-properties-common
# 重启docker
sudo service docker restart
避免每次操作docker需要输入sudo
# 创建分组一般
sudo groupadd docker
# 将当前用户添加到分组
sudo usermod -aG docker $USER
# 重启终端生效
exit
[!warning]
安装docker会可能会遇到镜像无法拉去问题试下面的几个镜像地址,根据你自己的需要进行配置不是所有人都需要;同时也不能保证地址真的有效!!!
复制下面的内容到
daemon.json
# 创建目录 sudo mkdir -p /etc/docker # 写入配置文件 sudo tee /etc/docker/daemon.json <<-'EOF' { "registry-mirrors": [ "https://docker-0.unsee.tech", "https://docker-cf.registry.cyou", "https://docker.1panel.live" ] } EOF # 重启docker服务 sudo systemctl daemon-reload && sudo systemctl restart docker
重启docker
sudo systemctl restart docker.socket
Docker-Compose 搭建环境
安装Docker-Compose
使用docker-compose有的时候会因为版本不同,但是配置文件主要内容就是这些。需要注意版本问题
Centos、Rocky
sudo yum install docker-ce docker-ce-cli containerd.io
sudo yum install curl
curl -L https://get.daocloud.io/docker/compose/releases/download/1.25.4/docker-compose-`uname -s`-`uname -m` > /usr/local/bin/docker-compose
sudo chmod +x /usr/local/bin/docker-compose
sudo docker-compose --version
Ubuntu
sudo apt install docker-compose
docker-compose文件位置
相关命令
只拉取镜像
docker-compose pull 命令会拉取 docker-compose.yml 文件中定义的所有服务的镜像,但不会创建或启动容器。
docker-compose pull
拉取镜像而不做其他操作
只拉取镜像而不做其他操作,可以结合 docker-compose 的其他命令来实现:
docker-compose up --no-start
拉取镜像并创建容器
docker-compose up -d
数据库文件
在后端文件的根目录中
后端日志文件
在后端日志文件中,使用了logback.xml
进行格式化。然而,使用logback.xml
后,配置文件中指定的日志输出文件位置可能会失效。如果项目是通过Docker部署的,想在宿主机查看日志文件需要进行文件映射。
使用SpringBoot
在配置文件中,指定名称和目录路径即可,之后使用docker映射就可以在宿主机看到日志了
如果想要用自带需要删除logback.xml
文件
logging:
level:
cn.bunny.service.mapper: warn
cn.bunny.service.controller: warn
cn.bunny.service.service: warn
root: warn
pattern:
dateformat: HH:mm:ss:SSS
file:
path: "logs/${spring.application.name}"
name: "logs/${spring.application.name}"
使用logback.xml
指定目录的位置<file>D:/logs/${datetime}/auth-server.log</file>
根据你需要的指定
<!-- 格式化 年-月-日 输出 -->
<timestamp key="datetime" datePattern="yyyy-MM-dd"/>
<appender name="STOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>
%cyan([%thread]) %yellow(%-5level) %green(%logger{100}).%boldRed(%method)-%boldMagenta(%line)- %blue(%msg%n)
</pattern>
</encoder>
</appender>
<!-- 文件日志 -->
<appender name="FILE" class="ch.qos.logback.core.FileAppender">
<file>/www/root/server/logs/${datetime}/auth-server.log</file>
<append>true</append>
<encoder>
<pattern>%date{yyyy-MM-dd HH:mm:ss} %-5level %logger{100} %method %line %msg%n</pattern>
<charset>UTF-8</charset>
</encoder>
</appender>
如果按照上面搭建,日志文件会在
/www/root/server/logs
下,即使使Windows系统也会存在,如果docker使用的是文件映射,那么日志文件会在容器相对应的位置
如果开发环境或者其他环境也需要日志,可以根据当前环境进行选择<springProfile name="prod">
<!-- 生产环境 -->
<springProfile name="prod">
<!-- 日志记录器:业务程序INFO级别 -->
<logger name="cn.bunny" level="INFO"/>
<!-- 根日志记录器:INFO级别 -->
<root level="INFO">
<appender-ref ref="CONSOLE"/>
<appender-ref ref="FILE"/>
</root>
</springProfile>
项目特点
按钮权限显示
如果当前用户在这个页面中只有【添加】和【删除】那么页面按钮中只会显示出【添加按钮】和【删除按钮】
去除前后空格
后端配置了自动去除前端传递的空字符串,如果传递的内容前后有空格会自动去除前后的空格
项目接口和页面
接口地址有两个:
- knife4j
- swagger
接口地址://localhost:7070/doc.html#/home
swagger接口地址:http://localhost:7070/swagger-ui/index.html
前端接口地址:http://localhost:7000/#/welcome
登录功能
[!warning]
可以选择邮箱登录或者是密码直接登录,两者不互用。
账号登录
业务需求
- 用户输入用户名和密码进行登录
实现思路
- 用户输入账号和密码和数据库中账号密码进行比对,成功后进行页面跳转
- 如果账户禁用会显示账户已封禁
[!tip]
这里做了登录策略,可以方便以后的扩展。
后端实现文件位置
- 拦截请求为
/admin/login
的请求之后进行登录验证的判断
邮箱登录
业务需求
- 用户输入邮箱账号、密码、邮箱验证码之后进行登录
实现思路
- 需要验证用户输入的邮箱格式是否正确。
- 在未输入验证码的情况下输入密码会提示用户,同时后端也会进行验证。如果输入了邮箱验证码但是Redis中不存在或已过期,会提示:邮箱验证码不存在或已过期。
- 之后对邮箱账号和密码进行判断包括邮箱验证码进行判断
首页功能
功能菜单,首页图表展示部分功能已经由这个模板作者设计好,其中需要注意的是,如果要查看历史消息或者是进入消息页面可以双击
消息功能
业务需求
-
消息页面的展示,包含删除、批量删除、选中已读和当前页面所有消息都标为已读
-
当用户对左侧菜单点击时可以过滤出消息内容,展示不同的消息类型
-
可以点击已读和全部进行筛选消息
-
可以根据标题进行搜搜
-
包含分页
实现思路
-
显示当前消息类型,用户点击时带参数请求,只带当前消息类型,不默认携带已读状态查询,然后从数据库筛选并返回结果。
-
点击"已读"选项时,若选择"全部"(之前是设置为undefined,这样就不会携带参数了,但是底下会有警告),现在改为空字符串,后端只需过滤掉空字符串即可。
-
删除选定数据,若用户选择列表并筛选出所有ID,将数据传递给后端(用户删除为逻辑删除)。
-
将所有数据标记为已读!当前页面前端使用map提取所有ID,整合成ID列表传递给后端,表示页面上所有数据已读。
-
输入标题后,随输入变化进行搜索。
消息提醒在页面刷新时会出现这样
用户管理
需求分析
- 用户操作需要包含CURD的操作
- 为了方便在用户中需要给出快速禁用当前用户按钮
- 需要显示用户头像、性别、最后登录的IP地址和归属地
- 在左侧中需要包含部分查询
- 可以根据点击的部门查询当前部门下的用户
- 根据用户可以强制下线、管理员可以修改用户密码、为用户分配角色
重置密码
重置密码需要判断当前用户密码是否是符合指定的密码格式,并且会根据当前输入密码计算得分如果当前密码复杂则得分越高那么密码强度越强
重置密码组件在前端的公共组件文件中
分配角色
-
给用户分配了admin角色后,其他路由绑定和权限设置就不再需要了,因为后端会根据admin角色在前端用户信息中设置通用权限码,如
*
、*::*
、*::*::*
,表示前端用户可以访问所有权限并查看所有内容。 -
管理员有权对用户进行角色分配,这涉及到许多操作,包括菜单显示和接口访问权限。角色与权限相关联,角色也与菜单相关联。
-
当用户访问菜单时,会根据其角色看到其所属的菜单内容。随后,角色与权限接口相关联,根据用户的权限来决定是否显示操作按钮。后端会根据用户的权限验证其是否可以访问当前接口。
-
用户登录或刷新页面时会重新获取用户信息,用户信息中包含角色和权限信息。利用角色和权限信息与前端传递的路径进行比对判断,如果用户包含菜单角色,则可以访问。如果用户包含前端路由中的权限,则表示该权限可以访问。后端也会进行权限判断,以防止通过接口文档等方式访问。
-
分配好角色后,菜单会根据当前路由角色匹配用户角色,从而根据用户角色显示相应的菜单内容。
角色管理
角色管理包含CURD和权限分配操作
业务需求
用户对角色进行CURD操作,点击权限设置时让用户分配权限
实现思路
-
在设计的表中,如果存在相同的角色码,系统会提示用户当前角色已经存在。
-
后端会根据角色的ID分配权限的ID列表。
- 后端在角色权限表中会根据角色的ID分配权限内容。在角色权限表中,会先删除当前角色所有的权限内容,然后再进行权限内容的重新分配。
public void assignPowersToRole(AssignPowersToRoleDto dto) {
List<Long> powerIds = dto.getPowerIds();
Long roleId = dto.getRoleId();
// 删除这个角色下所有权限
baseMapper.deleteBatchRoleIdsWithPhysics(List.of(roleId));
// 保存分配数据
List<RolePower> rolePowerList = powerIds.stream().map(powerId -> {
RolePower rolePower = new RolePower();
rolePower.setRoleId(roleId);
rolePower.setPowerId(powerId);
return rolePower;
}).toList();
saveBatch(rolePowerList);
// 找到所有和当前更新角色相同的用户
List<Long> roleIds = userRoleMapper.selectList(Wrappers.<UserRole>lambdaQuery().eq(UserRole::getRoleId, roleId))
.stream().map(UserRole::getUserId).toList();
// 根据Id查找所有用户
List<AdminUser> adminUsers = userMapper.selectList(Wrappers.<AdminUser>lambdaQuery().in(!roleIds.isEmpty(), AdminUser::getId, roleIds));
// 用户为空时不更新Redis的key
if (adminUsers.isEmpty()) return;
// 更新Redis中用户信息
List<Long> userIds = adminUsers.stream().map(AdminUser::getId).toList();
roleFactory.updateUserRedisInfo(userIds);
}
权限管理
在权限配置中,添加/修改权限时的请求地址为后端接口的请求地址,请求地址使用了【正则表达式
】判断和【antpath
】方式填写
业务需求
- 对权限表进行CURD操作
- 在表格中点击新增时,父级id为当前点击行的id
实现思路
点击当前行父级id为当前的行的id
权限判断实现方式
后端判断方式
判断权限是否可以访问,后端实现判断逻辑
前端判断方式
角色分配方式有下面几种想洗参考:https://pure-admin.github.io/vue-pure-admin/#/permission/button/router,文档页面
- 使用标签方式
- 使用函数方式
- 使用指令方式
在前端utils文件夹下有auth.ts
文件里面包含了权限码信息,如果当前菜单属性中包含这个权限码表示可以访问这个权限
菜单管理
菜单路由
在做菜单返回时必须要了解角色和权限表
需求分析
-
从数据库中返回出所有的菜单,其中需要整合成前端所要的形式,需要包含
roles
和auths
,及其其它参数。 -
用户需要根据自己的角色访问不同的菜单。
-
如果当前用户不可以访问某些按钮需要隐藏。
-
用户通过其它手段访问如:swagger、knife4j、apifox、postman这种工具访问需要做权限验证,如果当前用户不满足访问这些接口后端需要拒绝。
-
在数据库中为部分字段建立了唯一索引
实现思路
- 角色和权限哪些可以访问的页面交给前端,在模板中已经设计好,如果用户访问了自己看不到的菜单会出现
403
页面;判断方式是根据后端返回的菜单中如果包含当前用户的角色就表示可以访问当前的菜单,如果用户信息中没有这个角色则表示不可以访问这个页面。 - 页面是否可以访问只是在操作上,如果用户通过接口访问是阻止不了的,所以这时后端需要在后端中进行判断,当前的访问路径是否是被允许的,也就是这个用户是否有这个权限,权限表设计中包含了请求路径
- 后端需要判断用户请求这个接口是否有权访问
整合成前端格式返回需要递归,后端根据当前用户访问的菜单需要进行递归菜单数据之后返回前端,并将这些菜单绑定的角色放置在
roles
中,之后根据角色查询全新啊相关内容,要将权限内容放置在auths
中.如果包含子菜单需要防止在
children
数组中,后端实现时如果没有子菜单默认是空数组而不是null
大致如下:
{ "menuType": 0, "title": "admin_user", "path": "/system/admin-user", "component": "/system/adminUser/index", "meta": { "icon": "ic:round-manage-accounts", "title": "admin_user", "rank": 2, "roles": [ "admin", "all_page", "system", "test" ], "auths": [ "message::updateMessage", "menuIcon::getMenuIconList", "admin::messageReceived", "config::getWebConfig", "admin::config", "i18n::getI18n", .... ], "frameSrc": "" }, "children": [], "id": "1841803086252548097", "parentId": "1", "name": "admin_user", "rank": 2 }
部门管理
业务需求
- 包含CURD
- 在用户管理中可以选择对应的部门
实现思路
- CURD接口文件如下
- 管理员为用户分配部门
菜单图标
业务需求
- 用户在菜单中可以选择存储在数据库中的图标内容
- 包含CURD内容
实现思路
后端需要返回接口格式实体类如下
public class MenuIconVo extends BaseUserVo {
@Schema(name = "iconCode", title = "icon类名")
private String iconCode;
@Schema(name = "iconName", title = "icon 名称")
private String iconName;
}
前端封装好的组件
邮箱相关
业务需求
- 邮件用户配置CURD
- 邮件模板CURD
- 邮件用户中只能有一个是默认的,如果当前修改其它项需要将其它已经启用改为不启用
- 邮件模板需要绑定邮件用户
实现思路
邮件模板中,添加或者修改时前端需要返回所有的邮件模板用户,添加或者修改时将用户ID存储在邮件模板的数据字段中
web配置
主题颜色
后端做了Hash值校验,如果是RGB会造成颜色显示有些问题,比如使用纯色的按钮,想下面的这种的,鼠标hover事件会变透明因为无法计算Hash值,所以在修改颜色值时一定要保证是Hash值
修改完成的示例
系统监控
服务监控
从SpringBoot的Actuator中获取信息,页面采用响应式
系统缓存
当前内容被SpringBoot缓存会显示在这
定时任务
采用Quarter持久化存储。
示例显示
多语言管理
日志管理
存储在MySQL中,如果想做优化可以放在MongoDB中
消息管理
之后点击时会看到消息封面、标题、简介、消息等级、消息等级内容
消息类型
包含CURD,用户编辑消息发送时可以在选择
同时在用户消息栏中也会显示对应内容,前端判断逻辑如下
消息编辑
提供md编辑器和富文本编辑器
消息接受用户,如果不填写表示全部的用户,填写后会根据填写的内容存储在用户接受表中
消息等级是显示消息样式颜色,文字内容为消息简介内容
消息接收管理
根据所选的接受用户会出现在下面的用户接受表中,可以对当前用户是否已读进行修改
消息发送管理
之前编辑过的消息都会在这
环境部署
使用Docker进行部署,后端接口地址以/api
开头,前端默认请求前缀为/api
,因此在请求时需要进行替换。详细内容请参考以下【项目部署】说明。
前端部署
运行pnpm build
dockerfile
中暴露端口要和生产环境的端口号保持一致
使用http协议
如果不使用https,需要将下面内容注释
对外暴露端口改为80
或者你自己喜欢的端口
NGINX配置
将NGINX配置修改为以下内容,如果你的暴露的端口和我一样是80
运行docker文件
命令预览
docker build -f Dockerfile -t bunny_auth_web:1.0.0 . && docker run -p 80:443 -p 443:443 --name bunny_auth_web --restart always bunny_auth_web:1.0.0
后端部署
注意事项
如果需要打包需要手动创建生产环境文件:application-prod.yml
环境设置
开发环境环境:对外暴露的端口是7070
生产环境:对外暴露的端口是8000
需要先打包,打包完成后会在目录下生成对应的target
相关文件
# 开发环境
mvn clean package -Pprod -DskipTests
# 测试环境
mvn clean package -Ptest -DskipTests
⭐ 如果不需要内置文件操作什么的这一步可以忽略
这个文件夹是必须的