springboot+easyui+jpa实现动态权限角色的后台管理系统
Posted lukizzz
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了springboot+easyui+jpa实现动态权限角色的后台管理系统相关的知识,希望对你有一定的参考价值。
最近因为多次需要使用easyui的后台管理系统,所以自己写了一个easyui后台管理系统的模版,可修改权限增加角色
(这里先放创建数据库和加载菜单,配置拦截器的方法和遇到的问题)
1.先创建数据库(我是在本地创建的数据库)
资源表:存放菜单资源
角色表:存放角色数据
角色和资源的中间表:关联角色表和资源表
用户表:存放用户数据
角色和用户中间表:关联角色和用户
2.创建完数据库和数据表后在项目中创建相应的实体类,控制层,服务层和数据访问层
3.写前端的login.ftl和index.ftl
login.ftl就不放了,放上index.ftl,在加载页面的时候调用js打开一级菜单,在一级菜单打开的时候获取二级菜单
<!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8"/> <title>ace Admin</title> <meta name="description" content=""/> <meta name="viewport" content="width=device-width, initial-scale=1.0"/> <#include "inc/inc.ftl"/> </head> <body> <div id="test"></div> <div class="easyui-layout" data-options="fit:true"> <div data-options="region:‘north‘" class="head_body" style="overflow:hidden;"> <div> <div class="head_left"></div> <div class="head_right"> <div class="huanying"> 欢迎 <span>${Session.userInfo.username}</span> 登陆 | 现在是<span id="show_date"></span> </div> <div class="anniu"> <div class="bb"> <a href="javascript:logout();" class="logout"></a> <a id="open_change_password" class="changePwd" href="javascript:editPassword();"></a> </div> <div class="bs"> <a class="styleswitch a1" style="CURSOR: pointer" title="黑灰色" rel="black"></a> <a class="styleswitch a2" style="CURSOR: pointer" title="天蓝色" rel="default"></a> <a class="styleswitch a3" style="CURSOR: pointer" title="灰色" rel="bootstrap"></a> <a class="styleswitch a4" style="CURSOR: pointer" title="浅灰色" rel="gray"></a> <a class="styleswitch a5" style="CURSOR: pointer" title="白色" rel="metro"></a> </div> </div> </div> </div> </div> <div data-options="region:‘west‘,split:true,title:‘导航菜单‘" style="width:188px;"> <div class="easyui-accordion" id="main-accordion" data-options="fit:true,border:false"> </div> </div> <div id="mainPanel" data-options="region:‘center‘"> <div id="index_tabs" class="easyui-tabs" data-options="fit:true,border:false,tabHeight:30" style="height:600px;"> <div title="Home"> <div style="padding:10px 0 10px 10px"> <h2>系统介绍</h2> <div class="light-info"> </div> </div> </div> </div> </div> </div> <div id="dlg" class="easyui-dialog" style="width:450px;height:auto;padding:10px 20px" buttons="#dlg-buttons" data-options="closed:true,modal:true"> <form id="fm" method="post" enctype="multipart/form-data"> <input type="hidden" id="userId" name="userId"/> <table class="grid"> <tr> <td>密码:</td> <td> <input type="text" name="password" class="easyui-passwordbox" iconWidth="28" data-options="required:true,validType:[‘length[0,20]‘]"/> </td> </tr> </table> </form> </div> <div id="dlg-buttons"> <a href="javascript:void(0)" class="easyui-linkbutton c8" data-options="iconCls:‘icon-ok‘" onclick="javascript:saveUser();" style="width:90px;">保存</a> <a href="javascript:void(0)" class="easyui-linkbutton c2" data-options="iconCls:‘icon-cancel‘" onclick="javascript:$(‘#dlg‘).dialog(‘close‘);" style="width:90px;">取消</a> </div> </body> </html> <script type="text/javascript"> $(document).ready(function () { // 设置tab高度,浏览器当前窗口可视区域高度 -头部高度 // $("#index_tabs").height($(window).height()-$("#navbar").height()); //显示系统时间 var myDate = new Date(); var week = [‘日‘, ‘一‘, ‘二‘, ‘三‘, ‘四‘, ‘五‘, ‘六‘]; var month = myDate.getMonth() + 1; var weekDay = " 星期" + week[myDate.getDay()]; var showDate = myDate.getFullYear() + "年" + month + "月" + myDate.getDate() + "日" + weekDay; $("#show_date").text(showDate); loadFirstMenu(); // loadSecondMenu(); // $(‘#main-accordion‘).accordion(‘getSelected‘).panel(‘collapse‘);//默认全部关闭 }); function loadFirstMenu() { $.ajax({ url: ‘${request.contextPath}/resource/first_menu?userId=‘ +${Session.userInfo.userId}, type: ‘get‘, success: function (data) { for (var i in data) {//不使用过滤 var mc = $(‘#main-accordion‘).accordion(‘add‘, { title: data[i].name, content: $.formatString(‘<ul id=‘{0}‘ class="easyui-tree menu_tree" style="padding:10px;"></ul>‘, "tree" + data[i].resourceId) }); $(‘#‘ + "tree" + data[i].resourceId).tree({ url: ‘${request.contextPath}/resource/second_menu?resourceId=‘ + data[i].resourceId + ‘&userId=‘ +${Session.userInfo.userId}, method: ‘get‘, parentField: ‘parentId‘, onSelect: function (node) { var start = node.path.indexOf("http"); var path = ‘${request.contextPath}‘ + node.path; if (start == 0) { var path = node.path; } addTab(node.text, path, node.iconCls); } }); } } }); } //初始化TABS组件 $(‘#index_tabs‘).tabs({ fit: true, border: false, tabHeight: 30, tools: [{ iconCls: ‘icon-reload‘, handler: function () { var currTab = $(‘#index_tabs‘).tabs(‘getSelected‘); var index = $(‘#index_tabs‘).tabs(‘getTabIndex‘, currTab); if (index != 0) { var updateUrl = $(currTab.panel(‘options‘).content).attr(‘src‘); $(‘#index_tabs‘).tabs(‘update‘, { tab: currTab, options: { content: refreshTab(updateUrl) } }); } } }, { iconCls: ‘icon-clear‘, handler: function () { $.messager.confirm(‘提示‘, ‘确定要全部关闭选项卡?‘, function (r) { if (r) { var tabTitle = new Array(); var tabs = $(‘#index_tabs‘).tabs("tabs"); var tCount = tabs.length; if (tCount > 0) { for (var i = 0; i < tCount; i++) { tabTitle.push(tabs[i].panel(‘options‘).title); } for (var i = 0; i < tabTitle.length; i++) { if (tabTitle[i] != ‘首页‘) { $(‘#index_tabs‘).tabs("close", tabTitle[i]); } } } } }); } }] }); function editPassword() { $(‘#dlg‘).dialog(‘open‘).dialog(‘setTitle‘, ‘修改‘); $(‘#userId‘).val(${Session.userInfo.userId}); } function saveUser() { $(‘#fm‘).form(‘submit‘, { url: "${request.contextPath}/sys/user_save", success: function (result) { var result = eval(‘(‘ + result + ‘)‘); if (result.code == 0) { $.messager.show({ title: ‘提示‘, msg: result.message }); $(‘#dlg‘).dialog(‘close‘); } } }); } </script>
4.将获取一级菜单和二级菜单的http请求发送到对应controller层
package com.lk.modeleasyui.controller; import com.lk.modeleasyui.beans.MenuDTO; import com.lk.modeleasyui.domain.SysResource; import com.lk.modeleasyui.service.SysResourceService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import java.util.List; /** * @Author: Lukizzz * @Date: 2018/9/11 11:39 * @Description: */ @RestController @RequestMapping(value = "/resource") public class SysResourceController { @Autowired private SysResourceService sysResourceService; @GetMapping(value = "/first_menu") public List<SysResource> getFirstMenu(Long userId) throws Exception { return sysResourceService.getFirstMenu(userId); } @GetMapping(value = "/second_menu") public List<MenuDTO> getSecondMenu(Long resourceId) throws Exception { return sysResourceService.getSecondMenu(resourceId); } }
注意使用restController的注解 不然前端无法接收到json
5.service:
package com.lk.modeleasyui.service; import com.lk.modeleasyui.beans.MenuDTO; import com.lk.modeleasyui.beans.TreeBean; import com.lk.modeleasyui.dao.SysResourceDAO; import com.lk.modeleasyui.dao.SysRoleResourceDAO; import com.lk.modeleasyui.dao.SysRoleUserDAO; import com.lk.modeleasyui.domain.SysResource; import com.lk.modeleasyui.domain.SysRoleResource; import com.lk.modeleasyui.domain.SysRoleUser; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import java.util.ArrayList; import java.util.List; /** * @Author: Lukizzz * @Date: 2018/9/11 11:51 * @Description: */ @Service public class SysResourceService { @Autowired private SysResourceDAO sysResourceDAO; @Autowired private SysRoleUserDAO sysRoleUserDAO; @Autowired private SysRoleResourceDAO sysRoleResourceDAO; public List<SysResource> getFirstMenu(Long userId) { List<SysRoleUser> list = sysRoleUserDAO.findAllByUserId(userId); List<Long> roleIds = new ArrayList<>(); for (SysRoleUser sysRoleUser :list){ roleIds.add(sysRoleUser.getRoleId()); } List<SysRoleResource> resourceIds = sysRoleResourceDAO.findAllByRoleIdIn(roleIds); List<SysResource> sysResources = new ArrayList<>(); for (SysRoleResource sysRoleResource:resourceIds){ SysResource sysResource = new SysResource(); sysResource.setResourceId(sysRoleResource.getResourceId()); sysResources.add(sysResource); } return sysResourceDAO.findAllByResourceIds(sysResources); } public List<MenuDTO> getSecondMenu(Long resourceId) { List<SysResource> list = sysResourceDAO.findAllBylist(resourceId); List<MenuDTO> menus = new ArrayList<>(); for(SysResource resource : list){ MenuDTO menu = new MenuDTO(); menu.setText(resource.getName()); menu.setPath(resource.getUrl()); menu.setIconCls(resource.getIcon()); menus.add(menu); } return menus; } }
这里创建了两个类:MenuDTO TreeBean
MenuDTO:
public class MenuDTO extends TreeBean { private String path; public String getPath() { return path; } public void setPath(String path) { this.path = path; } }
TreeBean:
@Data public class TreeBean { private String id; private String text; private String state = "open"; private boolean checked = false; private Object attributes; private String parentId; private String iconCls; }
在测试过程中,如果没有登录直接打开index的页面会出现乱码,原因是没有登录的话session获取不到userInfo,因此我设置了一个拦截器,创建一个LoginInterceptor类和一个工具类WebAppConfig
LoginInterceptor:
public class LoginInterceptor extends HandlerInterceptorAdapter { @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { HttpSession session = request.getSession(); if (session.getAttribute("userInfo") == null) { response.sendRedirect(request.getContextPath() + "/login"); return false; } return super.preHandle(request, response, handler); } }
如果session获取的userInfo为空的话就重定向到登陆界面
WebAppConfig:
@Configuration public class WebAppConfig implements WebMvcConfigurer { @Override public void addInterceptors(InterceptorRegistry registry) { String[] excludes = new String[]{"/static/**"}; registry.addInterceptor(new LoginInterceptor()).excludePathPatterns("/login").excludePathPatterns("/do_login").excludePathPatterns(excludes); WebMvcConfigurer.super.addInterceptors(registry); } @Override public void addResourceHandlers(ResourceHandlerRegistry registry) { registry.addResourceHandler("/static/**").addResourceLocations("classpath:/static/"); WebMvcConfigurer.super.addResourceHandlers(registry); } }
在配置拦截器的时候遇到几个小问题:
1.WebMvcConfigurerAdapter过时:
在网络上的大部分配置拦截器的教程都是继承WebMvcConfigurerAdapter,课时现在版本的WebMvcConfigurerAdapter已经被spring认为过时,我找了很久找到了它的替代品,也是官方推荐我们去使用的,实现 WebMvcConfigurer,具体的代码可见上面的WebAppConfig.
2.拦截器是配置成功了,但是登陆界面的静态资源也被拦截了
这个问题的解决方法WebAppConfig代码里也已经有了,首先String[] excluds定义加载的静态资源的路径,excludePathPatterns("/login")这个"/login"代表着这个路径不被拦截,所以我们在下面的代码中加入excludePathPatterns("excluds"),跳过拦截
在创建登陆的时候还遇到个小问题
@RequestMapping(value = "/index") public String index() { return "index"; } @RequestMapping(value = "/") public String toIndex() { return "index"; }
本意是想跳转到index页面
但是一直出现报错,错误的大概意思是包名和视图名重复了,修改多次index的名字后无果后来发现是freemarker的依赖没有添加
放上效果图:
其他的问题和过程写在下一篇的随笔里
以上是关于springboot+easyui+jpa实现动态权限角色的后台管理系统的主要内容,如果未能解决你的问题,请参考以下文章
SpringBoot中使用Spring Data Jpa 实现简单的动态查询的两种方法
springboot JPA 一对多关联查询 ,动态复杂查询 去除重复数据 in语句使用
Spring data jpa 实现简单动态查询的通用Specification方法
SpringBoot动态查询 Specification使用