事情挺让我头痛的,记得一年前在沈阳,我曾经有一段时间也因因这个问题而疲于奔命,为什么说疲于奔命呢?由
于当时项目进度不允许,导致最终系统权限模块草草了事,每个模块都是由读权限字符串来控制用户ACL,当用户无
法访问时,提示权限不够。这么做对用户是很不负责任的,既然让用户看到了操作的方式和界面,为什么又告诉用
户没有权限呢?我开始怀疑我们是否应该在底层就封杀用户的访问权限。
现在项目开展起来了,虽然目前我已经有了对权限控制的一套方案,并且实施成了我的可重用框架代码,虽然目前
的权限也是基于众星捧月的AOP思想,但我至今对权限设计仍有两个疑惑:
疑惑一:很多同行提出方案,想要在底层就截取用户权限,控制用户对方法或者类的访问。这样做的好处在于可以
将系统功能与业务逻辑松散耦合,并且实现简单,结构清晰,三两个advisor、filter,或者acegi就能搞定,但在
web程序中体现出了他的劣势,当我们将用户的访问拒绝在业务逻辑之外的时候,我们此时是否应该抛出异常提示用
户?一旦提示用户没有相应的权限,我认为对于用户来说,这就不是一个perfect practice。由此得出,我们根本
就不应该让用户做此次操作,而控制用户操作的源头就是界面,也就是说,在界面上我们就应该对用户的权限元素(
如添加按钮、功能菜单等)进行控制。此时,一对矛盾出现了,要控制界面上形形色色的元素只有两种办法,一,将
权限与你的界面结合起来设计,这将违背AOP的思想,也使得系统控制模块的重用性大大下降,二,我们借鉴
primeton的想法,将权限控制的理念抽取出来,单独做成一套权限系统,解决你所有的需要权限控制的系统需求,
这样也有令人头痛的问题,你的所有想用它来控制权限的系统,必须界面上统一风格。或许这样的方式对商业web系
统是合适的,毕竟需要你大刀阔斧个性化的地方不多,但我们却很难保证在未来几年内商业web系统的风格不改变。
再者,开发这么一个系统也不是一蹴而就的事,在这个问题上一直让我困惑不已。
疑惑二:大多应用的权限判定是基于权限字符串的,但存储在数据库中的权限字符串能够判定的权限并不多,在我
们这次项目中,我引用了基于二进制的8421权限判定法则,我深深的感觉到权限字符串的弱势,这使我想起了中国
古老一套数学理论-“盈不足术”,超递增序列的魅力在我眼前滑过,
首先我来解释一下盈余不足理论:有十只盒子,第一个盒子里放一个盘子,第二个盒子里放两只,第三个盒子里放
四只,第四个盒子里放八只……第九个盒子里放256只,第十个盒子放512只,即第N只箱子里放2^(N-1)只盘子,一
共1023只。那么命题如下:在1023这个数字之内,任何一个数目都可以由这十只盒子里的几只组合相加而成。那么1
、2、4、8、16、32、64、128、256、512这个序列为什么有这么个魔力?这个数列的特点:1、每项是后一项的二倍
,2、每项都比前面所有项的和大,而且大1。这个1就是关键,就因为这个1,它才可以按1递增,拼出总和之内任意
一个整数。这个序列叫做超递增序列,它是解决背包问题的基础。3、拼出总和之内任意一个整数可以由这个序列中
的一些数构成,且构成方法唯一,据说是密码学中的NP定理。譬如说这个数列总合中20这个数,只能由16+4一种方
法构成,由此延伸出来,如果综合中这个数据代表一个权值,我们可以解出它的所有构成参数(操作),如20这个
数据,我们可以挨个和序列中每一项按位与,得出来如果不等于0,就说明他是由这个数构成的。
保存权值到int还是varchar对于我们来说是个问题,当然,保存字符串的好处是运算压力小。我们可能听过一个故
事,就是把这个超递增序列延伸到第64项,就是那个术士和皇帝在国际象棋棋盘上要米粒的传说。64项的和是一个
天文数字!但计算机本身就是一个只认识二进制的机器!很多程序员整天只关心架构,甚至不知道或者不关心位操
作是什么玩意,当然我们有朋友担心数据库的int不够长,那么既然可以保存一个只有0、1组成的varchar字符串,
为什么不能保存一个十六进制的字符串,有人规定varchar只能保存01吗?十六进制串的长度正好是二进制的四分之
一。
由此我们可以无限制的扩展权值操作。
在最近的项目里,我对权限的控制分成两个部分,第一就是用户体验上,我设置了一个权限标签,从数据库中抽取
权限信息,然后做到标签里,也凑或算成是界面AOP了,第二就是底层的拦截,用了Spring 的AOP,为的是防止权限
冲突,双管齐下。暂时解决权限所需,另外在算法上我用了16进制的权限判别代码,虽然配置较麻烦,写完代码还
要写文档说明,不过也解决了权限繁杂又多的问题,暂时就这样了,嘿嘿,以后有空再研究。
_________________________________________________
首先上文权限设计拙见(1)中只是想记录下自己权限设计上的一点看法,以及将自己日常最常用的权限解决方案记
录下来以供日后回顾,没想到有朋友关注此类的设计,那就只能先把代码拿出来献丑了,抛砖引玉,大家共同探讨
学习
接着上文来说,上文所讨论的权限设计是一条思路,但既然是web应用,少不了数据库的支持,本文我们来讨
论一下数据库的设计。(以下想法及思路仅仅代表本人拙见)
说到权限的数据库设计,必先理清权限中几种实体及其关系,此部分想必有过设计权限经验的同仁都知道怎
么设计了,网上摆渡一下也是一裤衩子一裤衩子的,我们就在最平凡直观的数据库关系的基础上来建立权限。下面
是我的几个表(所有的表都带有一个pk_id,作为表的自动生成的唯一主键):
用户表(T_UserInfo):
1/**//*==============================================================*/
2/**//* Table: T_UserInfo */
3/**//*==============================================================*/
4create table T_UserInfo
5(
6 pk_id NUMBER not null,
7 name VARCHAR2(20),
8 sex BOOLEAN,
9 age int,
10 emp_num NUMBER,
11 polity int,
12 unit VARCHAR2(50),
13 department VARCHAR2(20),
14 specialty int,
15 position VARCHAR2(10),
16 offtel VARCHAR2(20),
17 famtel VARCHAR2(20),
18 post_state VARCHAR2(10),
19 remark VARCHAR2(100),
20 constraint PK_T_USERINFO primary key (pk_id)
21);用户表就不多说了,都是一些常用字段,年龄、电话、职位等,建议大家建立一个通用一些,字段多一些的一
个用户表,便于以后扩展,以后如果有特殊需求,不用扩这个基本表,可以通过主外键关系来新建一个表,用于扩
充字段
角色表(T_RoleInfo):
1/**//*==============================================================*/
2/**//* Table: T_RoleInfo */
3/**//*==============================================================*/
4create table T_RoleInfo
5(
6 pk_id number not null,
7 role_name VARCHAR2(20),
8 role_desc VARCHAR2(100),
9 parent_role_id NUMBER,
10 constraint PK_T_ROLEINFO primary key (pk_id)
11);角色表中需要说明的就一个parent_role_id父角色id,此字段用来扩展角色的继承关系。
资源表(T_ResourceInfo):
1/**//*==============================================================*/
2/**//* Table: T_ResourceInfo */
3/**//*==============================================================*/
4create table T_ResourceInfo
5(
6 pk_id NUMBER not null,
7 module_name VARCHAR2(20),
8 module_code VARCHAR2(10),
9 module_desc VARCHAR2(100),
10 privilege_name VARCHAR2(10),
11 privilege_code CHAR,
12 privilege_desc VARCHAR2(100),
13 constraint PK_T_RESOURCEINFO primary key (pk_id)
14);
15这个表需要说明的就比较多了,首先该表用来记录资源与资源权限,我这边所谓的资源就是实体,就是数据库表
,角色需要对应到资源,有些角色对该资源有权限,有些角色则对该资源无权限,角色可对此资源操作的权限也不
同。说白了,就是不同的角色对不同的数据库表的操作权限不同。因此我们这里的资源就是数据库表。
module_name:资源名;module_code:资源代码(存放数据库表名);
privilege_name:权限名;privilege_code:权限代码(代表权限的code,也就是我们上文所说的权值)
例如角色a对数据库表T_UserInfo有添加与删除的权限则该表应该按照如下配置:
module_name:人员信息;
module_code:T_UserInfo
privilege_name:添加与删除
privilege_code:6
这里我们假设的是2的0次方为添加权限,2的1次方为添加权限,2的2次方为删除权限,2的3次方为更新权限,则拥
有添加与删除权限就应该为2的1次方+2的2次方=6,其实2的几次方代表什么含义我们可以另外开个数据库表来配置
(或者xml文件)此处我们忽略这些步骤。当然如果你的权限较多,譬如你还希望a这个角色对人员信息表有上传得
权限,我们可以将将上传权限定义为2的4次方,16,16的16进制数为10,记录在数据库里的形式应该为0x10如果a角
色拥有添加、删除、更新、上传权限,则a的权值应该为2的1次方+2的2次方+2的3次方+2的4次方=30,用16进制来表
示就应该为0x1E,记录16进制数据,你不用担心位数不够。
剩余的就是几张关系表了:
人员角色关系表(T_R_User_Role):
1/**//*==============================================================*/
2/**//* Table: T_R_user_role */
3/**//*==============================================================*/
4create table T_R_user_role
5(
6 pk_id NUMBER not null,
7 user_id NUMBER,
8 role_id NUMBER,
9 constraint PK_T_R_USER_ROLE primary key (pk_id)
10);
11角色资源关系表(T_R_Role_Resource)
1/**//*==============================================================*/
2/**//* Table: T_R_role_resource */
3/**//*==============================================================*/
4create table T_R_role_resource
5(
6 pk_id NUMBER not null,
7 role_id NUMBER,
8 res_id NUMBER,
9 constraint PK_T_R_ROLE_RESOURCE primary key (pk_id)
10);
11当然如果你不怕麻烦,可以添加进去组(group)、系统(system)、组织(organization),建立起一套属于你自己的
完整的权限解决方案,作为系统无关的模块去套用到每个你所架构的应用中去,那是一件极爽的事情。
连续剧开始了,暂时搁笔~~
__________________________________________________________________________
在上文中我们提到了一个资源对应一个数据库表,在T_ResourceInfo表中我们也提到了有一个字段专门来记录表名
,然后我书写一个资源配置文件,用来配置我的业务类与资源的对应关系,代码如下:
1<?xml version="1.0" encoding="GB2312"?>
2<data>
3 <mapping SysName="s">
4 <module BusinessClass="com.ideal.framework.business.businessface.IBLogin"
TableName="user_info"/>
5 </mapping>
6</data>其中BusinessClass代表业务接口,TableName代表该业务接口所要操作的数据实体(数据表),此处的
TableName必须与T_ResourceInfo中的Module_Code一致。
用户登录后,需要操作T_UserInfo这个表时,我们的逻辑将会把请求带入IBLogin这个业务逻辑中,在我们的AOP模
块中,可以用MethodInterceptor来截获当前用户想要操作的业务逻辑,当AOP模块截获了用户的请求,并判断用户
想要操作IBLogin这个业务逻辑,它将在上述的mapping文件中去找该业务逻辑对应的资源user_info,然后去资源表
中判断该用户是否有操作user_info的权限。
(注:上述xml文件在系统初始化时候加载入内存中,我们也可以将权限信息也加载在内存中,不会很大,一切资源
在内存中操作,非常快)
下面我贴点代码,在系统初始化时:
1package com.ideal.framework;
2
3import java.util.*;
4import java.sql.*;
5import com.ideal.framework.dao.daoface.*;
6import com.ideal.framework.po.*;
7
8public class ResourceContainer
9{
10 public static boolean change_resource; //更新资源 系统持久
11 public static Vector resource_container = new Vector(); //资源容器 用户持久
12 private IUserRoleDAO m_user_role_dao;
13 private IRoleResourceDAO m_role_resource_dao;
14 private IUserDAO m_user_dao;
15
16 public ResourceContainer()
17{
18 }
19
20 public void setUserResource()
21 {
22 System.out.println("initialize resource:");
23 List user_list = m_user_dao.getAllUser();
24 for (int i = 0; i < user_list.size(); i++)
25 {
26 UserInfo user = (UserInfo) user_list.get(i);
27 List role_list = m_user_role_dao.getRoleInfo(user);
28 for (int j = 0; j < role_list.size(); j++)
29 {
30 RoleInfo role = (RoleInfo) role_list.get(j);
31 List resource_list = m_role_resource_dao.
32 getResourceInfo(role);
33 for (int k = 0; k < resource_list.size(); k++)
34 {
35 Hashtable hash = new Hashtable();
36 hash.put(user.getLoginId(), resource_list.get(k));
37 hash.put("Unit_"+user.getLoginId(), user.getUnit());
38 hash.put("Role_"+user.getLoginId(), role.getRoleName());
39 ResourceContainer.resource_container.add(hash);
40 }
41 }
42 }
43 }
44
45 public Vector getResource_container()
46 {
47 return resource_container;
48 }
49
50 public void setResource_container(Vector resource_container)
51 {
52 this.resource_container = resource_container;
53 }
54
55 public IRoleResourceDAO getM_role_resource_dao()
56 {
57 return m_role_resource_dao;
58 }
59
60 public IUserDAO getM_user_dao()
61 {
62 return m_user_dao;
63 }
64
65 public IUserRoleDAO getM_user_role_dao()
66 {
67 return m_user_role_dao;
68 }
69
70 public void setM_role_resource_dao(IRoleResourceDAO m_role_resource_dao)
71 {
72 this.m_role_resource_dao = m_role_resource_dao;
73 }
74
75 public void setM_user_dao(IUserDAO m_user_dao)
76 {
77 this.m_user_dao = m_user_dao;
78 }
79
80 public void setM_user_role_dao(IUserRoleDAO m_user_role_dao)
81 {
82 this.m_user_role_dao = m_user_role_dao;
83 }
84
85 public void setChange_resource(boolean change_resource)
86 {
87 this.change_resource = change_resource;
88 }
89
90 public boolean isChange_resource()
91 {
92 return change_resource;
93 }
94}
95将用户对应的角色,资源信息加载如内存,另外在初始化时候的xml文件的树形结构也加载入内存,这边就不贴代
码了
下面是AOP模块的advice代码:
package com.ideal.framework.sys.advice;
/** *//**
* <p>Title: BusinessAccessAdvisor</p>
* <p>Description: 业务模块AOP权限监听器</p>
* <p>Copyright: Copyright (c) 2006</p>
* <p>Company: ideal</p>
* @author alex
* @version 1.0
*/
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import com.ideal.framework.InitResource;
import com.ideal.framework.util.XMLUtil;
import java.util.ArrayList;
import java.util.Hashtable;
import com.ideal.framework.sys.accesscontrol.GenericAccessBase;
import java.lang.reflect.Field;
import com.ideal.framework.po.*;
import java.lang.reflect.Method;
import java.util.*;
import java.io.*;
import javax.servlet.http.HttpServletRequest;
public class BusinessAccessAdvisor
implements MethodInterceptor
{
public BusinessAccessAdvisor()
{
}
public Object invoke(MethodInvocation invocation) throws
Throwable
{
String user_name = "";
Object obj = invocation.getArguments()[1];
if (obj instanceof HttpServletRequest)
{
HttpServletRequest request = (HttpServletRequest)obj;
user_name = (String)request.getSession().getAttribute("UserName");//取出用户名
}
String bean_name = invocation.getMethod().getDeclaringClass().getName();//取出用户想要操作的
业务逻辑
XMLUtil xml = (XMLUtil) InitResource.context.getBean("XMLUtil");
ArrayList list = xml.getFieldList("mapping", "s", xml.doc);
for (int i = 0; i < list.size(); i++)
{
Hashtable hash = (Hashtable) list.get(i);
if (hash.get("BusinessClass").equals(invocation.getMethod().//判断用户是否有权操作该业务
逻辑所对应表
getDeclaringClass().getName()))
{
String table_name = (String) hash.get("TableName");
GenericAccessBase access_controller = (GenericAccessBase)
InitResource.context.getBean("GenericAccessBase");
if (access_controller.CheckAccessPrivilege(user_name, table_name))//若用户有权操作该
表,则让程序进入业务逻辑
{
return invocation.proceed();
}
}
}
System.out.println("no permission .reject by " + bean_name);
return null;
}
}
下面是判断用户是否具有操作该表权限的类:
1package com.ideal.framework.sys.accesscontrol;
2
3import com.ideal.framework.InitResource;
4import com.ideal.framework.util.XMLUtil;
5import com.ideal.framework.po.UserInfo;
6import com.ideal.framework.ResourceContainer;
7import java.util.*;
8//import com.ideal.framework.po.ResourceInfo;
9
10public class GenericAccessBase
11{
12 UserInfo user;
13
14 public GenericAccessBase()
15 {
16 }
17
18 public void setUser(UserInfo user)
19 {
20 this.user = user;
21 }
22
23 public boolean CheckAccessPrivilege(String user_name, String table_name)
24 {
25 for (int i = 0; i < ResourceContainer.resource_container.size(); i++)
26 {
27 Hashtable temp_hash = (Hashtable)ResourceContainer.resource_container.get(i);//从内存中
取出用户资源信息
28 if (temp_hash.containsKey(user_name))
29 {
30 ResourceInfo resource = (ResourceInfo)temp_hash.get(user_name);
31 if (table_name.trim().toLowerCase().equals(resource.getModuleCode().trim
().toLowerCase()))//比对用户拥有的资源和当前的table_name
32 {
33 return true;
34 }
35 }
36 }
37 return false;
38 }
39}
40ok,到此为止,我们的底层拦截就完成了,接下来就是界面权限处理,界面权限比较复杂,因为用户可能具有添
加权限,没有上传权限,有下载权限却没有更新权限等,情况很复杂,所以我们这边必须有一个判断当前用户是否
具有这些复杂权限的类:
1package com.ideal.framework.sys.privilege;
2
3/** *//**
4 * <p>Title: GenericPrivilegeBase</p>
5 * <p>Description: 通用权限法则</p>
6 * <p>Copyright: Copyright (c) 2006</p>
7 * <p>Company: ideal</p>
8 * @author alex
9 * @version 1.0
10 */
11
12public class GenericPrivilegeBase
13{
14 public final static int NO_PRIVILEGE = 0;
15 public final static int QUERY_OR_USE_PRIVILEGE = 1;//察看权限
16 public final static int CREATE_PRIVILEGE = 2;//添加权限
17 public final static int DELETE_PRIVILEGE = 4;//删除权限
18 public final static int UPDATE_PRIVILEGE = 8;//更新权限
19 public final static int ALL_PRIVILEGE = QUERY_OR_USE_PRIVILEGE |
20 CREATE_PRIVILEGE | DELETE_PRIVILEGE | UPDATE_PRIVILEGE;//增删改查权限
21
22 public GenericPrivilegeBase()
23 {
24 }
25
26 public static boolean isValidPrivilege(int privilege)//判断是否具有权限
27 {
28 if ( (privilege & QUERY_OR_USE_PRIVILEGE) != 0)
29 {
30 return true;
31 }
32
33 if ( (privilege & CREATE_PRIVILEGE) != 0)
34 {
35 return true;
36 }
37
38 if ( (privilege & DELETE_PRIVILEGE) != 0)
39 {
40 return true;
41 }
42
43 if ( (privilege & UPDATE_PRIVILEGE) != 0)
44 {
45 return true;
46 }
47
48 return false;
49 }
50
51 public static boolean checkQueryPrivilege(int privilege)//判断是否具有察看权限
52 {
53 if ( (privilege & QUERY_OR_USE_PRIVILEGE) != 0)
54 {
55 return true;
56 }
57 else
58 {
59 return false;
60 }
61 }
62
63 public static boolean checkUsePrivilege(int privilege)
64 {
65 if ( (privilege & QUERY_OR_USE_PRIVILEGE) != 0)
66 {
67 return true;
68 }
69 else
70 {
71 return false;
72 }
73 }
74
75 public static boolean checkCreatePrivilege(int privilege)//判断是否有添加权限
76 {
77 if ( (privilege & CREATE_PRIVILEGE) != 0)
78 {
79 return true;
80 }
81 else
82 {
83 return false;
84 }
85 }
86
87 public static boolean checkDeletePrivilege(int privilege)//判断是否有删除权限
88 {
89 if ( (privilege & DELETE_PRIVILEGE) != 0)
90 {
91 return true;
92 }
93 else
94 {
95 return false;
96 }
97 }
98
99 public static boolean checkUpdatePrivilege(int privilege)
100 {
101 if ( (privilege & UPDATE_PRIVILEGE) != 0)
102 {
103 return true;
104 }
105 else
106 {
107 return false;
108 }
109 }
110}
111然后我们自定义两个标签,Privilege与noPrivilege用来判断用户是否具有权限,这两个标签必须具有三个基本
的attribute,beanName:当前所要操作的哪个资源;scope:用户信息存放在哪个域;operation:用户想要进行什
么操作
贴一个privilege标签的代码:
1package com.ideal.framework.tag;
2
3import javax.servlet.jsp.tagext.BodyTagSupport;
4import javax.servlet.jsp.tagext.*;
5import javax.servlet.http.*;
6import javax.servlet.jsp.*;
7import java.sql.*;
8import java.io.*;
9import com.ideal.framework.*;
10import com.ideal.framework.po.ResourceInfo;
11import java.util.Hashtable;
12import com.ideal.framework.sys.privilege.GenericPrivilegeBase;
13
14public class PrivilegeTag
15 extends BodyTagSupport
16{
17 String operation;
18 private String beanName;
19 private String scope;
20
21 public PrivilegeTag()
22 {
23 super();
24 }
25
26 public void setOperation(String operation)
27 {
28 this.operation = operation;
29 }
30
31 public void setBeanName(String beanName)
32 {
33 this.beanName = beanName;
34 }
35
36 public void setScope(String scope)
37 {
38 this.scope = scope;
39 }
40
41 public int doStartTag() throws JspTagException
42 {
43 if (scope == null || scope.equals(""))
44 return SKIP_BODY;
45 else
46 {
47 String user_name = "";
48 if (scope.equalsIgnoreCase("session"))
49 {
50 HttpSession session = pageContext.getSession();
51 user_name = (String) session.getAttribute("UserName");
52 }
53 else
54 {
55 HttpServletRequest request = (HttpServletRequest) pageContext.
56 getRequest();
57 user_name = (String) request.getAttribute("UserName");
58 }
59
60 for (int i = 0; i < ResourceContainer.resource_container.size(); i++)
61 {
62 Hashtable temp_hash = (Hashtable) ResourceContainer.
63 resource_container.get(i);
64 if (temp_hash.containsKey(user_name))
65 {
66 ResourceInfo resource = (ResourceInfo) temp_hash.get(
67 user_name);
68 if (beanName.trim().toLowerCase().equals(resource.
69 getModuleCode().trim().toLowerCase()))
70 {
71 if(this.checkPrivilege(resource.getPrivilegeCode()) == EVAL_BODY_TAG)
72 return EVAL_BODY_TAG;
73 }
74 }
75 }
76
77 }
78 return SKIP_BODY;
79return EVAL_BODY_TAG;
80 }
81
82 public int checkPrivilege(String privilege)
83 {
84 int int_privilege = 0;
85 try
86 {
87 int_privilege = Integer.parseInt(privilege);
88 }
89 catch (NumberFormatException ex)
90 {
91 System.out.println(ex.getMessage());
92 }
93 GenericPrivilegeBase gpb = new GenericPrivilegeBase();
94 if (operation.equals("NONE"))
95 return EVAL_BODY_TAG;
96 if (operation.equals("QUERY"))
97 if (gpb.checkQueryPrivilege(int_privilege))
98 return EVAL_BODY_TAG;
99 if (operation.equals("CREATE"))
100 if (gpb.checkCreatePrivilege(int_privilege))
101 return EVAL_BODY_TAG;
102 if (operation.equals("DELETE"))
103 if (gpb.checkDeletePrivilege(int_privilege))
104 return EVAL_BODY_TAG;
105 if (operation.equals("UPDATE"))
106 if (gpb.checkUpdatePrivilege(int_privilege))
107 return EVAL_BODY_TAG;
108 if (operation.equals("USE"))
109 if (gpb.checkUsePrivilege(int_privilege))
110 return EVAL_BODY_TAG;
111 return SKIP_BODY;
112 }
113
114 public int doAfterBody() throws JspTagException
115 {
116 return SKIP_BODY;
117 }
118
119 public int doEndTag() throws JspTagException
120 {
121 try
122 {
123 if (bodyContent != null)
124 {
125 bodyContent.writeOut(bodyContent.getEnclosingWriter());
126 }
127 }
128 catch (IOException ex)
129 {
130 throw new JspTagException("IO Error:" + ex.getMessage());
131 }
132 return EVAL_PAGE;
133 }
134
135 public void doInitBody() throws JspTagException
136 {
137 }
138
139 public void setBodyContent(BodyContent bodyContent)
140 {
141 this.bodyContent = bodyContent;
142 }
143}
144在页面上,我们如此使用该标签:
1<privilege beanName="user_info" scope="session" operation="create">
2 <input type="button" value="添加">
3</privilege>
如此,系统会自动根据当前session中的用户来判断是否需要显示当前的添加按钮。
到此所有权限的代码完成,在此套权限设计中,我始终抱着AOP的想法:让他属于一个系统切面,以后再开发其他系
统时,作为一个模块就可以加载上去,与系统无关