基于Java Web的权限管理系统的设计与实现

权限管理系统在去年的项目中使用过,后来一直想单独拿出来做一个权限管理系统,一直拖着,今年做的博客当中也使用到了,趁着有时间就把这个Demo写出来了,使用的是SSM框架 + Maven实现的,利用过滤器和URL来控制用户访问的页面。本系统没有使用Apache Shiro。

源代码地址:https://github.com/ShrMus/PrivilegeSystem

我写的权限管理系统主要由权限、角色、用户组成。有的权限管理系统也包括用户分组,那样的我就没写了,如果理解了由以上3个组成的权限管理系统,相信聪明的你也能实现分组的权限。

先来说说设计的事情


解释一下,一个角色拥有多个权限,一个权限可以被多个角色拥有;一个用户可以拥有多个权限,一个权限可以被多个用户拥有;一个角色可以被多个用户拥有,一个用户可以拥有多个角色。

接下来是数据库表,我使用的是MySQL数据库。

权限表Privilege

字段名

数据类型

备注

privilege_id

int

逻辑主键,自增

privilege_name

varchar(50)

权限名

privilege_url

varchar(255)

权限url

privilege_parent_id

int

父权限id,例如角色管理是父权限,角色增、删、改、查是子权限

角色表Role

字段名

数据类型

备注

role_id

int

逻辑主键,自增

role_name

varchar(20)

角色名

role_description

varchar(255)

描述

用户表User

字段名

数据类型

备注

user_id

int

逻辑主键,自增

user_username

varchar(20)

用户名

user_password

varchar(100)

密码

角色权限表Role_Privilege

字段名

数据类型

备注

role_privilege_id

int

逻辑主键,自增

role_id

int

角色id

privilege_id

int

权限id

用户权限表User_Privilege

字段名

数据类型

备注

user_privilege_id

int

逻辑主键,自增

user_id

int

用户id

privilege_id

int

权限id

用户角色表User_Role

字段名

数据类型

备注

user_role_id

int

逻辑主键,自增

user_id

int

用户id

role_id

int

角色id

六张表,根据E-R图和数据库表大家可以知道表和表之间的关系。


好了,设计到这里就完成了。

麻烦各位移驾文章开头,下载系统源代码。

接下来说说实现

我用的是Mybatis逆向工程生成实体类和mapper文件,你也可以直接将sql文件导入到数据库。

生成实体类和mapper文件之后的第一件事是修改Privilege、Role和User实体类。

Privilege类中添加了

  private Privilege parentPrivilege;
    
  /**
   * 子权限列表
   */
  private List<Privilege> childPrivilegeList;

Role类中添加了

/**
 * 角色拥有的权限列表
 */
private List<Privilege> privilegeList;
   	
/**
 * 拥有这个角色的用户
 */
private List<User> userList; 

User类中添加了

/**
 * 用户所有角色
 */
private List<Role> userRoleList;

/**
 * 用户拥有的权限列表
 */
private List<Privilege> privilegeList;

由于Mybatis逆向工程和Hibernate逆向工程不一样,所以就只能手动添加。Hibernate使用得多的朋友就会知道接下来要干嘛了,Hibernate逆向工程生成实体类之后需要在xxx.hbm.xml中添加刚刚在实体类中添加的属性,配置实体之间的关系,而Mybatis就是在对应的xxxMapper.xml中进行配置。

PrivilegeMapper.xml中添加

<association property="parentPrivilege" column="privilege_parent_id" select="selectByPrimaryKey" />
<collection property="childPrivilegeList" column="privilege_id" select="getChildrenPrivilegeListById" />

mapper文件中这两个标签的用法可以在网上查到,这两个标签中的select属性,它的值是<select>标签的id,第一个是查找父权限,可以根据父权限id查找,第二个是查找子权限列表,所以就得自己写了,我写在文件末尾。

  <!-- 查找权限id的所有子权限 -->
  <select id="getChildrenPrivilegeListById" parameterType="java.lang.Integer" resultMap="BaseResultMap">
  	select privilege_id,privilege_name,privilege_url,privilege_parent_id 
  	from privilege 
  	where privilege_parent_id=#{id} 
  </select>

