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种权限,我们在工程的开始就将他们
写入一个基类,这样后面判定权限的时候都引用了这个基类里面的判定函数。