8421-權限管理系統
8421權限
前幾天看所以到論壇上的朋友問關于權限管理的問題,勾起了我前些日子做的一個
權限系統項目的回憶,早就想為這個項目寫點什么了,一直苦于沒有時間,今天
偷閑,上來寫點東西。由于水平有限,可能我對權限管理的設計和理解還存在問
題,原創這篇文章,也只是起到個拋磚引玉的作用,大家一起來探討。
在整個權限系統中,可以分成5個概念。它們分別是:組,角色,成員,資源,權限
這5個概念構成了整個權限管理系統,其中權限是整個系統中的最小單位。
首先我舉個例子幫助大家理解這5個概念,譬如說你公司里一個項目組,我們可以把
這整個項目組理解成一個組的概念,而項目組里面有不同的角色,項目經理,項目組
長,技術經理,程序員,每個角色由不同的成員來充當,而其中每個成員又有著不同
的任務,項目經理有“需求分析”這個資源,程序員有“代碼開發”這個資源,而程序員
這個角色對于“代碼開發”這塊資源的權限擁有情況又不一樣,可能有的程序員擁有代
碼開發的權限而沒有對其他模塊代碼修改的權限,所以綜上所述這幾個概念可以畫成
一張E-R圖,但是由于我還沒學會貼圖,有心無力了。。
在我做這個系統的時候,我首先從角色入手,由于不同的系統對于自己的組,成員定
義不同,所以如果這套權限系統應用到其他系統上去的時候,可能組和成員的管理會
被重新開發,但是基本的角色,資源,權限的關系是可以應用到每一個部分的。
所以我先完成了角色->資源->權限部分,由于篇幅有限,所以我就只說這一重要模塊
了。
角色和資源之間屬于多對多的關系,即角色可以擁有多個資源,一個資源也可以被多
個角色所擁有。,而每個資源擁有不同的權限,大多web開發中,資源的權限都可以
分成5種,添加,刪除,修改,查詢,使用,我們可以首先創建一個基類來確定這5種
權限的唯一標識:
public class GenericPrivilegeBase
{
//當判斷權限時,按二進制位與,為1則有權限,為0則沒權限
public final static int NO_PRIVILEGE = 0; //0:無權限
public final static int QUERY_OR_USE_PRIVILEGE = 1; //1:查看或使用
public final static int CREATE_PRIVILEGE = 2; //2:創建
public final static int DELETE_PRIVILEGE = 4; //4:刪除
public final static int UPDATE_PRIVILEGE = 8; //8:修改
public final static int ALL_PRIVILEGE = QUERY_OR_USE_PRIVILEGE | CREATE_PRIVILEGE | DELETE_PRIVILEGE | UPDATE_PRIVILEGE;
//判斷是否有權限
public static boolean isValidPrivilege(int privilege)
{
if ( (privilege & QUERY_OR_USE_PRIVILEGE) != 0)
{
return true;
}
if ( (privilege & CREATE_PRIVILEGE) != 0)
{
return true;
}
if ( (privilege & DELETE_PRIVILEGE) != 0)
{
return true;
}
if ( (privilege & UPDATE_PRIVILEGE) != 0)
{
return true;
}
return false;
}
//判斷是否有查詢權限
public static boolean checkQueryPrivilege(int privilege)
{
if ( (privilege & QUERY_OR_USE_PRIVILEGE) != 0)
{
return true;
}
else
{
return false;
}
}
//判斷是否有創建權限
public static boolean checkCreatePrivilege(int privilege)
{
if ( (privilege & CREATE_PRIVILEGE) != 0)
{
return true;
}
else
{
return false;
}
}
......
//根據系統名不同而取不同的權限管理表
public String getResourceTableName()
{
return m_system_name + "Resource";
}
public String getPrivilegeTableName()
{
return m_system_name + "Privilege";
}
......
}
定義完這個權限判別基類后,我們后臺管理類(DAO)就可以通過繼承該類來獲得各種
權限管理表,對他進行操作。此時,在頁面上,我們需要對用戶資源進行過濾,在web
開發中,我們通常將url看成一種資源。而url對應的頁里的各種增刪改查操作看成是權
限,所以我們現在所要做的第一步就是進行url的過濾,當一個用戶登錄時,我們首先要
判定他有哪些資源,從而斷定他是否可以訪問該url。這里,我們可以通過一個過濾器
filter來過濾。下面給出該資源過濾器的部分代碼。
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain filterChain) throws IOException,
ServletException
{
/**@todo Implement this javax.servlet.Filter method*/
httprequest = (HttpServletRequest) request;
httpresponse = (HttpServletResponse) response;
session = httprequest.getSession();
//獲得用戶ID(登錄時存取在session)
String user_id = (String) session.getAttribute("UserID");
if (user_id == null)
user_id = "0";
//如果在設計數據庫時資源表沒有設計對應的url字段,可以將資源對應url關系寫到xml里
// XMLAnalyze xml_analyze = new XMLAnalyze(this.resource_path);
// String res_id = xml_analyze.XMLResourceReader(httprequest.
// getServletPath());
Connection conn = DataSourcePool.getDS().getConnection();
GenericPrivilegeBase user_manager = new GenericPrivilegeBase(conn,
system_name);
String sql = "select Privilege,Url from " + user_manager.getRoleMemberTableName();
sql += " as r ," + user_manager.getPrivilegeTableName() +
" as p, "+user_manager.getResourceTableName()+" as u where r.MemberID='" + Integer.parseInt(user_id) + "'";
sql += " and r.RoleID = p.RoleID and p.ResID = u.ResID";
boolean flag = false;
String request_url = httprequest.getServletPath();
if (request_url.indexOf("Action.do") != -1)
{
request_url = request_url.replaceAll("Action.do", ".jsp");
request_url = request_url.toLowerCase();
}
try
{
Statement stmt = conn.createStatement();
ResultSet rs = stmt.executeQuery(sql);
while (rs.next())
{
int privilege = rs.getInt(1);
String url_list = rs.getString(2);
if(url_list != null)
{
String[] url = url_list.split(",");
for (int i = 0; i < url.length; i++)
{
//如果權限不為0且請求url包含該用戶所擁有的資源對應的url
if (privilege != 0 && request_url.indexOf(url) !=-1)
{
flag = true;
break;
}
if (flag)
break;
}
}
}
rs.close();
stmt.close();
conn.close();
}
catch (SQLException ex)
{
System.out.println(ex.getMessage());
if (conn != null)
{
try
{
conn.close();
}
catch (Exception e)
{}
}
}
boolean find = false;
//除去不需要進行資源過濾的url,如login.jsp等
if (this.special != null)
{
String[] except_page = this.special.split(",");
if (except_page.length >= 0)
{
for (int i = 0; i < except_page.length; i++)
{
if (request_url.indexOf(except_page) >= 0)
{
find = true;
break;
}
}
}
}
if (!find)
{
if (!flag || user_id == null || user_id.trim().length() <= 0)
{
httprequest.getRequestDispatcher(this.error_page).forward(httprequest,httpresponse);
}
else
{
filterChain.doFilter(httprequest, httpresponse);
}
}
else
{
filterChain.doFilter(httprequest, httpresponse);
}
}
通過這個過濾器,我們完成了資源的過濾,從此我們不必再去關心用戶能否訪問某個
url(資源),接下來的我們需要解決的問題就是就是如何在頁面里面有效的控制增加
,刪除等這些權限,這可以通過兩個辦法來控制。
第一個辦法,就是在用戶登錄的時候
我們將該用戶所有的資源都取出來放入一個容器內,每次遇到需要權限判定的地方就將
這個容器里的內容循環,看看是否有該權限。
第二個辦法是通過一個自定義的標簽來控制。只要向自定義的標簽內傳入用戶id和系統
名就可以了,這種標簽自然是應該成對的,包括有此權限的標簽和無此權限的標簽,類
似<logic:present>和<logic:notPresent>標簽,具體的自定義標簽我書寫如下:
<page:privilege beanName="InitBean" scope="session" operation="UPDATE">
<html:link href="modifyRoleAction.do" paramId="roleid" paramName="role" paramProperty="m_role_id">修改</html:link>
</page:privilege>
這段標簽的意思是:如果取InitBean這個javabean中的屬性字段userid為當前用戶,該
用戶在本頁url(本頁url是一個資源),如果有UPDATE權限,那就執行標簽內body部分的
鏈接代碼(html:link)
與此標簽對應的標簽如下:
<page:notPrivilege beanName="InitBean" scope="session" operation="UPDATE">
<html:link href="modifyRoleAction.do" paramId="roleid" paramName="role" paramProperty="m_role_id">修改</html:link>
</page:notPrivilege>
我給出一部分該標簽類代碼:
public int doStartTag() throws JspTagException
{
int userID = 0;
String systemName = "";
if (scope == null || scope.equals(""))
return SKIP_BODY;
else
{
if (scope.equalsIgnoreCase("session"))
{
HttpSession session = pageContext.getSession();
LoginForm init_bean = (LoginForm)session.getAttribute("InitBean");
if (init_bean == null)
return SKIP_BODY;
String user_id = init_bean.getUserid();//得到用戶id
systemName = init_bean.getSystem_name();
try
{
userID = Integer.parseInt(user_id);
}
catch (NumberFormatException ex1)
{
}
}
else
if (scope.equalsIgnoreCase("request"))
{
HttpServletRequest request = (HttpServletRequest)pageContext.getRequest();
LoginForm init_bean = (LoginForm)request.getAttribute("InitBean");
if (init_bean == null)
return SKIP_BODY;
String user_id = init_bean.getUserid();
systemName = init_bean.getSystem_name();
try
{
userID = Integer.parseInt(user_id);
}
catch (NumberFormatException ex1)
{
}
}
}
Connection conn = DataSourcePool.getDS().getConnection();
if (conn == null || systemName == null || operation == null)
return SKIP_BODY;
GenericPrivilegeBase user_manager = new GenericPrivilegeBase(conn,
systemName);
HttpServletRequest request = (HttpServletRequest)pageContext.getRequest();
String resource_path = request.getServletPath();//得到本頁資源url
String sql = "select Privilege,Url from " + user_manager.getRoleMemberTableName();
sql += " as r ," + user_manager.getPrivilegeTableName() +
" as p, "+user_manager.getResourceTableName()+" as u where r.MemberID='" + userID + "'";
sql += " and r.RoleID = p.RoleID and p.ResID = u.ResID";
int privilege = 0;
try
{
Statement stmt = conn.createStatement();
ResultSet rs = stmt.executeQuery(sql);
while (rs.next())
{
privilege = rs.getInt(1);
String url_list = rs.getString(2);
if(url_list != null)
{
String[] url = url_list.split(",");
for (int i = 0; i < url.length; i++)
{
//如果權限不為0且請求url包含該用戶所擁有的資源對應的url
if (privilege != 0 && request_url.indexOf(url) !=-1)
{
flag = true;
break;
}
if (flag)
break;
}
}
}
rs.close();
stmt.close();
conn.close();
}
catch (SQLException ex)
{
System.out.println(ex.getMessage());
if (conn != null)
{
try
{
conn.close();
}
catch (Exception e)
{}
}
return SKIP_BODY;
}
if (operation.equals("NONE"))
return EVAL_BODY_TAG;
if (operation.equals("QUERY"))//如果需要判定是否具有查詢
if (user_manager.checkQueryPrivilege(privilege))//如果具有查詢權限
return EVAL_BODY_TAG;
if (operation.equals("CREATE"))
if (user_manager.checkCreatePrivilege(privilege))
return EVAL_BODY_TAG;
if (operation.equals("DELETE"))
if (user_manager.checkDeletePrivilege(privilege))
return EVAL_BODY_TAG;
if (operation.equals("UPDATE"))
if (user_manager.checkUpdatePrivilege(privilege))
return EVAL_BODY_TAG;
if (operation.equals("USE"))
if (user_manager.checkUsePrivilege(privilege))
return EVAL_BODY_TAG;
return SKIP_BODY;
}
通過這兩個標簽就可以判定頁面上的所有權限了。
在以上的說明和代碼里面,我們對權限的控制主要是通過4個二進制位與來獲得,他們
分別是8,4,2,1,這四位二進制位分別代表了4種權限,我們在工程的開始就將他們
寫入一個基類,這樣后面判定權限的時候都引用了這個基類里面的判定函數。