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沒有更新,須要從新在倉庫中下載。