Guns 技术文档 v1.0

Guns 技术文档 v1.0
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

  1. 序言
    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

  2. 使用手册
    注意:
    Guns运行环境:JDK1.8 maven 3.3.9或更高 请使用阿里云maven镜像 作者当前使用开发工具为Eclipse oxygen.2 和 IDEA 2017.3
    2.1 下载项目
    登录码云平台,打开Guns主页,点击下载按钮下载
    2.2 导入项目
    2.2.1 eclipse导入 1. 导入之前请检查eclipse的maven配置是否本机所安装的maven(一般不用eclipse自带的 maven),如下

  3. 检查maven安装目录下的settings.xml是否配置了阿里云镜像

  4. 再次检查eclipse中maven的配置是否应用了当前maven安装目录的settings.xml配置文件 (个人习惯全局和用户配置设置为一个),如下

  5. 以上设置完成,需要重启一下eclipse 5. 点击eclipse菜单File->import,出现如下界面,选择Existing maven project

  6. 找到下载的项目目录,并点击所有模块,之后点击Finish,导入成功
    2.2.2 IDEA导入 1. 同样,导入前检查IDEA的maven配置是否正确

  7. 检查maven安装目录下的settings.xml是否配置了阿里云镜像(同2.2.1节 第2步) 3. 进入IDEA主界面,点击open,并选择下载好的guns代码的根目录

  8. 进入IDEA之后,右击guns-parent,选择add as maven project,即可完成导入
    2.3 运行项目
    运行前的准备:
    安装mysql数据库,作者所用mysql版本为5.7

  9. 执行guns-admin模块下的sql/guns.sql脚本,初始化guns的数据库环境

  10. 打开guns-admin/src/main/resources/application.yml配置文件,修 改 数据连接 , 账号 和 密码 ,改为您所连接数据库的配置

  11. 如需修改服务器端口或者context-path,可参考下图,修改相应属性即可

  12. 执行GunsApplication类中的main方法,即可运行Guns系统 5. 打开浏览器,输入localhost:8080,即可访问到Guns的登录页面,默认登录账号密码: admin/111111
    2.4 打包部署
    目前Guns支持两种打包方式,即jar包 和war包

  13. 打包之前修改guns-admin.pom 中的packaging 节点,改为jar或者war

  14. 在项目的guns-parent目录执行clean package -Dmaven.test.skip=true,即可打包, 如下

  15. 命令执行成功后,在guns-admin/target目录下即可看到打包好的文件
    提示:若打的包为jar包,可通过java -jar guns-admin-1.0.0-SNAPSHOT.jar来启动Guns 系统

  16. 开发手册
    用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 模块结构

  17. guns-admin模块为后台管理系统模块,包括管理系统的业务代码,前端页面,项目的配置 信息等等

  18. guns-core 模块为抽象出的核心(通用)模块,以供其他模块调用,此模块主要封装了一 些通用的工具类,公共枚举,常量,配置等等 3. guns-generator 为代码生成模块,其中代码生成模块整合了mybatis-plus的代码生成器和 guns独有的代码生成器,可以一键生成entity,dao,service,html,js等代码,可减少新业 务70%的工作量 4. guns-parent模块为其他所有模块的父模块,主要功能是管理项目中所有用到的jar,以 及聚合其他模块 5. guns-rest 为专门提供restful api的模块,该模块中主要实现了jwt鉴权和传输数据签名的 机制
    3.1.2 包结构
    3.2 实战开发
    Guns开发三部曲 -> 1.建表 2.代码生成 3.添加菜单 4.适配业务代码
    下面以一个 订单业务 为例,实战演练如何用Guns编写简单的增删改查业务
    3.2.1 建表 新建订单表如下:

  19. DROP TABLE IF EXISTS biz_order; 2. CREATE TABLE biz_order ( 3. id int(11) NOT NULL AUTO_INCREMENT COMMENT ‘主键’, 4. goods_name varchar(255) DEFAULT NULL COMMENT ‘商品名称’, 5. place varchar(255) DEFAULT NULL COMMENT ‘下单地点’, 6. create_time datetime DEFAULT NULL COMMENT ‘下单时间’, 7. user_name varchar(255) DEFAULT NULL COMMENT ‘下单用户名称’, 8. user_phone varchar(255) DEFAULT NULL COMMENT ‘下单用户电话’, 9. PRIMARY KEY (id) USING BTREE 10. ) ENGINE=InnoDB DEFAULT CHARSET=utf8 ROW_FORMAT=DYNAMIC COMMENT='订单表 '; 11. 12. SET FOREIGN_KEY_CHECKS = 1;
    3.2.2 代码生成 登录管理系统,打开代码生成页面,填写如下内容,注意看 红线部分 内容
    下面详细讲解代码生成使用: 1. 项目路径: 代码生成的路径,具体到guns-admin模块的绝对路径, 一般不需要修改 ,因为程 序会自动计算出guns-admin的绝对路径 2. 项目的包: 为guns-admin的同GunsApplication类同一目录的包,如下图,一般也不需要 修改

  20. 核心包: gun-core的包,一般也不需要修改 4. 作者: 填写代码生成出的注释上的作者 5. 业务名称: 生成业务的中午名称 6. 模块名称: 对应代码中modular包下的模块名称,如下图,若模块名称填order,则生成出 的业务代码回到order包下

  21. 父级菜单: 此项的选择会影响生成sql添加菜单项的切入点,生成出的sql文件执行后可自动 增加到sys_menu菜单项,省去手动添加菜单的繁琐 8. 表前缀: 填写此项会自动移除生成实体,mapper和service类的名称中包含的重复前缀,例 如生成订单表业务代码时,填写biz_ ,则生成的实体中不会包含Biz前缀名称,若不填写,则 生成的实体类为BizOrder 9. 数据表: 选择即为生成该表所对应的实体,dao,service等类 10. 模板: 选择后生成相应的控制器,实体,service,dao代码等等
    生成代码之后需要重启一下管理系统,生成的代码才可以生效!
    3.3.3 添加菜单与分配权限 生成代码之后,需要为管理系统添加菜单,才可以让新增加的业务显示到页面上,添加菜单有 两种方式: 第一种为手动添加菜单,依次点击 系统管理 -> 菜单管理 -> 点击添加 ,打开添加页面,如下
    这里需要注意如下几点:
    请求地址 需要和Controller中的RequestMapping的值一致 排序 为同层级菜单中显示菜单的顺序 父级编号 的选择可以更改菜单插入的位置 图标 可以从H+的资源库中获取 因为菜单管理不单单是对管理系统中的菜单管理,也包含权限的管理,所以需要选择 是否是菜单这个选项
    第二种添加菜单的方式为直接执行代码生成中的sql脚本,默认生成的sql文件 在src/main/java目录下,如下所示

  22. 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’); 2. 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’); 3. 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’); 4. 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’); 5. 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’); 6. 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注册方法 即可完成该项的检查

  23. @if(shiro.hasPermission("/menu/add")){ 2. <#button name=“添加” icon=“fa-plus” clickFun=“Menu.openAddMenu()”/> 3. @} 4. @if(shiro.hasPermission("/menu/edit")){ 5. <#button name=“修改” icon=“fa-edit” clickFun=“Menu.openChangeMenu()” space=“true”/> 6. @} 7. @if(shiro.hasPermission("/menu/remove")){ 8. <#button name=“删除” icon=“fa-remove” clickFun=“Menu.delMenu()” spa ce=“true”/> 9. @}
    其中shiro.hasPermission()起到了权限检查的作用,如果有该资源对应的权限,则被检查 的资源显示,若没有该资源的权限,则按钮不显示
    若想深入了解shiro和权限控制的实现原理,可参考视频教程第12节 节 shiro与权限系统 与权限系统 ,内有 70分钟详细的讲解
    3.4 多数据源的使用
    首先,我们新建一个数据库guns_test ,并分别在guns 数据库和guns_test 数据库中分别新 增同样结构的两个表test

  24. DROP DATABASE IF EXISTS guns_test; 2. CREATE DATABASE IF NOT EXISTS guns_test DEFAULT CHARSET utf8 COLLATE u tf8_general_ci; 3. 4. USE guns_test; 5. 6. SET NAMES utf8mb4; 7. SET FOREIGN_KEY_CHECKS = 0; 8. 9. – ---------------------------10. – Table structure for test 11. – ---------------------------12. DROP TABLE IF EXISTS test; 13. CREATE TABLE test ( 14. id int(11) NOT NULL AUTO_INCREMENT, 15. value varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL, 16. PRIMARY KEY (id) USING BTREE 17. ) ENGINE = InnoDB AUTO_INCREMENT = 1 CHARACTER SET = utf8 COLLATE = utf 8_general_ci ROW_FORMAT = Dynamic; 18. 19. SET FOREIGN_KEY_CHECKS = 1;

  25. 对表进行代码生成,方便测试两个数据源

  26. 打开application.yml中的多数据源开关

  27. 配置application.yml中的多数据源的连接信息

  28. 编写测试多数据源的代码,注意观察@DataSource注解

  29. package com.stylefeng.guns.modular.order.service; 2. 3. /** 4. * 测试多数据源的服务 5. * 6. * @author fengshuonan 7. * @date 2018年1月25日21:48:34 8. / 9. public interface ITestService { 10. 11. /* 12. * 测试第二个数据源 13. / 14. void testBiz(); 15. 16. /* 17. * 测试guns本身的数据源 18. */ 19. void testGuns(); 20. }

  30. package com.stylefeng.guns.modular.order.service.impl; 2. 3. import com.stylefeng.guns.common.constant.DatasourceEnum; 4. import com.stylefeng.guns.common.persistence.dao.TestMapper; 5. import com.stylefeng.guns.common.persistence.model.Test; 6. import com.stylefeng.guns.core.mutidatasource.annotion.DataSource; 7. import com.stylefeng.guns.modular.order.service.ITestService; 8. import org.springframework.beans.factory.annotation.Autowired; 9. import org.springframework.stereotype.Service;

    1. /** 12. * 测试服务 13. * 14. * @author fengshuonan 15. * @date 2018年1月25日21:48:39 16. */ 17. @Service 18. public class TestServiceImpl implements ITestService { 19. 20. @Autowired 21. TestMapper testMapper; 22. 23. @Override 24. @DataSource(name = DatasourceEnum.DATA_SOURCE_BIZ) 25. public void testBiz() { 26. Test test1 = new Test(); 27. test1.setValue(“111”); 28. testMapper.insert(test1); 29. } 30. 31. 32. @Override 33. @DataSource(name = DatasourceEnum.DATA_SOURCE_GUNS) 34. public void testGuns() { 35. Test test1 = new Test(); 36. test1.setValue(“222”); 37. testMapper.insert(test1); 38. } 39. }
  31. package com.stylefeng.guns.system; 2. 3. import com.stylefeng.guns.base.BaseJunit; 4. import com.stylefeng.guns.modular.order.service.ITestService; 5. import org.junit.Test; 6. import org.springframework.beans.factory.annotation.Autowired; 7. 8. /** 9. * 业务测试 10. * 11. * @author fengshuonan 12. * @date 2018年1月25日21:50:23 13. */

  32. public class BizTest extends BaseJunit { 15. 16. @Autowired 17. ITestService testService; 18. 19. @Test 20. public void test() { 21. testService.testGuns(); 22. 23. testService.testBiz(); 24. } 25. }

  33. 执行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()这样接收参数,如下所示,

  34. public class PageFactory { 2. 3. public Page defaultPage() { 4. HttpServletRequest request = HttpKit.getRequest(); 5. int limit = Integer.valueOf(request.getParameter(“limit”)); //每页多少条数据 6. int offset = Integer.valueOf(request.getParameter(“offset”)); //每页的偏移量(本页当前有多少条) 7. String sort = request.getParameter(“sort”); //排序字段名 称 8. String order = request.getParameter(“order”); //asc或 desc(升序或降序) 9. if (ToolUtil.isEmpty(sort)) { 10. Page page = new Page<>((offset / limit + 1), limit); 11. page.setOpenSort(false); 12. return page; 13. } else { 14. Page page = new Page<>((offset / limit + 1), limit, sort ); 15. if (Order.ASC.getDes().equals(order)) { 16. page.setAsc(true); 17. } else { 18. page.setAsc(false); 19. } 20. return page; 21. } 22. } 23. }
    在后台代码中如需接收参数,构建分页Page对象的时候,只需如下这样一调用即可构建分页 对象

  35. 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方法的第一个参数即可,例子如下

  36. DataScope dataScope = new DataScope(ShiroKit.getDeptDataScope()); 2. 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筛选查询,如下

  37. 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模块的运行流程 1. 执行guns-rest 模块下的db文件夹的sql初始化脚本guns_rest.sql 2. 启动guns-rest 模块

  38. 下载postman接口测试工具或者insomnia接口测试工具,下面以insomnia接口测试工具 为例,演示rest模块资源访问流程 4. 访问/auth接口,传递给接口账号密码获取访问接口用的token,如下
    接口请求成功,auth接口返回给两个属性的json,randomKey 的作用是在之后接口的数据 传输中对数据做MD5混淆加密用的,token的作用是在之后访问资源的过程中,携带到请 求的header中,证明我们是有权限访问资源的 5. 接着去访问/hello接口,在访问之前,我们需要做两件事: 第一 把请求hello接口的请求头Header中带一个 Authorization属性,属性的值 为Bearer和token值,注意中间用空格隔开
    第二 /hello接口的所需要一个@RequestBody类型的数据,所以我们还需要传给这个接 口一个json数据
    注意 json数据不能直接为如下的形式

  39. {“name”:“ffff”,“user”:“stylefeng”,“age”:12,“tips”:“code”}
    为了保证传输的数据的安全性,Guns做了对传输数据的签名,所以传输过程中需要对数据进 行签名,我们可以直接运行DecryptTest这个测试类,直接生成签名好的json数据,如下
    这里注意填写md5的加密盐为刚才/auth接口生成的randomKey,运行后生成如下json

  40. {“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注解 来记录日志,该注解源码如下,

  1. /** 2. * 标记需要做业务日志的方法 3. * 4. * @author fengshuonan 5. * @date 2017-03-31 12:46 6. / 7. @Inherited 8. @Retention(RetentionPolicy.RUNTIME) 9. @Target({ElementType.METHOD}) 10. public @interface BussinessLog { 11. 12. /* 13. * 业务的名称,例如:“修改菜单” 14. / 15. String value() default “”; 16. 17. /* 18. * 被修改的实体的唯一标识,例如:菜单实体的唯一标识为"id" 19. / 20. String key() default “id”; 21. 22. /* 23. * 字典(用于查找key的中文名称和字段的中文名称) 24. */ 25. Class<? extends AbstractDictMap> dict() default SystemDict.class; 26. }
    其中,value为需要记录日志的业务名称,key为修改或删除内容的唯一标识,通过这个唯一 标识可以知道具体的修改的哪条记录,删除的哪条记录等等,dict 为对修改字段的中文翻译
    字典,因为程序记录的都是英文的字段名称,这里通过字典,把英文字段和中文名称对应起 来,那么日志信息记录到数据库中就可以变为中文的记录
    以UserDict为例,
  2. /** 2. * 用户的字典 3. * 4. * @author fengshuonan 5. * @date 2017-05-06 15:01 6. */ 7. public class UserDict extends AbstractDictMap { 8. 9. @Override 10. public void init() { 11. put(“userId”,“账号”); 12. put(“avatar”,“头像”); 13. put(“account”,“账号”); 14. put(“name”,“名字”); 15. put(“birthday”,“生日”); 16. put(“sex”,“性别”); 17. put(“email”,“电子邮件”); 18. put(“phone”,“电话”); 19. put(“roleid”,“角色名称”); 20. put(“deptid”,“部门名称”); 21. put(“roleIds”,“角色名称集合”); 22. } 23. 24. @Override 25. protected void initBeWrapped() { 26. putFieldWrapperMethodName(“sex”,“getSexName”); 27. putFieldWrapperMethodName(“deptid”,“getDeptName”); 28. putFieldWrapperMethodName(“roleid”,“getSingleRoleName”); 29. putFieldWrapperMethodName(“userId”,“getUserAccountById”); 30. putFieldWrapperMethodName(“roleIds”,“getRoleName”); 31. } 32. }
    翻译字典类中包含两个方法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都会拦截并记录到数据库,若是您有自己的异常日志需要记录到数据库或 者日志文件,推荐如下做法
  3. 如果记录到数据库,调用Guns的日志记录工具类,如下
  4. LogManager.me().executeLog();
    该方法为异步记录日志的方法,executeLog()方法中需要传递一个TimerTask 对 象,TimerTask对象可以用LogTaskFactory类创建,在LogTaskFactory类中,有5个方法, 可以分别记录不用的日志,有 登录日志 , 退出日志 , 业务日志 , 异常日志 等等,可以自行选择 调用 2. 若需要记录日志到文件中,可以采用slf4j的org.slf4j.Logger类记录,具体方法如下
  5. //首先在类中初始化 2. private Logger log = LoggerFactory.getLogger(this.getClass()); 3. 4. //再在方法中调用 5. log.error(“业务异常:”, e);
    3.10 如何使用缓存
    在Guns中使用缓存的地方不多,主要在ConstantFactory的查询中用了缓存,在 ConstantFactory有高频调用的查询,所以在这些方法上加了缓存,搜索加上缓存后还要注意 在修改了相关数据的时候要删除缓存,否则可能导致数据的不一致,在Guns中默认用的是 Ehcache缓存,并配合了spring cache使用,用spring cache的好处就是,spring cache是缓 存的抽象,如果想换为redis缓存,则不用修改代码,改一下配置即可实现,下面介绍两种操 作缓存的方法
    3.10.1 用工具类操作 在guns-core中封装了一些常用的操作Ehcache缓存的工具类CacheKit,此类采用静态方法 调用的方式,可以添加,获取,删除缓存,用法非常简单
  6. //添加缓存,第一个参数为缓存的名称,是ehcache.xml中节点的NAME,key为添加 缓存的键值,value为缓存的值 2. public static void put(String cacheName, Object key, Object value); 3. 4. //获取某个缓存名称中的某个键值对应的缓存 5. public static T get(String cacheName, Object key); 6. 7. //获取某个缓存的所有key 8. public static List getKeys(String cacheName); 9. 10. //删除某个key对应的缓存 11. public static void remove(String cacheName, Object key); 12. 13. //删除某个缓存名称下的所有缓存 14. 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中,枚举一般分两类,一种是状态枚举,一种是异常枚举, 状态枚举 的作用是枚举状 态,列出状态的所有值,例如
  7. /**
    • 菜单的状态 3. * 4. * @author fengshuonan 5. * @Date 2017年1月22日 下午12:14:59 6. */ 7. public enum MenuStatus { 8. 9. ENABLE(1, “启用”), 10. DISABLE(0, “禁用”); 11. 12. int code; 13. String message; 14. 15. MenuStatus(int code, String message) { 16. this.code = code; 17. this.message = message; 18. } 19. 20. … 21. }
      异常枚举 的作用是枚举所有出现的业务异常,例如,
  8. /** 2. * 所有业务异常的枚举 3. * 4. * @author fengshuonan 5. * @date 2016年11月12日 下午5:04:51 6. / 7. public enum BizExceptionEnum implements ServiceExceptionEnum{ 8. 9. /* 10. * 错误的请求 11. */ 12. SESSION_TIMEOUT(400, “会话超时”), 13. SERVER_ERROR(500, “服务器异常”); 14. 15. BizExceptionEnum(int code, String message) { 16. this.code = code; 17. this.message = message; 18. } 19. 20. private Integer code; 21.
  9. private String message; 23. 24.     ... 25. }
    

使用枚举可以方便维护一些状态的值和管理所有的业务异常,所以在有 状态 或者新 的 业务异常 的时候推荐写到枚举里
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
进去之后,找到如下图所示的选项,打勾
4. 扩展与高级配置
4.1 修改项目名和包名
4.1.1 修改项目名 1. 以guns-admin在idea环境下为例,右击项目,点refactor->Rename
2. 选Rename directory
3. 修改guns-parent\pom.xml改为guns-myadmin
4. 修改guns-myadmin\pom的artifactId改为guns-myadmin
4.1.2 修改包名 下面以把com.stylefeng.guns改为com.company.project为例 1. 选择com.stylefeng.guns包,仍然为右键refactor->Rename 2. 弹出对话框选择,有两个选项,一个是Rename Package也就是重命名所有模块 中com.stylefeng.guns的包,一个是Rename Directory只重命名当前模块 的com.stylefeng.guns包,我们选Rename Package
3. 输入com.company.project点Refactor,之后稍等几分钟

  1. 把所有包扫描的地方改为com.company.project,首先改application.yml中的mybatisplus的配置
  2. 修改MybatisPlusConfig类中的扫描注解
  3. 修改MultiSourceExAop类中AOP的扫描
  4. 修改LogAop类中aop的扫描
  5. 修改PermissionAop中aop的扫描
  6. 修改所有mybatis的mapping.xml中的类配置,例如如下配置中的namespace 和type
    4.2 放过接口权限验证
    在日常开发中,我们可能需要放过某个接口的权限验证,即用户不用登录就可以访问接口 1. 首先我们在BlackboardController这个类中,增加一个接口
  7. 在ShiroConfig类中,找到shiroFilter()这个方法,配置上这个接口,注意加到最上 面,这个Map是有顺序的,可以用通配符
  8. 启动应用,并且不登录系统,我们访问http://localhost:8080/blackboard/test即可看
    到,这个接口不需要登录也可以访问到
    4.3 静态资源和模板位置的变更
    由于spring boot默认是把静态资源文件css,js等放到 resources/static目录的,默认把前 端模板文件放到resources/templates目录,笔者认为前端面页面还是按maven的思想放 到webapp目录比较分层清晰,所以做了一个变动,主要变动如下:
    yml配置中增加了两个配置
    若想变动资源和模板的位置修改这两个配置即可
    4.4 三个或更多数据源如何配置
  9. 新建类似于MutiDataSourceProperties这样的类,用于接收第三个数据源
  10. 在MybatisPlusConfig类中,配置类似于如下代码的方法
  11. 在DynamicDataSource配置中,增加第二步新加的数据源
  12. 同时在DatasourceEnum类中,增加第三个数据源名称
  13. 使用方法同第二个数据源使用方法相同
    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,所以需要把相关依赖的注释打开
  14. 2. org.springframework.session
  15. <artifactId>spring-session</artifactId> 4.     <scope>compile</scope><!-- 需要分布式session的话需要改为compile --> 5. </dependency>
    

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 2. spring.redis.port=xxx 3. spring.redis.password=xxx
    4.需要把SpringSessionConfig类中的注释打开
  3. @EnableRedisHttpSession(maxInactiveIntervalInSeconds = 1800)
    5.如需配置session失效时间,请在SpringSessionConfig类中修改 maxInactiveIntervalInSeconds属性值
    4.8 使用Redis
    默认Guns在部署分布式的环境中使用了Redis作为分布式session的存储,如果想在项目中用 redis做缓存或者存储,建议使用RedisTemplate来进行操作
    1.首先下载Redis,可以在Guns的qq群里找到redis的可执行包,或者去redis官网下载 2.在项目的config包下建立redis的配置类
  4. /** 2. * Redis配置 3. * 4. * @author fengshuonan 5. * @date 2018年1月28日 11:49:12 6. */ 7. @Configuration 8. @EnableCaching
  5. public class RedisConfig { 10. 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. 18. @Bean 19. public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) { 20. RedisTemplate<String, Object> template = new RedisTemplate<>(); 21. template.setConnectionFactory(factory); 22. template.afterPropertiesSet(); 23. return template; 24. } 25. }
    3.在application.yml中配置redis的连接属性