接着修改RoleMapper.xml,添加

<collection column="role_id" property="privilegeList" select="com.shrmus.mapper.PrivilegeMapper.getPrivilegeListByRoleId" />
<collection column="role_id" property="userList" select="com.shrmus.mapper.UserMapper.getUserListByRoleId" />

第一个是查找角色拥有的权限,我把这条sql语句写在PrivilegeMapper.xml中

  <!-- 在role_privilege表中根据角色id查找拥有的权限 -->
  <select id="getPrivilegeListByRoleId" parameterType="java.lang.Integer" resultMap="com.shrmus.mapper.PrivilegeMapper.BaseResultMap">
  	select distinct p.privilege_id,p.privilege_name,p.privilege_url,p.privilege_parent_id 
  	from role_privilege rp 
  	left join privilege p on rp.privilege_id=p.privilege_id 
  	where role_id=#{roleId} 
  </select>

第二个是查找拥有这个角色的用户,我写在UserMapper.xml末尾

  <!-- 根据角色id查找拥有这个角色的所有用户 -->
  <select id="getUserListByRoleId" parameterType="java.lang.Integer" resultMap="BaseResultMap">
  	select r.role_id,r.role_name 
  	from user_role ur 
  	left join role r on 
  	ur.role_id=r.role_id 
  	where user_id=#{userId} 
  </select>

然后修改UserMapper.xml,添加

    <collection column="user_id" property="userRoleList" select="com.shrmus.mapper.RoleMapper.getRoleListByUserId" />
    <collection column="user_id" property="privilegeList" select="com.shrmus.mapper.PrivilegeMapper.getPrivilegeListByUserId" />

第一个是查找用户拥有的角色列表,我写在RoleMapper.xml末尾

  <!-- 根据用户id查找用户的角色信息 -->
  <select id="getRoleListByUserId" parameterType="java.lang.Integer" resultMap="BaseResultMap">
  	select r.role_id,r.role_name 
  	from user_role ur 
  	left join role r on 
  	ur.role_id=r.role_id 
  	where user_id=#{userId} 
  </select>

第二个是查找用户拥有的权限,我写在PrivilegeMapper.xml末尾

  <!-- 在user_privilege表中根据id查找用户拥有的权限 -->
  <select id="getPrivilegeListByUserId" parameterType="java.lang.Integer" resultMap="com.shrmus.mapper.PrivilegeMapper.BaseResultMap">
  	select distinct p.privilege_id,p.privilege_name,p.privilege_url,p.privilege_parent_id 
  	from user_privilege up 
  	left join privilege p on up.privilege_id=p.privilege_id 
  	where user_id=#{userId}  
  </select>

关于实体类的配置就完成了。

接下来要做的就是初始化权限、角色和用户。在此之前,你可以需要修改数据库连接密码,在src/main/resources/mybatis/dbconfig.properties文件中的jdbc.password。

移驾src/test/java,打开com.shrmus.test中的Init.java文件。

第一步是初始化权限。测试的权限只做了这些。


运行initPrivilege测试方法,就会将这些权限添加到数据库。

然后是初始化角色,我将角色分为超级管理员、普通管理员、普通用户3个角色。

运行initRole测试方法,会添加这3个角色,并且初始化超级管理员的权限,超级管理员拥有所有权限。

最后是初始化用户,运行initUser测试方法,添加一个用户名为admin密码为admin的用户,分配超级管理员的角色,分配超级管理员拥有的权限。

初始化的工作就完成了。

接下来就讲几个思路

关于用户

添加用户(超级管理员添加用户,并不是用户自己注册)时,至少要选择一个角色,首先添加用户的信息,然后查找添加用户时选择的角色的权限,将角色的权限添加为用户的权限,如果多个角色之间有相同的权限会去重。

修改用户时,可能会修改用户的角色信息,如果修改了用户的角色信息,就要修改用户的权限。

