1. 程式人生 > >基於Java Web的許可權管理系統的設計與實現

基於Java Web的許可權管理系統的設計與實現

許可權管理系統在去年的專案中使用過,後來一直想單獨拿出來做一個許可權管理系統,一直拖著,今年做的部落格當中也使用到了,趁著有時間就把這個Demo寫出來了,使用的是SSM框架 + Maven實現的,利用過濾器和URL來控制使用者訪問的頁面。本系統沒有使用Apache Shiro。

我寫的許可權管理系統主要由許可權、角色、使用者組成。有的許可權管理系統也包括使用者分組,那樣的我就沒寫了,如果理解了由以上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值,併發的時候肯定會出問題的,我沒解決

當然還有很多程式碼可以優化,當然許可權管理可以用別的方式,歡迎和我討論。