基于Springboot的角色用户菜单权限系统
2019-03-25 本文已影响1554人
欧_汤姆
假设我们要做一个学校的管理系统,要求不同的角色用户登录进去所看到的内容是不同的,例如校长和门卫登录进去所看到的菜单页面是不同的。校长可以看到全校的摄像头,而门卫看到的摄像头个数则是有限制的。那么如何来实现呢?
首先来分析需要些什么:
1.用户表 user
2.角色表 role
3.菜单表 menu
4.摄像头表 camera
5.用户角色表 roleUser
6.用户菜单表 roleMenu
7.用户摄像头表 roleCamera
以下截图仅供举例参考








关于表的数据就是这些了,里面的字段根据自己的项目需求添加。
前台的整个执行流程是什么呢?


点击保存之后
form表单提交到后台将数据添加到用户表(user)中,这个基本的增删改查不多说了,很简单。
然后写一个角色管理页面,可以配置角色。

点击 添加角色

点击保存,将数据存入role(角色表)基础的增删改查不多说

点击分配用户

之前添加的两个用户展现出来,如果选择用户点击保存,则后台将用户的userId和这个角色的roleId保存到roleUser表中
同理,分配菜单和摄像头都是这个流程。
那么,如果菜单分配好了,假设,我给“最高管理者”这个角色分配了四个菜单,“次管理者”这个角色分配了两个菜单,将用户“校长”分配给“最高管理者”这个角色。也就是说,校长这个用户登录进入这个平台的时候应该只能看见四个菜单。
新建一个控制器:platformController
package com.lencity.securitymanagementplatform.controller;
import java.util.ArrayList;
import java.util.List;
import javax.servlet.http.HttpSession;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.servlet.ModelAndView;
import com.alibaba.fastjson.JSONObject;
import com.lencity.securitymanagementplatform.data.entity.Camera;
import com.lencity.securitymanagementplatform.data.entity.Menu;
import com.lencity.securitymanagementplatform.data.entity.Role;
import com.lencity.securitymanagementplatform.data.entity.RoleMenu;
import com.lencity.securitymanagementplatform.data.entity.RoleUser;
import com.lencity.securitymanagementplatform.data.entity.User;
import com.lencity.securitymanagementplatform.data.service.MenuService;
import com.lencity.securitymanagementplatform.data.service.RoleMenuService;
import com.lencity.securitymanagementplatform.data.service.RoleUserService;
import com.lencity.securitymanagementplatform.data.service.UserService;
@Controller
@Scope("session")
@RequestMapping("")
public class PlatformController {
@Autowired
private UserService userService;
@Autowired
private RoleUserService roleUserService;
@Autowired
private RoleMenuService roleMenuService;
@Autowired
private MenuService menuService;
@GetMapping("/")
public String index() {
return "forward:/login.html";
}
@PostMapping(value = "/login", produces = "application/json;charset=utf-8")
@ResponseBody
public String login(String name, String password, HttpSession session) {
User user = userService.login(name, password);
if (user == null) {
session.removeAttribute("loginUser");
return "0";
}
session.setAttribute("loginUser", user);
return "1";
}
@RequestMapping(value = "/logout", produces = "application/json;charset=utf-8")
@ResponseBody
public String logout(HttpSession session) {
session.removeAttribute("user");
return "1";
}
@RequestMapping(value = "/changePassword", produces = "application/json;charset=utf-8")
@ResponseBody
public String changePassword(String oldPassword, String newPassword, HttpSession session) {
User user = (User) session.getAttribute("user");
boolean result = userService.changePassword(user.getName(), oldPassword, newPassword);
return result ? "1" : "0";
}
}
JS
function login() {
$("#form").ajaxSubmit({
type : 'POST',
cache : false,
success : function(result) {
if (result == "0") {
alert("帐号或密码错误,请重新输入!");
} else {
location.href = "user";
}
}
});
}
接下来就是拦截器

