Guns开发文档

**此文档为PDF下复制的内容,所以很多图片无法显示,感兴趣的可以下载PDF格式资料
链接:百度网盘地址
提取码:y7xu
**


Guns 技术文档 v1.0

  1. 序言
    1.1 文档简介
    1.2 Guns教程
    1.3 获取帮助
  2. 使用手册
    2.1 下载项目
    2.2 导入项目
    2.2.1 eclipse导入
    2.2.2 IDEA导入
    2.3 运行项目
    2.4 打包部署
  3. 开发手册
    3.1 了解Guns
    3.1.1 模块结构
    3.1.2 包结构
    3.2 实战开发
    3.2.1 建表
    3.2.2 代码生成
    3.3.3 添加菜单与分配权限
    3.3.4 编写业务代码
    3.3 权限控制于校验
    3.3.1 用户,角色和资源
    3.3.2 如何对资源进行权限控制
    3.3.3 前端页面对权限资源的显示
    3.4 多数据源的使用
    3.5 如何分页
    3.5.1 简单查询的分页
    3.5.2 复杂查询的分页
    3.5.3 获取前端表格插件传值
    3.6 数据范围
    3.6.1 介绍
    3.6.2 如何使用
    3.6.3 原理
    3.7 guns-rest模块的使用
    3.7.1 关于jwt鉴权
    3.7.2 关于传输数据的签名
    3.7.3 guns-rest模块的运行流程
    3.7.4 运行原理
    3.8 工作流
    3.9 日志记录
    3.9.1 业务日志
    3.9.2 异常日志
    3.10 如何使用缓存
    3.10.1 用工具类操作
    3.10.2 用spring cache操作缓存
    3.11 使用枚举
    3.12 spring boot热部署
    3.12.1 重新加载html
    3.12.2 重新加载java类
  4. 扩展与高级配置
    4.1 修改项目名和包名
    4.1.1 修改项目名
    4.1.2 修改包名
    4.2 放过接口权限验证
    4.3 静态资源和模板位置的变更
    4.4 三个或更多数据源如何配置
    4.5 添加登录验证码
    4.6 spring profile
    4.7 多机器部署开启spring session
    4.8 使用Redis
    4.9 XSS过滤器
    4.9.1 介绍
    4.9.2 原理
    4.9.3 放过过滤
  5. 核心思想
    5.1 分包
    5.2 统一异常拦截
    5.2.1 介绍
    5.2.2 优点
    5.2.3 关于性能
    5.3 结果包装器
    5.3.1 如何使用
    5.3.2 ConstantFactory
    5.4 前端思想
    5.4.1 布局
    5.4.2 标签
  6. 常见问题答疑
    6.1 默认的系统登录账号和密码是多少
    6.2 权限异常
    6.3 为何分页是前端实现
    6.4 关于${ctxPath}
    6.5 放过某些url的权限验证
    6.6 主页的搜索功能
    6.7 运行sql报错
    6.8 关于打包
    6.9 查询结果的驼峰转化问题
    6.10 为何使用beetl
    6.11 为何有的业务没有service层
    6.12 为何既有dao,又有mapper
  7. 序言
    1.1 文档简介
    本文档基于最新的Guns版本,集 Guns使用手册 , Guns开发手册 , Guns核心思想 等于一体,并
    整理了 qq群 和 gitee 上用户经常反馈的问题的答疑!本文档最好的阅读方式是从上到下依次
    阅读(推荐),也可根据需要直接从目录查看相关文档!感谢您对Guns的支持!
    1.2 Guns教程
    教程采用视频的形式,讲述了Guns作者近年来工作经验的总结,以及自2017年3月份编写
    Guns的感悟。教程历时两个月精心打造,希望大家多多支持!
    点击查看教程详细介绍
    教程零售价格:199元
    如何获取教程?
    请添加作者(stylefeng)qq 332464581(请备注购买教程)或加入下方qq群联系群主购买
    1.3 获取帮助
    Guns官方qq交流群:254550081
    Guns官方git地址: https://gitee.com/naan1993/guns
  8. 使用手册
    注意:
    Guns运行环境:JDK1.8
    maven 3.3.9或更高
    请使用阿里云maven镜像
    作者当前使用开发工具为Eclipse oxygen.2 和 IDEA 2017.3
    2.1 下载项目
    登录码云平台,打开Guns主页,点击下载按钮下载
    2.2 导入项目
    2.2.1 eclipse导入
  9. 导入之前请检查eclipse的maven配置是否本机所安装的maven(一般不用eclipse自带的
    maven),如下
  10. 检查maven安装目录下的settings.xml是否配置了阿里云镜像
  11. 再次检查eclipse中maven的配置是否应用了当前maven安装目录的settings.xml配置文件
    (个人习惯全局和用户配置设置为一个),如下
  12. 以上设置完成,需要重启一下eclipse
  13. 点击eclipse菜单File->import,出现如下界面,选择 Existing maven project
  14. 找到下载的项目目录,并点击所有模块,之后点击Finish,导入成功
    2.2.2 IDEA导入
  15. 同样,导入前检查IDEA的maven配置是否正确
  16. 检查maven安装目录下的settings.xml是否配置了阿里云镜像(同 2.2.1节 第 2 步)
  17. 进入IDEA主界面,点击open,并选择下载好的guns代码的根目录
  18. 进入IDEA之后,右击 guns-parent ,选择 add as maven project ,即可完成导入
    2.3 运行项目
    运行前的准备:
    安装mysql数据库,作者所用mysql版本为5.7
  19. 执行 guns-admin 模块下的 sql/guns.sql 脚本,初始化guns的数据库环境
  20. 打开 guns-admin/src/main/resources/application.yml 配置文件,修
    改 数据连接 , 账号 和 密码 ,改为您所连接数据库的配置
  21. 如需修改服务器端口或者context-path,可参考下图,修改相应属性即可
  22. 执行 GunsApplication 类中的main方法,即可运行Guns系统
  23. 打开浏览器,输入 localhost:8080 ,即可访问到Guns的登录页面,默认登录账号密码:
    admin/111111
    2.4 打包部署
    目前Guns支持两种打包方式,即 jar包 和 war包
  24. 打包之前修改 guns-admin.pom 中的 packaging 节点,改为 jar 或者 war
  25. 在项目的 guns-parent 目录执行 clean package -Dmaven.test.skip=true ,即可打包,
    如下
  26. 命令执行成功后,在 guns-admin/target 目录下即可看到打包好的文件
    提示:若打的包为jar包,可通过 java -jar guns-admin-1.0.0-SNAPSHOT.jar 来启动Guns
    系统
  27. 开发手册
    用Guns开发手头常备如下几个工具:
    H+ 4.2源代码: 群文件里有
    mybatis-plus文档:http://mp.baomidou.com/
    beetl文档:http://ibeetl.com/guide/#beetl
    Spring Boot文档:https://docs.spring.io/springboot/docs/current/reference/html/
    3.1 了解Guns
    3.1.1 模块结构
  28. guns-admin 模块为后台管理系统模块,包括管理系统的业务代码,前端页面,项目的配置
    信息等等
  29. guns-core 模块为抽象出的核心(通用)模块,以供其他模块调用,此模块主要封装了一
    些通用的工具类,公共枚举,常量,配置等等
  30. guns-generator 为代码生成模块,其中代码生成模块整合了mybatis-plus的代码生成器和
    guns独有的代码生成器,可以一键生成entity,dao,service,html,js等代码,可减少新业
    务 70% 的工作量
  31. guns-parent 模块为其他所有模块的父模块,主要功能是管理项目中所有用到的jar,以
    及聚合其他模块
  32. guns-rest 为专门提供restful api的模块,该模块中主要实现了jwt鉴权和传输数据签名的
    机制
    3.1.2 包结构
    3.2 实战开发
    Guns开发三部曲 -> 1.建表 2.代码生成 3.添加菜单 4.适配业务代码
    下面以一个 订单业务 为例,实战演练如何用Guns编写简单的增删改查业务
    3.2.1 建表
    新建订单表如下:
  33. DROP TABLE IF EXISTS biz_order;
  34. CREATE TABLE biz_order (
  35. id int(11) NOT NULL AUTO_INCREMENT COMMENT ‘主键’,
  36. goods_name varchar(255) DEFAULT NULL COMMENT ‘商品名称’,
  37. place varchar(255) DEFAULT NULL COMMENT ‘下单地点’,
  38. create_time datetime DEFAULT NULL COMMENT ‘下单时间’,
  39. user_name varchar(255) DEFAULT NULL COMMENT ‘下单用户名称’,
  40. user_phone varchar(255) DEFAULT NULL COMMENT ‘下单用户电话’,
  41. PRIMARY KEY (id) USING BTREE
  42. ) ENGINE=InnoDB DEFAULT CHARSET=utf8 ROW_FORMAT=DYNAMIC COMMENT='订单表
    ';
  43. SET FOREIGN_KEY_CHECKS = 1;
    3.2.2 代码生成
    登录管理系统,打开代码生成页面,填写如下内容,注意看 红线部分 内容
    下面详细讲解代码生成使用:
  44. 项目路径: 代码生成的路径,具体到guns-admin模块的绝对路径, 一般不需要修改 ,因为程
    序会自动计算出guns-admin的绝对路径
  45. 项目的包: 为guns-admin的同 GunsApplication 类同一目录的包,如下图,一般也不需要
    修改
  46. 核心包: gun-core的包,一般也不需要修改
  47. 作者: 填写代码生成出的注释上的作者
  48. 业务名称: 生成业务的中午名称
  49. 模块名称: 对应代码中modular包下的模块名称,如下图,若模块名称填order,则生成出
    的业务代码回到order包下
  50. 父级菜单: 此项的选择会影响生成sql添加菜单项的切入点,生成出的sql文件执行后可自动
    增加到sys_menu菜单项,省去手动添加菜单的繁琐
  51. 表前缀: 填写此项会自动移除生成实体,mapper和service类的名称中包含的重复前缀,例
    如生成订单表业务代码时,填写 biz_ ,则生成的实体中不会包含Biz前缀名称,若不填写,则
    生成的实体类为BizOrder
  52. 数据表: 选择即为生成该表所对应的实体,dao,service等类
  53. 模板: 选择后生成相应的控制器,实体,service,dao代码等等
    生成代码之后需要重启一下管理系统,生成的代码才可以生效!
    3.3.3 添加菜单与分配权限
    生成代码之后,需要为管理系统添加菜单,才可以让新增加的业务显示到页面上,添加菜单有
    两种方式:
    第一种为手动添加菜单,依次点击 系统管理 -> 菜单管理 -> 点击添加 ,打开添加页面,如下
    这里需要注意如下几点:
    请求地址 需要和Controller中的RequestMapping的值一致
    排序 为同层级菜单中显示菜单的顺序
    父级编号 的选择可以更改菜单插入的位置
    图标 可以从H+的资源库中获取
    因为菜单管理不单单是对管理系统中的菜单管理,也包含权限的管理,所以需要选择
    是否是菜单这个选项
    第二种添加菜单的方式为直接执行代码生成中的sql脚本,默认生成的sql文件
    在 src/main/java 目录下,如下所示
  54. INSERT INTO guns.sys_menu (id, code, pcode, pcodes, name,
    icon, url, num, levels, ismenu, tips, status, isopen) V
    ALUES (‘956388083570089986’, ‘order’, ‘0’, ‘[0],’, ‘订单管理’, ‘’, ‘/ord
    er’, ‘99’, ‘1’, ‘1’, NULL, ‘1’, ‘0’);
  55. INSERT INTO guns.sys_menu (id, code, pcode, pcodes, name,
    icon, url, num, levels, ismenu, tips, status, isopen) V
    ALUES (‘956388083570089987’, ‘order_list’, ‘order’, ‘[0],[order],’, ‘订
    单管理列表’, ‘’, ‘/order/list’, ‘99’, ‘2’, ‘0’, NULL, ‘1’, ‘0’);
  56. INSERT INTO guns.sys_menu (id, code, pcode, pcodes, name,
    icon, url, num, levels, ismenu, tips, status, isopen) V
    ALUES (‘956388083570089988’, ‘order_add’, ‘order’, ‘[0],[order],’, ‘订
    单管理添加’, ‘’, ‘/order/add’, ‘99’, ‘2’, ‘0’, NULL, ‘1’, ‘0’);
  57. INSERT INTO guns.sys_menu (id, code, pcode, pcodes, name,
    icon, url, num, levels, ismenu, tips, status, isopen) V
    ALUES (‘956388083570089989’, ‘order_update’, ‘order’, ‘[0],[order],’, ’
    订单管理更新’, ‘’, ‘/order/update’, ‘99’, ‘2’, ‘0’, NULL, ‘1’, ‘0’);
  58. INSERT INTO guns.sys_menu (id, code, pcode, pcodes, name,
    icon, url, num, levels, ismenu, tips, status, isopen) V
    ALUES (‘956388083570089990’, ‘order_delete’, ‘order’, ‘[0],[order],’, ’
    订单管理删除’, ‘’, ‘/order/delete’, ‘99’, ‘2’, ‘0’, NULL, ‘1’, ‘0’);
  59. INSERT INTO guns.sys_menu (id, code, pcode, pcodes, name,
    icon, url, num, levels, ismenu, tips, status, isopen) V
    ALUES (‘956388083570089991’, ‘order_detail’, ‘order’, ‘[0],[order],’, ’
    订单管理详情’, ‘’, ‘/order/detail’, ‘99’, ‘2’, ‘0’, NULL, ‘1’, ‘0’);
    执行完成后可以看到,菜单管理页面中已经有了新添加的订单相关的菜单和资源,如下
    在添加完菜单只有,还需要给角色分配相关的菜单权限,才可以把新增的业务显示到菜单上
    打开 系统管理 -> 角色管理 ,给当前的登录的超级管理员,增加刚才新增的权限,如下图
    配置完成刷新页面即可看到,即可看到新增加的菜单,如下图,若看不到请重新登录
    到这里,基本的增删改查功能就实现了,如下图
    3.3.4 编写业务代码
    由于Guns的代码生成器还不能实现100%的智能,所以生成之后还需要对生成的代码做一些完
    善,如果有除了增删改查以外的业务,还需要手动编写。例如,上面编写的添加订单和修改订
    单里,下单时间默认是text文本框,这里需要手动改为laydate样式的日期框,如下图
    至此,guns的开发流程介绍完毕~!
    3.3 权限控制于校验
    3.3.1 用户,角色和资源
    用户、角色和资源(或者说权限),这三者的关系是 用户对应角色 , 角色对应资源 ,菜单和所
    有的按钮都可以看做是 资源 (或 权限 ),把某一个角色赋予相应的资源,那么该角色就会有访问
    该资源的权限,否则,该角色访问这些被管控的资源就会被服务器返回 403 没有权限 ,当角色
    绑定资源后还需要给 用户赋予角色 才可以让登录的用户访问相关服务器接口。
    一句话概括: 用户对应角色,角色对应资源
    3.3.2 如何对资源进行权限控制
    Guns系统中,通过在控制器上加 @Permission 注解进行权限校验,如下所示,该接口在被访
    问的时候,就会进行权限校验
    通过我们查找 用户对应的角色 ,并查找 角色对应的资源 ,可以找到,当前用户(admin)有该资源
    的权限,如下
    @Permission 注解中可以带一个String数组类型的参数,如下,加上该参数,则接口被限制为
    只有某个或某些角色才可访问
    权限的检查是通过 AOP 拦截 @Permission 注解完成的,当访问受权限控制的资源时, AOP 对
    当前请求的 servletPath 和数据库中 sys_menu 表的 url 字段进行匹配,如果当前用户所拥有
    的权限包含当前请求的 servletPath ,则访问这个接口成功
    3.3.3 前端页面对权限资源的显示
    在前端页面中,如果增删改查等按钮受权限控制,则我们需要对资源进行一个权限检查,如果
    有该资源的权限,才能让该按钮显示,通过 beetl 的 shiro注册方法 即可完成该项的检查
  60. @if(shiro.hasPermission("/menu/add")){
  61. <#button name=“添加” icon=“fa-plus” clickFun=“Menu.openAddMenu()”/>
  62. @}
  63. @if(shiro.hasPermission("/menu/edit")){
  64. <#button name=“修改” icon=“fa-edit”
    clickFun=“Menu.openChangeMenu()” space=“true”/>
  65. @}
  66. @if(shiro.hasPermission("/menu/remove")){
  67. <#button name=“删除” icon=“fa-remove” clickFun=“Menu.delMenu()” spa
    ce=“true”/>
  68. @}
    其中 shiro.hasPermission() 起到了权限检查的作用,如果有该资源对应的权限,则被检查
    的资源显示,若没有该资源的权限,则按钮不显示
    若想深入了解shiro和权限控制的实现原理,可参考视频教程第 12节 shiro与权限系统 ,内有
    70分钟详细的讲解
    3.4 多数据源的使用
    首先,我们新建一个数据库 guns_test ,并分别在 guns 数据库和 guns_test 数据库中分别新
    增同样结构的两个表 test
  69. DROP DATABASE IF EXISTS guns_test;
  70. CREATE DATABASE IF NOT EXISTS guns_test DEFAULT CHARSET utf8 COLLATE u
    tf8_general_ci;
  71. USE guns_test;
  72. SET NAMES utf8mb4;
  73. SET FOREIGN_KEY_CHECKS = 0;

  74. – Table structure for test

  75. DROP TABLE IF EXISTS test;
  76. CREATE TABLE test (
  77. id int(11) NOT NULL AUTO_INCREMENT,
  78. value varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci
    DEFAULT NULL,
  79. PRIMARY KEY (id) USING BTREE
  80. ) ENGINE = InnoDB AUTO_INCREMENT = 1 CHARACTER SET = utf8 COLLATE = utf
    8_general_ci ROW_FORMAT = Dynamic;
  81. SET FOREIGN_KEY_CHECKS = 1;
  82. 对表进行代码生成,方便测试两个数据源
  83. 打开application.yml中的多数据源开关
  84. 配置application.yml中的多数据源的连接信息
  85. 编写测试多数据源的代码,注意观察 @DataSource注解
  86. package com.stylefeng.guns.modular.order.service;
  87. /**
    • 测试多数据源的服务
    • @author fengshuonan
    • @date 2018年1月25日21:48:34
  88. */
  89. public interface ITestService {
  90. /**
    • 测试第二个数据源
  91. */
  92. void testBiz();
  93. /**
    • 测试guns本身的数据源
  94. */
  95. void testGuns();
  96. }
  97. package com.stylefeng.guns.modular.order.service.impl;
  98. import com.stylefeng.guns.common.constant.DatasourceEnum;
  99. import com.stylefeng.guns.common.persistence.dao.TestMapper;
  100. import com.stylefeng.guns.common.persistence.model.Test;
  101. import com.stylefeng.guns.core.mutidatasource.annotion.DataSource;
  102. import com.stylefeng.guns.modular.order.service.ITestService;
  103. import org.springframework.beans.factory.annotation.Autowired;
  104. import org.springframework.stereotype.Service;
  105. /**
    • 测试服务
    • @author fengshuonan
    • @date 2018年1月25日21:48:39
  106. */
  107. @Service
  108. public class TestServiceImpl implements ITestService {
  109. @Autowired
  110. TestMapper testMapper;
  111. @Override
  112. @DataSource(name = DatasourceEnum.DATA_SOURCE_BIZ)
  113. public void testBiz() {
  114. Test test1 = new Test();
  115. test1.setValue(“111”);
  116. testMapper.insert(test1);
  117. }
  118. @Override
  119. @DataSource(name = DatasourceEnum.DATA_SOURCE_GUNS)
  120. public void testGuns() {
  121. Test test1 = new Test();
  122. test1.setValue(“222”);
  123. testMapper.insert(test1);
  124. }
  125. }
  126. package com.stylefeng.guns.system;
  127. import com.stylefeng.guns.base.BaseJunit;
  128. import com.stylefeng.guns.modular.order.service.ITestService;
  129. import org.junit.Test;
  130. import org.springframework.beans.factory.annotation.Autowired;
  131. /**
    • 业务测试
    • @author fengshuonan
    • @date 2018年1月25日21:50:23
  132. */
  133. public class BizTest extends BaseJunit {
  134. @Autowired
  135. ITestService testService;
  136. @Test
  137. public void test() {
  138. testService.testGuns();
  139. testService.testBiz();
  140. }
  141. }
  142. 执行 BizTest 这个测试类,可以看出,两条数据同时插入了不同的数据库中的两张表中
    多数据源的原理就是一个项目同时配置了两个 DataSource ,并把这两个 DataSource 放
    到 DynamicDataSource 绑定,使用AOP进行动态切换当前操作的数据源。
    若想深入了解多数据源的配置和原理可参考 MybatisPlusConfig类 和 MultiSourceExAop类 ,
    也可参考视频教程第 7节 多多数数据据源源配配置置和和使使用用 ,内有详细的讲解
    3.5 如何分页
    Guns的分页是通过mybatis-plus的分页插件实现的,大体分如下两种情况
    3.5.1 简单查询的分页
    如果查询结果为单表查询,例如查询用户列表,则可以调用mybatis plus的自动生成的
    mapper中的 selectPage() 或者 selectMapsPage() 方法, Page 类的构造函数中第一个参数
    为当前查询第几页,第二个参数为每页的记录数。
    3.5.2 复杂查询的分页
    若查询结果是关联多个表的操作,则需要用到自定义的mapper,此时的分页操作也很简单,
    只需要给mapper的第一个参数设置为 Page 对象即可,例如Guns中 LogController 中的查
    询 操作日志列表 ,用的就是复杂查询的分页,我们可以看到在mybatis接口的第一个参数中,
    传递了 Page 对象,如下
    当mybatis执行此方法的时候,会被mybatis-plus的分页插件自动拦截到,并且把分页查询的
    结果返回到这个 Page 对象中!
    3.5.3 获取前端表格插件传值
    Guns中前端表格用的Bootstrap Table插件,在前端执行查询时,插件会自动往后台传递分页
    参数,并且默认的格式如下,
    Bootstrap Table 会传
    递 order(升序或者降序) , offset(每页偏移量) , limit(每页条数) , sort(排序的字段) 这四个
    参数,与之对应,后台封装了一个通用的接受分页参数的类 PageFactory ,从而不用每次都
    来 request.getParameter() 这样接收参数,如下所示,
  143. public class PageFactory {
  144. public Page defaultPage() {
  145. HttpServletRequest request = HttpKit.getRequest();
  146. int limit = Integer.valueOf(request.getParameter(“limit”));
    //每页多少条数据
  147. int offset = Integer.valueOf(request.getParameter(“offset”));
    //每页的偏移量(本页当前有多少条)
  148. String sort = request.getParameter(“sort”); //排序字段名
  149. String order = request.getParameter(“order”); //asc或
    desc(升序或降序)
  150. if (ToolUtil.isEmpty(sort)) {
  151. Page page = new Page<>((offset / limit + 1), limit);
  152. page.setOpenSort(false);
  153. return page;
  154. } else {
  155. Page page = new Page<>((offset / limit + 1), limit, sort
    );
  156. if (Order.ASC.getDes().equals(order)) {
  157. page.setAsc(true);
  158. } else {
  159. page.setAsc(false);
  160. }
  161. return page;
  162. }
  163. }
  164. }
    在后台代码中如需接收参数,构建分页Page对象的时候,只需如下这样一调用即可构建分页
    对象
  165. Page page = new PageFactory
    ().defaultPage();
    3.6 数据范围
    3.6.1 介绍
    Guns的数据范围是指当前部门的用户可以看到当前部门和子部门的数据,子部门的数据不可
    以看到上级部门的数据,但 超级管理员 例外,例如, userA 和 userB 两个用户都有查看用户列
    表的权限,但是 userA 在总公司部门, userB 在运营部,他们有如下部门关系
    那么 userA 在查看用户列表的时候能看到 公司所有人 的数据, userB 只能看到 运营部 的数据,
    这就是数据范围!
    3.6.2 如何使用
    使用时,只需要 new 一个 DataScope ,并在构造方法中传递给当前用户用后的部门权限(一般
    我们用封装好的 ShiroKit.getDeptDataScope() 方法即可获取到当前用户的部门权限集合),
    之后,传递给mybatis的dao方法的第一个参数即可,例子如下
  166. DataScope dataScope = new DataScope(ShiroKit.getDeptDataScope());
  167. List<Map<String, Object>> users = managerDao.selectUsers(dataScope, nam
    e, beginTime, endTime, deptid);
    注意: 在使用过程中,原mybatis的dao方法的查询结果中必须包含 deptid字段(默认情况) ,若
    部门id不叫deptid也可也初始化 DateScope 对象的时候,修改该对象的 scopeName 属性,改
    为自定义的部门id字段名即可
    3.6.3 原理
    数据范围的原理是利用了 mybatis拦截器 ,类似于mybatis-plus的分页插件,在原查询结果之
    上包装了一层 select筛选查询 ,如下
  168. select (原语句字段) from (原语句) where deptid in (DataScope对象中包含的部门
    id列表)
    若想深入了解数据范围的编写过程和原理可参考视频教程第 15节 数数据据范范围围使使用用和和原原理理 ,内有详
    细的讲解
    3.7 guns-rest模块的使用
    3.7.1 关于jwt鉴权
    在了解guns-rest模块的使用之前,需要了解一下jwt鉴权机制,下面给出一些参考资料
    什么是JWT-JSON WEB TOKEN -> https://www.jianshu.com/p/576dbf44b2ae
    说白了就是如果想请求服务器资源,需要先走服务器的auth接口,用账号和密码换取token,
    之后每个接口的请求都需要带着token去访问,否则就是鉴权失败.
    3.7.2 关于传输数据的签名
    签名机制是指客户端向服务端传输数据中,对传输数据进行md5加密,并且加密过程中利用
    Auth接口返回的随机字符串进行混淆加密,并把md5值同时附带给服务端,服务端通获取数
    据之后对数据再进行一次md5加密,若加密结果和客户端传来的数据一致,则认定客户端请求
    的数据是没有被篡改的,若不一致,则认为被加密的数据是被篡改的.
    3.7.3 guns-rest模块的运行流程
  169. 执行 guns-rest 模块下的db文件夹的sql初始化脚本 guns_rest.sql
  170. 启动 guns-rest 模块
  171. 下载postman接口测试工具或者insomnia接口测试工具,下面以insomnia接口测试工具
    为例,演示rest模块资源访问流程
  172. 访问/auth接口,传递给接口账号密码获取访问接口用的token,如下
    接口请求成功,auth接口返回给两个属性的json, randomKey 的作用是在之后接口的数据
    传输中对数据做MD5混淆加密用的, token 的作用是在之后访问资源的过程中,携带到请
    求的header中,证明我们是有权限访问资源的
  173. 接着去访问 /hello 接口,在访问之前,我们需要做两件事:
    第一 把请求hello接口的请求头Header中带一个 Authorization 属性,属性的值
    为 Bearer 和 token 值,注意中间用空格隔开
    第二 /hello 接口的所需要一个 @RequestBody 类型的数据,所以我们还需要传给这个接
    口一个json数据
    注意 json数据不能直接为如下的形式
  174. {“name”:“ffff”,“user”:“stylefeng”,“age”:12,“tips”:“code”}
    为了保证传输的数据的安全性,Guns做了对传输数据的签名,所以传输过程中需要对数据进
    行签名,我们可以直接运行 DecryptTest 这个测试类,直接生成签名好的json数据,如下
    这里注意填写md5的加密盐为刚才/auth接口生成的randomKey,运行后生成如下json
  175. {“object”:“eyJhZ2UiOjEyLCJuYW1lIjoiZmZmZiIsInRpcHMiOiJjb2RlIiwidXNlciI6In
    N0eWxlZmVuZyJ9”,“sign”:“d737820570c0881e8614272f9792e07d”}
    我们填入到接口的 请求体 里,并点击 Send
    接口访问成功!
    3.7.4 运行原理
    关于rest模块鉴权运行原理,其实就是一个简单的过滤器 AuthFilter类 实现的,若想了解运
    行机制可以查看下 auth包 下的类的代码(几十行)
    3.8 工作流
    最新的Guns 3.1版本引入了工作流框架flowable 6.2.0,并自带一个报销流程供大家参考,下
    面做一下介绍
    为了不和guns的数据库混淆,guns新建了一个数据库 guns_flowable ,并配置了一
    个 单独的数据源 来连接该数据库,在application.yml中的配置如下
    在guns启动过程中,若 guns_flowable 数据库没有表,flowable引擎会自动初始化工作流需
    要的表
    在报销管理业务中,一共有三个角色, 申请人 (账号:admin), 经理 (账号:manager), 老板 (账
    号:boss),他们的密码都是 111111 ,首先申请人填写报销单,
    填写之后需要在 报销审批 菜单中,提交下自己的申请
    如果报销金额小于500则是 经理(manager) 审批,我们登录经理的号,可以看到申请记录
    这里点击 通过 ,则该流程结束,如果点 不通过 则还需要申请人重新提交申请
    关于工作流的开发,可以参考flowable官方文档
    3.9 日志记录
    在我们日常开发中,对于某些关键业务,我们通常需要记录该操作的内容,例如修改了什么数
    据,修改的内容是什么,删除了哪些数据等等,在Guns中有一整套完善的解决方案来完成此
    项功能
    3.9.1 业务日志
    我们通过 @BussinessLog注解 来记录日志,该注解源码如下,
  176. /**
    • 标记需要做业务日志的方法
    • @author fengshuonan
    • @date 2017-03-31 12:46
  177. */
  178. @Inherited
  179. @Retention(RetentionPolicy.RUNTIME)
  180. @Target({ElementType.METHOD})
  181. public @interface BussinessLog {
  182. /**
    • 业务的名称,例如:“修改菜单”
  183. */
  184. String value() default “”;
  185. /**
    • 被修改的实体的唯一标识,例如:菜单实体的唯一标识为"id"
  186. */
  187. String key() default “id”;
  188. /**
    • 字典(用于查找key的中文名称和字段的中文名称)
  189. */
  190. Class<? extends AbstractDictMap> dict() default SystemDict.class;
  191. }
    其中, value 为需要记录日志的业务名称, key 为修改或删除内容的唯一标识,通过这个唯一
    标识可以知道具体的修改的哪条记录,删除的哪条记录等等, dict 为对修改字段的中文翻译
    字典,因为程序记录的都是英文的字段名称,这里通过字典,把英文字段和中文名称对应起
    来,那么日志信息记录到数据库中就可以变为中文的记录
    以 UserDict 为例,
  192. /**
    • 用户的字典
    • @author fengshuonan
    • @date 2017-05-06 15:01
  193. */
  194. public class UserDict extends AbstractDictMap {
  195. @Override
  196. public void init() {
  197. put(“userId”,“账号”);
  198. put(“avatar”,“头像”);
  199. put(“account”,“账号”);
  200. put(“name”,“名字”);
  201. put(“birthday”,“生日”);
  202. put(“sex”,“性别”);
  203. put(“email”,“电子邮件”);
  204. put(“phone”,“电话”);
  205. put(“roleid”,“角色名称”);
  206. put(“deptid”,“部门名称”);
  207. put(“roleIds”,“角色名称集合”);
  208. }
  209. @Override
  210. protected void initBeWrapped() {
  211. putFieldWrapperMethodName(“sex”,“getSexName”);
  212. putFieldWrapperMethodName(“deptid”,“getDeptName”);
  213. putFieldWrapperMethodName(“roleid”,“getSingleRoleName”);
  214. putFieldWrapperMethodName(“userId”,“getUserAccountById”);
  215. putFieldWrapperMethodName(“roleIds”,“getRoleName”);
  216. }
  217. }
    翻译字典类中包含两个方法 init() 和 initBeWrapped() ,其中 init() 为存放英文字段和中
    文字段的匹配, initBeWrapped() 操作的是把某些字段的数字值翻译为中文直观名称的过
    程,例如当修改用户信息时,用户修改了一个人性别信息(数据库中1是男,2是女),由1变为
    了2,程序记录的是数据库中1变为2,但是这句话给业务人员看到他是不知道1和2是什么东西
    的,所以这里做了一个 值的包装 ,把 1 包装为对应的中文名称 男 , 2 包装为对应的中文名
    称 女 ,这样,记录到数据库中,信息就变为了,xxx用户操作了 修改用户 功能,值由 男 变为
    了 女 .
    在initBeWrapped()方法中 putFieldWrapperMethodName() 这个方法的第一参数是被包装的
    字段名,第二个参数是 ConstantFactory 中的方法名,因为默认会调用 ConstantFactory 来
    包装值属性
    下面介绍业务日志记录的具体步骤:
    1.在需要被记录日志的接口上添加@BussinessLog注解,并根据需要填写三个属性
    (value,key,dict)
    2.若是添加或者修改业务,往往需要去编写Dict字典类
    3.若是修改业务,例如修改用户信息,因为点击更新用户的时候不会提交修改之前的
    数据,所以在更新用户信息之前需要保存一下用户的旧的信息才可以记录用户修改的
    内容,这个缓存用户临时信息的地方一般添加在跳转到用户详情接口,
    用 LogObjectHolder.me().set(user); 这行代码来缓存用户的旧的信息,具体用法
    可以参考 UserMgrController 类中的 userEdit() 和 edit()
    3.9.2 异常日志
    由于Guns有统一的异常拦截器,一般程序的报错,不管是业务异常还是未知的
    RuntimeException都会拦截并记录到数据库,若是您有自己的异常日志需要记录到数据库或
    者日志文件,推荐如下做法
  218. 如果记录到数据库,调用Guns的日志记录工具类,如下
  219. LogManager.me().executeLog();
    该方法为异步记录日志的方法,executeLog()方法中需要传递一个 TimerTask 对
    象,TimerTask对象可以用 LogTaskFactory 类创建,在 LogTaskFactory 类中,有5个方法,
    可以分别记录不用的日志,有 登录日志 , 退出日志 , 业务日志 , 异常日志 等等,可以自行选择
    调用
  220. 若需要记录日志到文件中,可以采用slf4j的 org.slf4j.Logger 类记录,具体方法如下
  221. //首先在类中初始化
  222. private Logger log = LoggerFactory.getLogger(this.getClass());
  223. //再在方法中调用
  224. log.error(“业务异常:”, e);
    3.10 如何使用缓存
    在Guns中使用缓存的地方不多,主要在ConstantFactory的查询中用了缓存,在
    ConstantFactory有高频调用的查询,所以在这些方法上加了缓存,搜索加上缓存后还要注意
    在修改了相关数据的时候要删除缓存,否则可能导致数据的不一致,在Guns中默认用的是
    Ehcache缓存,并配合了spring cache使用,用spring cache的好处就是,spring cache是缓
    存的抽象,如果想换为redis缓存,则不用修改代码,改一下配置即可实现,下面介绍两种操
    作缓存的方法
    3.10.1 用工具类操作
    在guns-core中封装了一些常用的操作Ehcache缓存的工具类 CacheKit ,此类采用静态方法
    调用的方式,可以添加,获取,删除缓存,用法非常简单
  225. //添加缓存,第一个参数为缓存的名称,是ehcache.xml中节点的NAME,key为添加
    缓存的键值,value为缓存的值
  226. public static void put(String cacheName, Object key, Object value);
  227. //获取某个缓存名称中的某个键值对应的缓存
  228. public static T get(String cacheName, Object key);
  229. //获取某个缓存的所有key
  230. public static List getKeys(String cacheName);
  231. //删除某个key对应的缓存
  232. public static void remove(String cacheName, Object key);
  233. //删除某个缓存名称下的所有缓存
  234. public static void removeAll(String cacheName);
    3.10.2 用spring cache操作缓存
    利用spring cache来操作缓存,可以很方便的在redis和ehcache之间切换缓存实现,利用
    spring cache 的缓存注解,加到方法之上可以很方便的缓存方法的结果,如果参数对应的键
    值存在了缓存,则下一次走这个方法则会直接返回缓存的结果,spring cache提供了4个注解
    来操作缓存.
    1.@Cacheable表明在调用方法之前,首先应该在缓存中查找方法的返回值,如果这
    个值能够找到,则会返回缓存的值,否则执行该方法,并将返回值放到缓存中,一般
    在数据库查询( select )之后调用这个注解
    2.@CachePut表明在方法调用前不会检查缓存,方法始终都会被调用,调用之后把
    结果放到缓存中,一般在数据库操作插入数据( save )的时候调用
    3.@CacheEvict表明spring会清除一个或者多个缓存,一般在数据库更新或者删除数
    据的时候调用( update 或者 delete )
    4.@Caching分组的注解,可以同时应用多个其他缓存注解,可以相同类型或者不同
    类型
    一般在用这些注解的时候,我们需要填写两个参数,一个是 value 代表缓存的名称,一个
    是 key 代表缓存的键值
    如上图所示,键值 key 一般包含两部分组成,一部分是 键的标识 例如上图中
    的 CacheKey.SINGLE_ROLE_NAME ,一部分是 参数 (一般是参数的值)例如上图中的 #roleId
    3.11 使用枚举
    在Guns中,枚举一般分两类,一种是状态枚举,一种是异常枚举, 状态枚举 的作用是枚举状
    态,列出状态的所有值,例如
  235. /**
    • 菜单的状态
    • @author fengshuonan
    • @Date 2017年1月22日 下午12:14:59
  236. */
  237. public enum MenuStatus {
  238. ENABLE(1, “启用”),
  239. DISABLE(0, “禁用”);
  240. int code;
  241. String message;
  242. MenuStatus(int code, String message) {
  243. this.code = code;
  244. this.message = message;
  245. }
  246. }
    异常枚举 的作用是枚举所有出现的业务异常,例如,
  247. /**
    • 所有业务异常的枚举
    • @author fengshuonan
    • @date 2016年11月12日 下午5:04:51
  248. */
  249. public enum BizExceptionEnum implements ServiceExceptionEnum{
  250. /**
    • 错误的请求
  251. */
  252. SESSION_TIMEOUT(400, “会话超时”),
  253. SERVER_ERROR(500, “服务器异常”);
  254. BizExceptionEnum(int code, String message) {
  255. this.code = code;
  256. this.message = message;
  257. }
  258. private Integer code;
  259. private String message;
  260. }
    使用枚举可以方便维护一些状态的值和管理所有的业务异常,所以在有 状态 或者新
    的 业务异常 的时候推荐写到枚举里
    3.12 spring boot热部署
    热部署的两种情况(适用于main方法启动)
    3.12.1 重新加载html
    如果是eclipse修改html保存后可以自动替换,如果不能请检查server配置
    如果是IDEA,可以修改html之后点击这个按钮,或者按快捷键CTRL+F9,即可更新
    3.12.2 重新加载java类
    如果是eclipse,在application.yml中找到配置spring.devtools.restart.enabled改为true即可
    如果是在IDEA中:
    第一步 请先修改spring.devtools.restart.enabled=true
    第二步 如果项目以guns根目录形式导入,例如下面所示情况
    需要修改application.yml中spring.devtools.restart.additional-paths添加上相应的项目名
    称,如下所示:
    如果项目以guns-parent形式导入,例如下面所示情况,则不需要修改additional-paths
    第三步 如下idea配置,打上对勾
    第四步 按下 Shift+Ctrl+Alt+/,选择Registry
    进去之后,找到如下图所示的选项,打勾
  261. 扩展与高级配置
    4.1 修改项目名和包名
    4.1.1 修改项目名
  262. 以guns-admin在idea环境下为例,右击项目,点 refactor->Rename
  263. 选 Rename directory
  264. 修改 guns-parent\pom.xml 改为 guns-myadmin
  265. 修改 guns-myadmin\pom 的artifactId改为 guns-myadmin
    4.1.2 修改包名
    下面以把 com.stylefeng.guns 改为 com.company.project 为例
  266. 选择 com.stylefeng.guns 包,仍然为右键 refactor->Rename
  267. 弹出对话框选择,有两个选项,一个是 Rename Package 也就是重命名所有模块
    中 com.stylefeng.guns 的包,一个是 Rename Directory 只重命名当前模块
    的 com.stylefeng.guns 包,我们选 Rename Package
  268. 输入 com.company.project 点Refactor,之后稍等几分钟
  269. 把所有包扫描的地方改为 com.company.project ,首先改 application.yml 中的mybatisplus的配置
  270. 修改 MybatisPlusConfig 类中的扫描注解
  271. 修改 MultiSourceExAop 类中AOP的扫描
  272. 修改 LogAop 类中aop的扫描
  273. 修改 PermissionAop 中aop的扫描
  274. 修改所有mybatis的mapping.xml中的类配置,例如如下配置中的 namespace 和 type
    4.2 放过接口权限验证
    在日常开发中,我们可能需要放过某个接口的权限验证,即用户不用登录就可以访问接口
  275. 首先我们在BlackboardController这个类中,增加一个接口
  276. 在 ShiroConfig 类中,找到 shiroFilter() 这个方法,配置上这个接口,注意加到最上
    面,这个Map是有顺序的,可以用通配符
  277. 启动应用,并且不登录系统,我们访问 http://localhost:8080/blackboard/test 即可看
    到,这个接口不需要登录也可以访问到
    4.3 静态资源和模板位置的变更
    由于spring boot默认是把静态资源文件css,js等放到 resources/static 目录的,默认把前
    端模板文件放到 resources/templates 目录,笔者认为前端面页面还是按maven的思想放
    到 webapp 目录比较分层清晰,所以做了一个变动,主要变动如下:
    yml配置中增加了两个配置
    若想变动资源和模板的位置修改这两个配置即可
    4.4 三个或更多数据源如何配置
  278. 新建类似于 MutiDataSourceProperties 这样的类,用于接收第三个数据源
  279. 在 MybatisPlusConfig 类中,配置类似于如下代码的方法
  280. 在 DynamicDataSource 配置中,增加第二步新加的数据源
  281. 同时在 DatasourceEnum 类中,增加第三个数据源名称
  282. 使用方法同第二个数据源使用方法相同
    4.5 添加登录验证码
    Guns系统中内置了登录输入验证码的功能,因为开发方便调试,所以默认是关闭的,若需要
    开启该功能,只需要在application.yml中配置开启即可,如下
    4.6 spring profile
    在实际的生产环境中,往往存在多个环境,例如开发环境(dev),测试环境(test),生产环境
    (prod),并且不同环境的数据库和日志记录等配置的都不相同,为了每次发布不同环境的包
    时,不来回的修改这些配置,特引入了spring profile,引入之后,我们只需要把所有环境的
    配置都预先列出来,在每次发布不同环境的包的时候,只需要选择当前激活的是哪个环境的配
    置即可快速切换配置,关于spring profile的详细描述可参考这篇博
    文https://www.jianshu.com/p/948c303b2253
    在yml配置中,我们用 — 来切分不同profile的配置,如下
    在分割线的下边我们就可以配置不同环境的配置了, profile 可以配置多个,只需要
    用 spring.profiles 来标记当前节段的 profile 的名字即可
    并用 spring.profiles.active 来激活当前的 profile 配置即可
    — 把配置切分成了多个节段,其中第一节是所有profile共有的配置,例如guns的配置中的
    这一大段
    第一节段 — 下方的配置则是不同的profile的配置
    4.7 多机器部署开启spring session
    多机环境把session托管给redis存储,所以要部署和配置redis,另外需要注意的是开启相关配

    1.单机环境下不需要依赖spring-session,所以需要把相关依赖的注释打开
  283. org.springframework.session
  284. spring-session
  285. compile

