SpringSecurity权限管理系统实战—1、项目简介和开发环境准备
SpringSecurity权限管理系统实战—2、日志、接口文档等实现
SpringSecurity权限管理系统实战—3、主要页面及接口实现
SpringSecurity权限管理系统实战—4、整合SpringSecurity(上)
SpringSecurity权限管理系统实战—5、整合SpringSecurity(下)
SpringSecurity权限管理系统实战—6、SpringSecurity整合jwt
SpringSecurity权限管理系统实战—7、处理一些问题
SpringSecurity权限管理系统实战—8、AOP 记录用户日志、异常日志javascript
后端五分钟,前端半小时。。css
每次写js都头疼。html
本身写前端是不可能的,这辈子不可能本身写前端的,只能找找别人的模板才能维持的了生存这样子。github,gitee上的模板又多,帮助文档又详细,我超喜欢这两个平台的。前端
(下一节进入springsecurity)java
咱们稍微分析一下数据表,只有菜单页面的增删改查几乎是没有涉及多个表的,因此咱们最早从菜单页面的逻辑开始写。node
在templates/system目录下新建menu文件夹,将PearAdmin自带的power.html移动到menu下,修改一下路由。jquery
页面最终效果是这样的git
layui的table数据表格的用法能够在layui官网上找到示例,我这里对于前端部分就不详细解释了,由于前端我也不咋会,都是根据别人的代码改了改。github
我直接贴上完整的power.html完整代码ajax
<!DOCTYPE html> <html lang="en" xmlns:th="http://www.thymeleaf.org"> <head> <meta charset="UTF-8"> <title>Title</title> <link rel="stylesheet" th:href="@{/PearAdmin/component/layui/css/layui.css}" /> <link rel="stylesheet" th:href="@{/PearAdmin/admin/css/pearCommon.css}"/> </head> <body class="pear-container"> <div class="layui-card"> <div class="layui-card-body"> <form class="layui-form" action=""> <div class="layui-form-item"> <label class="layui-form-label">菜单标题</label> <div class="layui-input-inline"> <input type="text" name="name" placeholder="请输入菜单标题" class="layui-input"> </div> <label class="layui-form-label">类型</label> <div class="layui-input-inline"> <select name="type"> <option value=""></option> <option value="1">菜单</option> <option value="2">按钮</option> </select> </div> <button class="pear-btn pear-btn-md pear-btn-primary" lay-submit lay-filter="menu-query"> <i class="layui-icon layui-icon-search"></i> 查询 </button> <button type="reset" class="pear-btn pear-btn-md"> <i class="layui-icon layui-icon-refresh"></i> 重置 </button> </div> </form> </div> </div> <div class="layui-card"> <div class="layui-card-body"> <table id="power-table" lay-filter="power-table"></table> </div> </div> <script type="text/html" id="power-toolbar"> <button class="pear-btn pear-btn-primary pear-btn-md" lay-event="add"> <i class="layui-icon layui-icon-add-1"></i> 新增 </button> <button class="pear-btn pear-btn-danger pear-btn-md" lay-event="batchRemove"> <i class="layui-icon layui-icon-delete"></i> 删除 </button> </script> <script type="text/html" id="power-bar"> <button class="pear-btn pear-btn-primary pear-btn-sm" lay-event="edit"><i class="layui-icon layui-icon-edit"></i></button> <button class="pear-btn pear-btn-danger pear-btn-sm" lay-event="remove"><i class="layui-icon layui-icon-delete"></i></button> </script> <script type="text/html" id="power-type"> {{#if (d.type == '1') { }} <span>菜单</span> {{# }else if(d.type == '2'){ }} <span>按钮</span> {{# } }} </script> <script type="text/html" id="power-status"> <input type="checkbox" name="status" value="{{d.id}}" lay-skin="switch" lay-text="启用|禁用" lay-filter="user-status" checked = "{{ d.id == 10003 ? 'true' : 'false' }}"> </script> <script type="text/html" id="icon"> <i class="layui-icon {{d.icon}}"></i> </script> <script th:src="@{/PearAdmin/component/layui/layui.js}" charset="utf-8"></script> <script> layui.use(['table','form','jquery','treetable'],function () { let table = layui.table; let form = layui.form; let $ = layui.jquery; let treetable = layui.treetable; let MODULE_PATH = "operate/"; window.render = function(){ treetable.render({ treeColIndex: 1, treeSpid: 0, treeIdName: 'powerId', treePidName: 'parentId', skin:'line', method:'post', treeDefaultClose: true, toolbar:'#power-toolbar', elem: '#power-table', url: '/api/menu', page: false, cols: [ [ {type: 'checkbox'}, {field: 'name', minWidth: 200, title: '菜单标题'}, {field: 'icon', title: '图标',templet:'#icon'}, {field: 'type', title: '类型',templet:'#power-type'}, {field: 'url', title: '路径'}, {field: 'status', title: '是否可用',templet:'#power-status'}, {field: 'permission', title: '权限标识'}, {field: 'sort', title: '排序'}, {field: 'createTime', title: '建立日期'}, {title: '操做',templet: '#power-bar', width: 150, align: 'center'} ] ] }); } render(); table.on('tool(power-table)',function(obj){ if (obj.event === 'remove') { window.remove(obj); } else if (obj.event === 'edit') { window.edit(obj); } }) table.on('toolbar(power-table)', function(obj){ if(obj.event === 'add'){ window.add(); } else if(obj.event === 'refresh'){ window.refresh(); } else if(obj.event === 'batchRemove'){ window.batchRemove(obj); } }); form.on('submit(menu-query)', function(data){ //模糊查询方法 var formData = data.field; var name = formData.name; var type = formData.type; table.reload(('power-table'),{ // table重载 where: {//这里传参 向后台 queryName: name, queryType: type //可传多个参数到后台... ,分隔 } , url: '/api/menu'//后台作模糊搜索接口路径 , method: 'get' }); return false; }); window.add = function(){ layer.open({ type: 2, title: '新增', shade: 0.1, area: ['450px', '500px'], content: '/api/menu/add' }); } window.edit = function(obj){ var data = obj.data; layer.open({ type: 2, title: '修改', shade: 0.1, area: ['450px', '500px'], content: '/api/menu/edit/?id='+data.id }); } window.remove = function(obj){ var data = obj.data; layer.confirm('肯定删除吗,若是存在下级节点则一并删除,此操做不能撤销!', {icon: 3, title:'提示'}, function(index){ layer.close(index); let loading = layer.load(); $.ajax({ url: "/api/menu/?id=" + data.id, dataType:'json', type:'delete', success:function(result){ layer.close(loading); if(result.success){ layer.msg(result.msg,{icon:1,time:1000},function(){ obj.del(); }); }else{ layer.msg(result.msg,{icon:2,time:1000}); } } }) }); } }) </script> </body> </html>
那么首先咱们要给的是table的数据,由于考虑到有一个模糊查询返回的数据格式是同样,因此能够合在一块儿写。
MenuDao新建方法
/** * * @param queryName 查询的表题 * @param queryType 查询类型 * @return */ List<MyMenu> getFuzzyMenu(String queryName,Integer queryType);
由于以前在yml中已经配置了mapper.xml的路径是在classpath:/mybatis-mappers/下,因此在resources目录下新建mybatis-mappers文件夹,在其中新建MenuMapper.xml文件。
若是你们不想写一些简单的sql语句,推荐你们使用MybatisPlus或者JPA。MybatisPlus可能还要写一些多表的sql语句,JPA几乎见不到SQL。
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <mapper namespace="com.codermy.myspringsecurityplus.dao.MenuDao"> <select id="getFuzzyMenu" resultType="com.codermy.myspringsecurityplus.entity.MyMenu"> -- 建议你们在写查询语句的时候不要写select * ,能够经过这篇文章了解(https://blog.csdn.net/qq_36101933/article/details/93973266) select t.id,t.parent_id,t.name,t.icon,t.url,t.permission,t.sort,t.type,t.create_time,t.update_time from my_menu t <where> <if test="queryName != null and queryName != ''"> AND t.name like CONCAT('%', #{queryName}, '%') </if> <if test="queryType != null and queryType != ''"> AND t.type = #{queryType} </if> </where> order by t.sort </select> </mapper>
这里再给你们安利一款idea的插件Free Mybatis plugin,它的做用就是能够快速经过xml找到mapper,或者mapper找到xml。效果以下图
点击箭头就能快速定位到相应方法,很是好用。
而后就是service,impl,controller
/** * @author codermy * @createTime 2020/7/10 */ public interface MenuService { List<MyMenu> getMenuAll(String queryName,Integer queryType); }
@Service//别忘了注解 public class MenuServiceImpl implements MenuService { @Autowired private MenuDao menuDao; @Override public List<MyMenu> getMenuAll(String queryName,Integer queryType) { return menuDao.getFuzzyMenu(queryName,queryType); } }
@Controller @RequestMapping("/api/menu") @Api(tags = "系统:菜单管理") public class MenuController { @Autowired private MenuService menuService; @GetMapping @ResponseBody @ApiOperation(value = "菜单列表") public Result getMenuAll(String queryName,Integer queryType){//这里没选择接收json字符串,前端传参经过/api/menu?queryName=测试的方式 return Result.ok().data(menuService.getMenuAll(queryName,queryType)).code(ResultCode.TABLE_SUCCESS); } }
前端代码我已经给出来了,重启项目,打开就是那个效果。
这里稍微提一下RestFul风格
GET /blog
(获取全部博客)POST /blog
(新建博客)PUT /blog/12
(更新id为 12 的博客)DELETE /blog/12
(删除id为 12 的博客)还有就是不要相似getAllBlog这种,冗余没有意义,形式不固定,不一样的开发者还须要了解文档才能调用。
详细看这篇文章
查已经完成了(模糊查询一样是这个接口,在前端页面逻辑已经写好了,里面给了注释),接下来就是增删改了。
MenuDao中添加以下方法
@Select("select t.id,t.parent_id,t.name,t.icon,t.url,t.permission,t.sort,t.type,t.create_time,t.update_time from my_menu t where t.id = #{id}") MyMenu getMenuById(Integer id); int update(MyMenu menu); @Options(useGeneratedKeys = true, keyProperty = "id") @Insert("insert into my_menu(parent_id, name, icon, url, permission, sort, type, create_time, update_time)values(#{parentId}, #{name}, #{icon}, #{url}, #{permission}, #{sort}, #{type}, now(), now())") int save(MyMenu menu); @Delete("delete from my_menu where id = #{id}") int deleteById(Integer id); @Delete("delete from my_menu where parent_id = #{parentId}") int deleteByParentId(Integer parentId);
MenuMapper.xml中添加
<update id="update"> update my_menu t <set> <if test="parentId != null"> parent_id = #{parentId}, </if> <if test="name != null"> `name` = #{name}, </if> <if test="icon != null"> `icon` = #{icon}, </if> <if test="url != null"> url = #{url}, </if> <if test="permission != null"> permission = #{permission}, </if> <if test="sort != null"> sort = #{sort}, </if> <if test="type != null"> type = #{type}, </if> update_time = #{updateTime} </set> where t.id = #{id} </update>
MapperService
MyMenu getMenuById(Integer id) Result updateMenu(MyMenu menu); Result<MyMenu> save(MyMenu menu); Result delete(Integer id);
MapperServiceImpl
@Override public MyMenu getMenuById(Integer id) { return menuDao.getMenuById(id); } @Override public Result updateMenu(MyMenu menu) { return (menuDao.update(menu) > 0) ? Result.ok().message("修改为功") : Result.error().message("修改失败"); } @Override public Result<MyMenu> save(MyMenu menu) { return (menuDao.save(menu) > 0) ? Result.ok().message("添加成功") : Result.error().message("添加失败"); } //若是这里删除了菜单树的父节点,把它的子节点一并删除 @Override public Result delete(Integer id) { menuDao.deleteById(id); menuDao.deleteByParentId(id); return Result.ok().message("删除成功"); }
个人后端逻辑写的不是很完善,好比插入时菜单名是否为空等等,只是在前端写了一些。这样普通用户用是没有什么问题,可是有些别有用心的人直接用你的接口,就会疯狂报错,形成服务器压力。
MenuController中添加
@GetMapping(value = "/edit") @ApiOperation(value = "跳转修改菜单页面") public String editPermission(Model model, MyMenu myMenu) { model.addAttribute("myMenu",menuService.getMenuById(myMenu.getId())); return "system/menu/menu-edit"; } @PutMapping @ResponseBody @ApiOperation(value = "修改菜单") public Result updateMenu(@RequestBody MyMenu menu) { return menuService.updateMenu(menu); } @GetMapping(value = "/add") @ApiOperation(value = "跳转添加菜单页面") public String addMenu(Model model) { model.addAttribute("myMenu",new MyMenu()); return "system/menu/menu-add"; } @PostMapping @ResponseBody @ApiOperation(value = "添加菜单") public Result<MyMenu> savePermission(@RequestBody MyMenu myMenu) { return menuService.save(myMenu); } //todo 批量删除 @DeleteMapping @ResponseBody @ApiOperation(value = "删除菜单") public Result deleteMenu(Integer id) { return menuService.delete(id); }
那么不难发现咱们还须要两个页面,分别是menu-add.html
和menu-edit.html
。
在对应位置建立,我直接给代码
menu-add
<!DOCTYPE html> <html lang="en" xmlns:th="http://www.thymeleaf.org"> <head> <meta charset="UTF-8"> <title>Title</title> <link rel="stylesheet" th:href="@{/PearAdmin/component/layui/css/layui.css}" /> <link rel="stylesheet" th:href="@{/PearAdmin/admin/css/pearCommon.css}"/> <link rel="stylesheet" th:href="@{/PearAdmin/admin/css/pear-tree/dtree.css}" /> <link rel="stylesheet" th:href="@{/PearAdmin/admin/css/pear-tree/font/dtreefont.css}"/> </head> <body> <form class="layui-form" action=""> <div class="mainBox"> <div class="main-container"> <div class="main-container"> <input type="text" id="id" th:value="${myMenu.id}" name="id" style="display:none;" autocomplete="off" class="layui-input"> <div class="layui-form-item"> <label class="layui-form-label"> <span style="color: red">*</span>菜单名 </label> <div class="layui-input-block"> <input type="text" th:value="${myMenu.name}" name="name" lay-verify="name" autocomplete="off" placeholder="请输入菜单名" class="layui-input"> </div> </div> <div class="layui-form-item"> <label class="layui-form-label">图标</label> <div class="layui-input-block"> <input type="text" id="iconPicker" name="icon" class="hide" th:value="${myMenu.icon}"> </div> </div> <div class="layui-form-item"> <label class="layui-form-label">路径</label> <div class="layui-input-block"> <input type="text" name="url" th:value="${myMenu.url}" autocomplete="off" class="layui-input"> </div> </div> <div class="layui-form-item"> <label class="layui-form-label">权限标识</label> <div class="layui-input-block"> <input type="text" name="permission" th:value="${myMenu.permission}" autocomplete="off" class="layui-input"> </div> </div> <div class="layui-form-item"> <label class="layui-form-label"> <span style="color: red">*</span>排序 </label> <div class="layui-input-block"> <input type="text" name="sort" th:value="${myMenu.sort}" lay-verify="sort" autocomplete="off" placeholder="请输入排序值" class="layui-input"> </div> </div> <div class="layui-form-item"> <label class="layui-form-label">类型</label> <div class="layui-input-block"> <input type="radio" name="type" value="1" title="菜单" th:checked="${myMenu.type == 1}? 'true':'false'"> <input type="radio" name="type" value="2" title="按钮" th:checked="${myMenu.type == 2}? 'true':'false'"> </div> </div> <div class="layui-form-item"> <label class="layui-form-label"> 上级菜单 </label> <div class="layui-input-block"> <input type="number" id="parentId" th:value="${myMenu.parentId}" name="parentId" lay-verify="parentId" style="display:none;width: 0px" autocomplete="off" class="layui-input"> <ul id="dataTree" class="dtree" data-id="0" th:data-value="${myMenu.parentId}"></ul> </div> </div> </div> </div> </div> <div class="bottom"> <div class="button-container"> <button type="submit" class="layui-btn layui-btn-normal layui-btn-sm" lay-submit="" lay-filter="user-save"> <i class="layui-icon layui-icon-ok"></i> 提交 </button> <button type="reset" class="layui-btn layui-btn-primary layui-btn-sm"> <i class="layui-icon layui-icon-refresh"></i> 重置 </button> </div> </div> </form> <script th:src="@{/PearAdmin/component/layui/layui.js}" charset="utf-8"></script> <script> layui.use(['iconPicker','dtree','form','jquery'],function(){ let form = layui.form; let $ = layui.jquery; let dtree = layui.dtree; let formDate = null; var iconPicker = layui.iconPicker; // 初始化树 dtree.render({ elem: "#dataTree", initLevel: "1", width: "100%", method: 'get', dataStyle: "layuiStyle", //使用layui风格的数据格式 response:{message:"msg",statusCode:200}, //修改response中返回数据的定义 url: "/api/menu/build", dataFormat: "list", //配置data的风格为list select: true, //指定下拉树模式 selectTips: "不选默认是顶级目录", selectCardHeight: "150" }); iconPicker.render({ // 选择器,推荐使用input elem: '#iconPicker', // 数据类型:fontClass/unicode,推荐使用fontClass type: 'fontClass', // 是否开启搜索:true/false,默认true search: true, // 是否开启分页:true/false,默认true page: true, // 每页显示数量,默认12 limit: 16, // 点击回调 click: function (data) { console.log(data); }, // 渲染成功后的回调 success: function(d) { console.log(d); } }); var param = dtree.getNowParam("dataTree"); formDate = $("#parentId"); dtree.on("node('dataTree')" ,function(obj){ var param = dtree.getNowParam("dataTree"); $("#parentId").val(param.nodeId); formDate = $("#parentId"); }); form.verify({ name: function(value){ if(value.length < 2){ return '菜单名至少2个字符'; } }, sort: [ /^[1-9]\d*$/ ,'只能是整数哦' ] }); form.on('submit(user-save)', function(data){ var permissionId = formDate; var bs = data.field.parentId data.field.parentId = Number (bs) var json = JSON.stringify(data.field) $.ajax({ url:'/api/menu', data:json, dataType:'json', contentType:'application/json', type:'post', success:function(result){ if(result.success){ layer.msg(result.msg,{icon:1,time:1000},function(){ parent.layer.close(parent.layer.getFrameIndex(window.name));//关闭当前页 parent.location.reload(); }); }else{ layer.msg(result.msg,{icon:2,time:1000}); } } }) return false; }); }) </script> </body> </html>
menu-edit
<!DOCTYPE html> <html xmlns:th="http://www.thymeleaf.org"> <head> <meta charset="UTF-8"> <title>Title</title> <link rel="stylesheet" th:href="@{/PearAdmin/component/layui/css/layui.css}" /> <link rel="stylesheet" th:href="@{/PearAdmin/admin/css/pearCommon.css}"/> <link rel="stylesheet" th:href="@{/PearAdmin/admin/css/pear-tree/dtree.css}" /> <link rel="stylesheet" th:href="@{/PearAdmin/admin/css/pear-tree/font/dtreefont.css}"/> </head> <body> <form class="layui-form" action=""> <div class="mainBox"> <div class="main-container"> <div class="main-container"> <input type="text" id="id" th:value="${myMenu.id}" name="id" style="display:none;" autocomplete="off" class="layui-input"> <div class="layui-form-item"> <label class="layui-form-label"> <span style="color: red">*</span>菜单名 </label> <div class="layui-input-block"> <input type="text" th:value="${myMenu.name}" name="name" lay-verify="name" autocomplete="off" placeholder="请输入菜单名" class="layui-input"> </div> </div> <div class="layui-form-item"> <label class="layui-form-label">图标</label> <div class="layui-input-block"> <input type="text" id="iconPicker" name="icon" class="hide" th:value="${myMenu.icon}"> </div> </div> <div class="layui-form-item"> <label class="layui-form-label">路径</label> <div class="layui-input-block"> <input type="text" name="url" th:value="${myMenu.url}" autocomplete="off" class="layui-input"> </div> </div> <div class="layui-form-item"> <label class="layui-form-label">权限标识</label> <div class="layui-input-block"> <input type="text" name="permission" th:value="${myMenu.permission}" autocomplete="off" class="layui-input"> </div> </div> <div class="layui-form-item"> <label class="layui-form-label"> <span style="color: red">*</span>排序 </label> <div class="layui-input-block"> <input type="text" name="sort" th:value="${myMenu.sort}" lay-verify="sort" autocomplete="off" placeholder="请输入排序值" class="layui-input"> </div> </div> <div class="layui-form-item"> <label class="layui-form-label">类型</label> <div class="layui-input-block"> <input type="radio" name="type" value="1" title="菜单" th:checked="${myMenu.type == 1}? 'true':'false'"> <input type="radio" name="type" value="2" title="按钮" th:checked="${myMenu.type == 2}? 'true':'false'"> </div> </div> <div class="layui-form-item"> <label class="layui-form-label"> 上级菜单 </label> <div class="layui-input-block"> <input type="number" id="parentId" th:value="${myMenu.parentId}" name="parentId" lay-verify="parentId" style="display:none;width: 0px" autocomplete="off" class="layui-input"> <ul id="dataTree" class="dtree" data-id="0" th:data-value="${myMenu.parentId}"></ul> </div> </div> </div> </div> </div> <div class="bottom"> <div class="button-container"> <button type="submit" class="layui-btn layui-btn-normal layui-btn-sm" lay-submit="" lay-filter="user-save"> <i class="layui-icon layui-icon-ok"></i> 提交 </button> <button type="reset" class="layui-btn layui-btn-primary layui-btn-sm"> <i class="layui-icon layui-icon-refresh"></i> 重置 </button> </div> </div> </form> <script th:src="@{/PearAdmin/component/layui/layui.js}" charset="utf-8"></script> <script type="text/javascript"> layui.use(['iconPicker','dtree','form','jquery'],function(){ let form = layui.form; let $ = layui.jquery; let dtree = layui.dtree; var iconPicker = layui.iconPicker; // 初始化树 dtree.render({ elem: "#dataTree", initLevel: "1", width: "100%", method: 'get', dataStyle: "layuiStyle", //使用layui风格的数据格式 response:{message:"msg",statusCode:200}, //修改response中返回数据的定义 url: "/api/menu/build", dataFormat: "list", //配置data的风格为list select: true, //指定下拉树模式 selectTips: "不选默认是顶级目录", selectCardHeight: "200", }); iconPicker.render({ // 选择器,推荐使用input elem: '#iconPicker', // 数据类型:fontClass/unicode,推荐使用fontClass type: 'fontClass', // 是否开启搜索:true/false,默认true search: true, // 是否开启分页:true/false,默认true page: true, // 每页显示数量,默认12 limit: 12, // 点击回调 click: function (data) { console.log(data); }, // 渲染成功后的回调 success: function(d) { console.log(d); } }); form.verify({ name: function(value){ if(value.length < 2){ return '菜单名至少2个字符'; } }, sort: [ /^[1-9]\d*$/ ,'只能是整数哦' ] }); form.on('submit(user-save)', function(data){ $.ajax({ url:'/api/menu', data:JSON.stringify(data.field), dataType:'json', contentType:'application/json', type:'put', success:function(result){ if(result.success){ layer.msg(result.msg,{icon:1,time:1000},function(){ parent.layer.close(parent.layer.getFrameIndex(window.name));//关闭当前页 parent.location.reload();//刷新页面 }); }else{ layer.msg(result.msg,{icon:2,time:1000}); } } }) return false; }); }) </script> <script type="text/javascript"> </script> </body> </html>
重启项目,访问一下
这里的修改是经过model传来的数据,.经过getMenuById方法返回数据存入model,经过Thymeleaf模板引擎放入指定位置。这里批量删除的功能还没有实现,有兴趣的同窗能够本身实现。
这样咱们这个页面基本就完成了,接下来的页面基本都是一个套路。我就不贴所有的代码了,挑其中部分来讲说,所有的代码能够在gitee和github中获取,我已经按照每篇文章的进度添加tag,若是哪一个部分没出来的同窗能够直接下载哪一个部分.。
这个部分主要是有个菜单树,PearAdmin是选用的dtree来实现的。详细用法请看官网 (我认为很全面了,基本的用法都能找到示例)
主要就是这个菜单树的数据怎么传,在dtree官网上能够看到开启复选框须要json中有个checkArr值,为0是未选中,1是选中。
那么咱们新建一个MenuDto,来封装一下咱们须要的参数
@Data public class MenuDto implements Serializable { private Integer id; private Integer parentId; private String checkArr = "0"; private String title; }
在MenuDao中添加以下方法
@Select("select t.id,t.parent_id,t.name,t.icon,t.url,t.permission,t.sort,t.type,t.create_time,t.update_time from my_menu t where t.id = #{id}") MyMenu getMenuById(Integer id); @Select("select p.id,p.parent_id,p.name from my_menu p inner join my_role_menu rp on p.id = rp.menu_id where rp.role_id = #{roleId}") @Result(property = "title",column = "name") List<MenuDto> listByRoleId(Integer roleId);
MenuServiceImpl中
@Override public List<MenuDto> buildMenuAllByRoleId(Integer roleId) { List<MenuDto> listByRoleId = menuDao.listByRoleId(roleId); List<MenuDto> permissionDtos = menuDao.buildAll(); List<MenuDto> tree = TreeUtil.tree(listByRoleId, permissionDtos); return tree; }
这里我写了一个TreeUtil工具类
public class TreeUtil { //todo 判断list是否为空 /** * * @param listByRoleId 经过角色id查询的menuid * @param menuDtos 返回的menutree * @return */ public static List<MenuDto> tree(List<MenuDto> listByRoleId, List<MenuDto> menuDtos ){ List<Integer> collect = listByRoleId.stream().map(MenuDto::getId).collect(Collectors.toList()); List<Integer> collect1 = menuDtos.stream().map(MenuDto::getId).collect(Collectors.toList()); for (Integer item : collect) {// 遍历list2 if (collect1.contains(item)) {// 若是存在这个数 MenuDto menuDto = new MenuDto(); menuDto = menuDtos.get(item-1); menuDto.setCheckArr("1"); menuDtos.set(item-1,menuDto); } } return menuDtos; } }
这个工具类的做用就是经过角色id查询这个角色所拥有的菜单id,而后再查出全部的菜单id,把他们比较,若是这其中有重复的菜单id,就把这个id对应的MenuDto对象里的checkArr换成1。我这个方法可能会有点绕,若是有小伙伴有更好的方法,欢迎留言告诉我。
而后这个页面的有须要注意的部分,就是再删除角色时,要先查询是否已经有用户是这个角色了,若是有就不能删除
这里无非也就是一些增删改查,要写的完善点的话也就是新增用户时手机号是否能相同等等。我这里新增用户时,会给他一个默认的密码123456
@PostMapping @ResponseBody @ApiOperation(value = "添加用户") public Result<MyUser> saveUser(@RequestBody UserDto userDto){ MyUser myUser = null; myUser = userService.getUserByPhone(userDto.getPhone()); if(myUser !=null && !(myUser.getId().equals(userDto.getId())) ){ return Result.error().code(20001).message("手机号已存在"); } userDto.setPassword(MD5.crypt("123456")); return userService.save(userDto,userDto.getRoleId()); }
目前用的时MD5的加密,可是这种密码仅仅是加密了,相对而言会安全一些,可是若是两个用户的密码是同样的那么他们加密后的密码也是同样的。那么这其实也有办法解决,就是给密码加盐,加盐就是给密码再加一个值,这样即便不一样用户的相同的密码在加密后也会不一样。详细解释。以后会基于SpringSecurity的BCryptPasswordEncoder()方法进行加密,此方法自带盐。
那么这个部分的代码就完成了,下一章正式进入SpringSecurity部分。
若是有同窗不想写前面部分,能够直接在gitee和github中下载v1.03的tag,里面是到本篇文章结束的全部代码。
注意: 里面的是sql没有更新,须要从新在仓库中下载。