package com.lencity.securitymanagementplatform.interceptor;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import com.lencity.securitymanagementplatform.data.entity.Menu;
import com.lencity.securitymanagementplatform.data.entity.RoleMenu;
import com.lencity.securitymanagementplatform.data.entity.RoleUser;
import com.lencity.securitymanagementplatform.data.entity.User;
import com.lencity.securitymanagementplatform.data.service.MenuService;
import com.lencity.securitymanagementplatform.data.service.ModuleService;
import com.lencity.securitymanagementplatform.data.service.RoleMenuService;
import com.lencity.securitymanagementplatform.data.service.RoleUserService;
@Component
public class PlatformInterceptor implements HandlerInterceptor {
@Autowired
private ModuleService moduleService;
@Autowired
private RoleMenuService roleMenuService;
@Autowired
private RoleUserService roleUserService;
@Autowired
private MenuService menuService;
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
HttpSession session = request.getSession();
if (session.getAttribute("moduleMenus") == null) {
session.setAttribute("moduleMenus", moduleService.getModuleMenus());
}
User user = (User) session.getAttribute("loginUser");
if (user != null) {
RoleUser roleUser = roleUserService.getUserByUserId(user.getId());
List<RoleMenu> roleMenus = roleMenuService.getRoleMenuByRoleId(roleUser.getRoleId());
List<Menu> menus=new ArrayList<>();
for (RoleMenu roleMenu : roleMenus) {
Menu menu = menuService.getMenuById(roleMenu.getMenuId());
menus.add(menu);
}
if (session.getAttribute("indexMenus") == null) {
session.setAttribute("indexMenus", menus);
}
return true;
} else {
//response.sendRedirect(request.getContextPath());
redirect(request, response);
return false;
}
}
// 对于请求是ajax请求重定向问题的处理方法
public void redirect(HttpServletRequest request, HttpServletResponse response) {
// 获取当前请求的路径
String basePath = request.getContextPath();
// 如果request.getHeader("X-Requested-With") 返回的是"XMLHttpRequest"说明就是ajax请求,需要特殊处理
// 否则直接重定向就可以了
if ("XMLHttpRequest".equals(request.getHeader("X-Requested-With"))) {
// 告诉ajax我是重定向
response.setHeader("REDIRECT", "REDIRECT");
// 告诉ajax我重定向的路径
response.setHeader("CONTENTPATH", basePath + "/");
response.setStatus(HttpServletResponse.SC_FORBIDDEN);
} else {
try {
response.sendRedirect(basePath + "/");
} catch (IOException e) {
e.printStackTrace();
}
}
}
}