4.在GunsApplication类中,编写CommandLineRunner来测试一下Redis的连接

  1. @Bean 2. CommandLineRunner commandLineRunner() { 3. return new CommandLineRunner() { 4. @Override 5. public void run(String… strings) throws Exception { 6. BoundValueOperations<String, Object> test = redisTemplate.b oundValueOps(“test”); 7. test.set(“test value”); 8. 9. Object o = test.get(); 10. System.out.println(o);
  2.     } 12.     }; 13. }
    

4.9 XSS过滤器
4.9.1 介绍 为了抵御XSS攻击,不让用户在录入数据的同时插入恶意js代码,Guns对所有传入数据中带 有html标签 和

其中性别,角色,部门,状态都是数值或者id类型,我们需要把他们包装成文字形式返回给前 端
1.首先建立UserWarpper类继承BaseControllerWarpper类

  1. /** 2. * 用户管理的包装类 3. * 4. * @author fengshuonan 5. * @date 2017年2月13日 下午10:47:03 6. */ 7. public class UserWarpper extends BaseControllerWarpper { 8. 9. public UserWarpper(List<Map<String, Object>> list) { 10. super(list); 11. } 12. 13. @Override 14. public void warpTheMap(Map<String, Object> map) { 15. map.put(“sexName”, ConstantFactory.me().getSexName((Integer) ma p.get(“sex”))); 16. map.put(“roleName”, ConstantFactory.me().getRoleName((String) m ap.get(“roleid”))); 17. map.put(“deptName”, ConstantFactory.me().getDeptName((Integer) map.get(“deptid”))); 18. map.put(“statusName”, ConstantFactory.me().getStatusName((Integ er) map.get(“status”))); 19. } 20. }
    通过查看BaseControllerWarpper类可了解到被包装的参数必须为Map或者List类型
  2. /** 2. * 控制器查询结果的包装类基类
        • @author fengshuonan 5. * @date 2017年2月13日 下午10:49:36 6. */ 7. public abstract class BaseControllerWarpper { 8. 9. public Object obj = null; 10. 11. public BaseControllerWarpper(Object obj) { 12. this.obj = obj; 13. } 14. 15. @SuppressWarnings(“unchecked”) 16. public Object warp() { 17. if (this.obj instanceof List) { 18. List<Map<String, Object>> list = (List<Map<String, Object>> ) this.obj; 19. for (Map<String, Object> map : list) { 20. warpTheMap(map); 21. } 22. return list; 23. } else if (this.obj instanceof Map) { 24. Map<String, Object> map = (Map<String, Object>) this.obj; 25. warpTheMap(map); 26. return map; 27. } else { 28. return this.obj; 29. } 30. } 31. 32. protected abstract void warpTheMap(Map<String, Object> map); 33. }
          我们继承BaseControllerWarpper类主要是为了实现warpTheMap()方法,也就是具体的包 装过程,warpTheMap()方法的参数map就是被包装的原始数据的每个条目,我们可以在这每 个条目中增加一些字段也就是被包装字段的中文名称,如下
          5.3.2 ConstantFactory 在包装过程中,我们经常会用到ConstantFactory这个类,这个类是连接数据库和包装类的
          桥梁,我们可以在ConstantFactory中封装一些编辑的查询方法,这些方法通常会被多个包 装类多次调用,并且在调用这些方法的时候ConstantFactory.me()的形式静态调用,可以快 速的 包装一些状态和id,非常方便,在ConstantFactory中我们可以利用spring cache 的@Cacheable注解来缓存一些数据,把这些频繁的查询缓存起来
          5.4 前端思想
          Guns前端采用了beetl模板引擎,beetl包含语法简洁,速度快,文档全,社区活跃等众多优 点,所有的beetl语法都以 @开头
          5.4.1 布局 在用户登录页面后进入的是index.html页面,这个页面加载了整个后台管理系统的框架,我 们可以看到index.html源代码中把整个页面分为了三部分,左侧菜单栏,右侧页面和右侧主 题栏部分,其实就是利用beetl的 @include把整个大的复杂的页面细化了,这样好维护
          左侧菜单和右侧主题栏部分在用户登录后会一直不变,除非刷新浏览器页面,动态变化的是页 面右侧这部分,我们打开6个标签页,并打开浏览器F12调试
          新建和切换标签,页面的地址不会变化,变化的是页面右侧的iframe这部分
          下面我们分析一下右侧页面的组成,我们打开菜单管理页面,查看他的代码
  3. @layout("/common/_container.html"){ 2.
    3. XXXX等html代码… 4.
    5. 6. @}
    整个页面被@layout所包围,@layout是beetl的引用布局(具体用法文档可以查看beetl的官 方文档),Guns中内置了/common/_container.html这样一个布局,可以 把/common/_container.html理解为一个html的抽象封装,我们每个页面都继承自这个模 板,默认包含了一系列通用的js css引用等,这样写即简化了我们的开发和维护,又使我们的 代码简洁有序,在/common/_container.html中的 l a y o u t C o n t e n t 就 代 表 我 们 每 个 页 面 不 同 的 h t m l 5.4.2 标 签 为 了 把 一 些 重 复 性 的 h t m l 封 装 起 来 , 我 们 使 用 了 b e e t l 的 标 签 , 这 些 标 签 的 本 质 是 把 重 复 性 的 h t m l 代 码 用 一 行 h t m l 标 签 替 代 , 从 而 方 便 使 用 , 易 于 维 护 , 这 些 标 签 都 放 在 c o m m o n / t a g s 这 个 文 件 夹 标 签 中 的 一 些 属 性 例 如 {layoutContent}就代表我们每个页面 不同的html 5.4.2 标签 为了把一些重复性的html封装起来,我们使用了beetl的标签,这些标签 的 本质是把重复性的html代码用一行html标签替代 ,从而方便使用,易于维护,这些标签都放 在common/tags这个文件夹 标签中的一些属性例如 layoutContenthtml5.4.2html使beetlhtmlhtml便使common/tags{name} ${id}等属性均为掉钱被调用时,从调用体的属性传 来<#xxxTag name=“xxx” id=“xxx”>
  4. 常见问题答疑
    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过程中,可能会出现
  5. [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分开了,生成代 码的时候不影响。(此项仅供参考)
  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Guns 文档高级版 v20191027 1. 序言1.1 文档简介 1.2 最新Guns&Roses架构视频 1.3 版本介绍 2. 使用手册 2.1 下载项目 2.1.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.2.3 添加菜单与分配权限 3.2.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 spring security + jwt鉴权 3.7.1 放开权限过滤 3.7.2 获取当前登录用户 3.7.3 会话管理 3.8 日志记录 3.8.1 业务日志 3.8.2 异常日志 3.9 如何使用缓存 3.10 使用枚举 3.11 spring boot热部署 3.11.1 重新加载html 3.11.2 重新加载java类 3.12 发送邮件 3.13 发送短信 4. 高级使用手册 4.1 修改项目名和包名 4.2 在线参数配置 4.3 模板位置的变更 4.4 顶部广告的关闭 4.5 添加登录验证码 4.6 spring profile 4.7 多机器部署注意事项 4.8 XSS过滤器 4.8.1 介绍 4.8.2 原理 4.8.3 放过过滤 4.9 多租户功能 4.9.1 介绍 4.9.2 打开租户开关 4.9.3 新建租户 4.9.4 使用租户账户登录 4.9.5 基本原理 4.10 多语言 4.10.1 介绍 4.10.2 录入字典条目 4.10.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层
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值