删除用户时,需要将用户拥有的权限也从数据库删除。

关于角色

删除角色时(实际上我觉得没必要删除角色),首先删除拥有这个角色的用户的权限,然后用户的角色信息,再删除角色拥有的权限,最后删除角色。

关于权限

删除权限,首先在用户权限表中删除用户拥有的这个权限,再在角色权限表中删除角色拥有的这个权限,最后删除权限。

分配权限,分配权限分为给角色分配权限和为用户分配权限。分配权限的图在上面有,需要分配哪个权限就勾上。

首先说给用户分配权限,需要将新分配的权限和这个用户原有的权限作比较,新权限在原权限中没有就添加,新权限在原权限中有就不用管,原权限在新权限中没有就删除。

给角色分配权限,如果是角色还没有分配权限而又有用户拥有这个角色,在第一次为角色分配权限的时候需要添加角色的权限和拥有这个角色的用户的权限。修改角色的权限和修改用户的权限的思路是一样的,但是修改角色的权限之后还要修改拥有这个角色的用户的权限。具体可以看代码实现。

到最后,谈谈怎么做到权限的控制

在src/main/resources/loginbeforeurl.txt文件中添加了一些URL路径,如果访问的URL包含这些,就需要登录,例如添加用户的URL是http://localhost:8090/privilege/user/add,文件中添加了/user/add,访问这个页面的前提需要登录,你也可以将这些放到数据库中。

控制用户访问的页面就是这样一个思路。

移驾src/main/java的com.shrmus.listener的InitPrivilegeListener.java文件,这个类实现了监听器ServletContextListener,作用就是在Tomcat启动的时候执行contextInitialized方法,在这个方法中查找数据库中添加的所有权限,查找访问前需要登录的路径,放到全局作用域中。

移驾src/main/java的com.shrmus.filter的MyUrlFilter.java文件,首先通过request.getServletPath()获取工程下的访问路径,例如添加用户的URL是http://localhost:8090/privilege/user/add,通过这个方法获取到的就是/user/add,这下知道怎么控制用户访问的页面了吧。就是用到字符串的contains方法。

获取全局作用域当中的权限列表和访问前需要登录的两个集合,就可以做到权限控制了。

另外说一下,在分配权限页面中,对已有的权限回显用到了自定义EL表达式,文件在WEB-INF目录下的myel.tld,对应的类在src/main/java的com.shrmus.tag的MyEL.java,原有的fn:contains是判断一个集合是否包含一个对象,在做回显的时候是判断权限的ID,所以就写了个自定标签。

在WEB-INF/page/privilege/allocation.jsp中用到了${myel:contains(object.privilegeList,privilege)},引入了<%@ taglib uri="/myel" prefix="myel"%>,EL表达式中的object是不确定是给角色分配权限还是给用户分配权限,所以用的是object,对应的类的代码是

public class MyEL{

	public static boolean contains(List<Object> objects,Object element) {
		for(Object obj:objects) {
			Privilege temp = (Privilege)obj; 
			Privilege elementPrivilege = (Privilege)element;
			if(temp.getPrivilegeId() == elementPrivilege.getPrivilegeId()) {
				return true;
			}
		}
		return false;
	}
}

整个权限管理系统就是这样了,功能也都测试过,可能会出现BUG,因为我前几天测试的时候遇到了几个问题。

给普通管理员分配权限时,用户权限表清空了;

用户分配了权限之后,在给用户拥有的角色分配权限,出现了问题;

刚刚测试的时候又没有问题,所以就先放着了,哪位读者测试的时候出现了问题可以和我联系。

还有添加用户的时候,用户至少要有一个角色,可以用js控制,我没写

添加用户的方法中用到了一个getMaxPrimaryKey方法,获取当前用户表中的最大id值,并发的时候肯定会出问题的,我没解决

当然还有很多代码可以优化,当然权限管理可以用别的方式,欢迎和我讨论。


猜你喜欢

转载自blog.csdn.net/ShrMuscles/article/details/80532495