package com.lencity.securitymanagementplatform;
import java.util.ArrayList;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import com.lencity.securitymanagementplatform.interceptor.ModuleInterceptor;
import com.lencity.securitymanagementplatform.interceptor.PlatformInterceptor;
@Configuration
public class PlatformWebConfiguration implements WebMvcConfigurer {
@Autowired
private PlatformInterceptor platformInterceptor;
@Autowired
private ModuleInterceptor moduleInterceptor;
@Override
public void addInterceptors(InterceptorRegistry registry) {
List<String> platformExcludePathes = new ArrayList<>();
platformExcludePathes.add("/moduleInterface/**");
platformExcludePathes.add("/");
platformExcludePathes.add("/platform/**");
platformExcludePathes.add("/login.html");
platformExcludePathes.add("/login");
platformExcludePathes.add("/publicData/**");
platformExcludePathes.add("/library/**");
platformExcludePathes.add("/data/**");
platformExcludePathes.add("/image/**");
platformExcludePathes.add("/js/**");
platformExcludePathes.add("/*.txt");
registry.addInterceptor(platformInterceptor).addPathPatterns("/**").excludePathPatterns(platformExcludePathes);
registry.addInterceptor(moduleInterceptor).addPathPatterns("/moduleInterface/**");
WebMvcConfigurer.super.addInterceptors(registry);
}
}
通过拦截器,userId去查询roleId,然后通过roleId去查询roleMenu 的菜单id
,最后通过菜单id去查询菜单表。存入session中。
menu.html
<section>
<!-- Left Sidebar -->
<aside id="leftsidebar" class="sidebar">
<!-- User Info -->
<div class="user-info">
</div>
<div class="menu">
<ul class="list">
<#list indexMenus as menu>
<li <#if page=="${menu.menuUrl}">class="active"</#if>>
<a href="${request.contextPath}/${menu.menuUrl}">
<i class="material-icons">${menu.menuLogo}</i>
<span>${menu.menuLabel}</span>
</a>
</li>
</#list>
<li <#if page=="module">class="active"</#if>>
<a class="menu-toggle">
<i class="material-icons">apps</i>
<span>平台模块</span>
</a>
<ul class="ml-menu">
<li >
<a href="${request.contextPath}/module">模块管理</a>
</li>
<#list moduleMenus as moduleMenu>
<li>
<a href="${request.contextPath}/module/page?pageUrl=${moduleMenu.configMenu.menuUrl}">${moduleMenu.configMenu.menuLabel}</a>
</li>
</#list>
</ul>
</li>
<li>
<a href="javascript:void(0);" class="menu-toggle">
<i class="material-icons">apps</i>
<span>模块事件记录</span>
</a>
<ul class="ml-menu">
<#list moduleMenus as moduleMenu>
<li>
<a class="menu-toggle">${moduleMenu.moduleName}</a>
<ul class="ml-menu">
<#list moduleMenu.recordMenus as recordMenu>
<li>
<a href="${recordMenu.menuUrl}">${recordMenu.menuLabel}</a>
</li>
</#list>
</ul>
</li>
</#list>
</ul>
</li>
<li>
<a href="javascript:void(0);" class="menu-toggle">
<i class="material-icons">apps</i>
<span>模块数据报表</span>
</a>
<ul class="ml-menu">
<#list moduleMenus as moduleMenu>
<li>
<a class="menu-toggle">${moduleMenu.moduleName}</a>
<ul class="ml-menu">
<#list moduleMenu.reportMenus as reportMenu>
<li class="active">
<a href="${reportMenu.menuUrl}">${reportMenu.menuLabel}</a>
</li>
</#list>
</ul>
</li>
</#list>
</ul>
</li>
</ul>
</div>
<!-- #Menu -->
<!-- Footer -->
<div class="legal">
<div class="copyright">
© 2018 - 2019 XXXX信息科技有限公司.
</div>
<div class="version">
<b>Version: </b> 0.1
</div>
</div>
<!-- #Footer -->
</aside>
<!-- #END# Right Sidebar -->
</section>
<div class="modal fade" id="changePasswordDialog" tabindex="-1"
role="dialog">
<div class="modal-dialog" role="document"
style="margin-top: 10%; width: 480px;">
<div class="modal-content">
<div class="card">
<div class="header">
<h2>修改密码</h2>
</div>
<div class="body">
<div class="content clearfix">
<form class="form-horizontal" id="changePasswordForm" action="${request.contextPath}/changePassword" method="post">
<div class="row clearfix">
<div class="col-lg-3 col-md-3 col-sm-3 col-xs-3 form-control-label">
<label for="email_address_2">原密码:</label>
</div>
<div class="col-lg-9 col-md-9 col-sm-9 col-xs-9">
<div class="form-group">
<div class="form-line">
<input type="password" name="oldPassword" class="form-control" placeholder="请输入原密码" minlength="6" maxlength="20">
</div>
</div>
</div>
</div>
<div class="row clearfix">
<div class="col-lg-3 col-md-3 col-sm-3 col-xs-3 form-control-label">
<label for="email_address_2">新密码:</label>
</div>
<div class="col-lg-9 col-md-9 col-sm-9 col-xs-9">
<div class="form-group">
<div class="form-line">
<input type="password" id="newPassword" name="newPassword" class="form-control" placeholder="请输入6位以上20位以内的密码" minlength="6" maxlength="20">
</div>
</div>
</div>
</div>
<div class="row clearfix">
<div class="col-lg-3 col-md-3 col-sm-3 col-xs-3 form-control-label">
<label for="email_address_2">确认新密码:</label>
</div>
<div class="col-lg-9 col-md-9 col-sm-9 col-xs-9">
<div class="form-group">
<div class="form-line">
<input type="password" name="confirmPassword" class="form-control" placeholder="请确认密码" minlength="6" maxlength="20">
</div>
</div>
</div>
</div>
<div class="row clearfix">
<div class="col-lg-offset-4 col-md-offset-4 col-sm-offset-4 col-xs-offset-4">
<button type="button" class="btn btn-primary waves-effect m-t-15 btn-lg" style="width:120px;" onclick="changePassword()">
<i class="material-icons">save</i>
<span>保存</span>
</button>
<button type="button" class="btn btn-default waves-effect btn-lg header-dropdown m-l-15 m-t-15" onclick="closeChangePasswordDialog()">
<i class="material-icons">cancel</i>
<span>取消</span>
</button>
</div>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
</div>
<script>
var Menuvalidate;
function showChangePassword() {
$("#changePasswordDialog").modal({
show : true,
backdrop : "static"
});
}
function closeChangePasswordDialog(){
$('#changePasswordDialog').modal('hide');
}
$(function() {
Menuvalidate = $('#changePasswordForm').validate({
rules : {
'oldPassword' : {
required : true
},
'newPassword' : {
required : true
},
'confirmPassword' : {
required : true,
equalTo : "#newPassword"
}
},
highlight : function(input) {
$(input).parents('.form-line').addClass('error');
},
unhighlight : function(input) {
$(input).parents('.form-line').removeClass('error');
},
errorPlacement : function(error, element) {
$(element).parents('.form-group').append(error);
}
});
$.validator.addMethod('newPassword', function(value, element) {
return value.match(/^(?![0-9]+$)(?![a-zA-Z]+$)[0-9A-Za-z]{6,20}$/);
}, '请输入6位以上20位以内数字+字母组合的密码');
});
function changePassword() {
$("#changePasswordForm").ajaxSubmit({
beforeSubmit: function() {
return Menuvalidate.form();
},
success: function(result) {
if(result==1){
closeChangePasswordDialog();
swal({
title: "提示",
text: "<span style='color:#40831C;font-size:30px;'>修改成功!</span>",
type: "success",
timer: 2000,
showConfirmButton: false,
html: true
});
setInterval(() => {
$(window).attr('location',contextPath);
}, 2000);
}else{
swal({
title: "原密码错误",
text: "<span style='color:red;font-size:30px;'>修改失败!</span>",
type: "error",
timer: 2000,
showConfirmButton: false,
html: true
});
}
},
complete : function(xhr, status){
forwardToLogin(xhr, status);
}
});
}
function logout() {
swal({
title: "确定退出登录?",
type: "warning",
showCancelButton: true,
confirmButtonColor: "#2196F3",
confirmButtonText: "确定",
cancelButtonText: "取消",
closeOnConfirm: false,
html: true
},
function(isConfirm){
if(isConfirm){
$.ajax({
url: contextPath + "/logout",
cache : false,
success : function(result) {
$(window).attr('location',contextPath);
},
complete : function(xhr, status){
forwardToLogin(xhr, status);
}
});
}
});
}
</script>
循环遍历session中的menus,展现如下效果

Over~