2.修改application.yml中guns.spring-session-open配置,改为true,打开spring-session

  1. guns.spring-session-open=true
    3.配置application.yml中,spring.redis.host,spring.redis.port,spring.redis.password
  2. spring.redis.host=xxx
  3. spring.redis.port=xxx
  4. spring.redis.password=xxx
    4.需要把SpringSessionConfig类中的注释打开
  5. @EnableRedisHttpSession(maxInactiveIntervalInSeconds = 1800)
    5.如需配置session失效时间,请在SpringSessionConfig类中修改
    maxInactiveIntervalInSeconds属性值
    4.8 使用Redis
    默认Guns在部署分布式的环境中使用了Redis作为分布式session的存储,如果想在项目中用
    redis做缓存或者存储,建议使用RedisTemplate来进行操作
    1.首先下载Redis,可以在Guns的qq群里找到redis的可执行包,或者去redis官网下载
    2.在项目的config包下建立redis的配置类
  6. /**
    • Redis配置
    • @author fengshuonan
    • @date 2018年1月28日 11:49:12
  7. */
  8. @Configuration
  9. @EnableCaching
  10. public class RedisConfig {
  11. @Bean
  12. public RedisCacheManager cacheManager(RedisTemplate<String, Object>
    redisTemplate) {
  13. RedisCacheManager redisCacheManager = new RedisCacheManager(red
    isTemplate);
  14. redisCacheManager.setDefaultExpiration(30 * 60);
  15. return redisCacheManager;
  16. }
  17. @Bean
  18. public RedisTemplate<String, Object>
    redisTemplate(RedisConnectionFactory factory) {
  19. RedisTemplate<String, Object> template = new RedisTemplate<>();
  20. template.setConnectionFactory(factory);
  21. template.afterPropertiesSet();
  22. return template;
  23. }
  24. }
    3.在 application.yml 中配置redis的连接属性
    4.在 GunsApplication 类中,编写 CommandLineRunner 来测试一下Redis的连接
  25. @Bean
  26. CommandLineRunner commandLineRunner() {
  27. return new CommandLineRunner() {
  28. @Override
  29. public void run(String… strings) throws Exception {
  30. BoundValueOperations<String, Object> test = redisTemplate.b
    oundValueOps(“test”);
  31. test.set(“test value”);
  32. Object o = test.get();
  33. System.out.println(o);
  34. }
  35. };
  36. }
    4.9 XSS过滤器
    4.9.1 介绍
    为了抵御XSS攻击,不让用户在录入数据的同时插入恶意js代码,Guns对所有传入数据中带
    有 html标签 和
  37. @}
    整个页面被 @layout 所包围, @layout 是beetl的引用布局(具体用法文档可以查看beetl的官
    方文档),Guns中内置了 /common/_container.html 这样一个布局,可以
    把 /common/_container.html 理解为一个html的抽象封装,我们每个页面都继承自这个模
    板,默认包含了一系列通用的js css引用等,这样写即简化了我们的开发和维护,又使我们的
    代码简洁有序,在 /common/_container.html 中的 ${layoutContent} 就代表我们每个页面
    不同的html
    5.4.2 标签
    为了把一些重复性的html封装起来,我们使用了beetl的标签,这些标签
    的 本质是把重复性的html代码用一行html标签替代 ,从而方便使用,易于维护,这些标签都放
    在 common/tags 这个文件夹
    标签中的一些属性例如 ${name} ${id} 等属性均为掉钱被调用时,从调用体的属性传
    来 <#xxxTag name=“xxx” id=“xxx”>
  38. 常见问题答疑
    6.1 默认的系统登录账号和密码是多少
    账号是 admin 密码是 111111
    6.2 权限异常
    6.3 为何分页是前端实现
    部分页面因为数据量比较少,就直接用客户端分页了,日志页面的分页是采用服务端分页的,
    如果其他业务有特别需要,可以手动设置一下
    6.4 关于 ${ctxPath}
    这个变量在哪里定义的?这个是beetl自带的具体请看beetl文档
    6.5 放过某些url的权限验证
    在ShiroConfig类下的shiroFilter方法里配置,参考4.2节
    6.6 主页的搜索功能
    主页的搜索功能目前没有写实际业务,只是装饰作用
    6.7 运行sql报错
    在初始化guns.sql过程中,可能会出现
  39. [Err] 1067 - Invalid default value for ‘createtime’
    这样的报错,Guns目前支持mysql 5.7的运行环境,若您的mysql低于此版本,请
    把 sys_expense 表的 DEFAULT CURRENT_TIMESTAMP 这部分语句去掉即可
    6.8 关于打包
    Guns现在是多模块组成,各个模块之间有依赖关系,打包时,先修改guns-admin模块的
    pom的 节点,改为jar或者war
    再在 guns-parent 目录下输入 clean package -Dmaven.test.skip=true 来打出所有模块的

    执行成功后,在guns-admin目录下即可看到打好的包
    6.9 查询结果的驼峰转化问题
    直接参考mp的文档
    6.10 为何使用beetl
    beetl具有语法简介,性能超高,文档全,社区活跃等特点,所以建议用beetl模板引擎
    6.11 为何有的业务没有service层
    部分业务比较简单,所以就没写service层,写service是为了让复杂业务更有条理,更清晰。
    (此项仅供参考)
    6.12 为何既有dao,又有mapper
    mapper是mybatis-plus自动生成的,里边有许多mybatis-plus增强的方法,dao是自己写的
    业务,mybatis-plus自动生成代码时会覆盖mapper,所以就把自己写的dao分开了,生成代
    码的时候不影响。(此项仅供参考)

猜你喜欢

转载自blog.csdn.net/qq_42910468/article/details/105071258
今日推荐