用户访问控制(Access control )机制总是围绕粗粒度和细粒度两个方面来讨论
粗粒度控制可以规定访问整个对象或对象群的某个层而细粒度控制则总是在方法或属性层进行控制比如
允许一个文件为只读是属于粗粒度控制而允许对这个文件某行有写操作则属于细粒度控制
一个好的用户控制机制当然既允许粗粒度也允许细粒度控制在Jive中我们看到是使用Proxy来达到这个目的但是我们也发现由于需要对每个类都要进行细粒度控制所以必然对每个类都要做一个Proxy类这样带来了很多Proxy类如ForumProxy ForumThreadProxy ForumFactoryProxy等无形增加了系统复杂性
使用动态Proxy可以很好的解决这个问题再结合javasecurityacl的ACL机制我们就可以灵活地实现粗粒度和细粒度的双重控制
当一个用户login后我们就要在内存中为其建立相应的授权访问机制使用javasecurityacl可以很方便的建立这样一个安全系统
首先任何一个对象都应该有个基本属性拥有者 或拥有者所属组(Windows中每个目录安全描述符都由部分构成对象的创建者对象所属的组自由存取控制和系统存取控制)
Java acl开始第一步是建立一个主体 Principal其中SecurityOwner是主体的拥有者 private static final Principal _securityOwner = new PrincipalImpl(SecurityOwner);
当用户login进来时他带有两个基本数据访问密码和他要访问的对象ApplicationName首先验证用户名和密码然后从数据库中取出其权限数据建立Permission这里使用Feature继承了Permission在Feature中定义了有关权限的细节数据(如读 写 删)
// 取出用户和被访问对象之间的权限关系这种权限关系可能不只一个也就是说用户
//可能对被访问对象拥有读 写 删等多个权限将其打包在Hasbtable中
Hashtable features = loadFeaturesForUser(sApplicationName sUserID);
创建一个用户对象
User user = new UserImpl(sUserID new Hashtable() );
为这个用户创建一个活动的acl entry
addAclEntry( user features);
其中最关键的是第四步addAclEntry我们看看其如何实现的
// 为这个用户创建一个新的Acl entry
AclEntry newAclEntry = new AclEntryImpl( user);
//遍历Hashtable features将其中多种权限加入:
feature = (Feature) hFeaturesget(keyName);
newAclEntryaddPermission( feature );
最后也要加入主体拥有者SecurityOwner
这样一个安全体系就已经建立完成
当你在系统中要检验某个用户使用拥有某个权限如读的权利时只要
aclcheckPermission(user feature )就可以acl是ACL的一个实例这样权限检查就交给
javasecurityaclACL 去处理了
有了ACL机制后我们就可以在我们系统中使用动态Proxy模式来对具体对象或方法进行控制比如我们有一个Report类有些用户可以读有些用户可以写(哪些用户可以读 哪些用户可以写已经在上面ACL里部署完成)
从Java 开始 Sun提供了Dynamic Proxy API为了使动态Proxy能够工作第一你必须有一个Proxy接口还要有一个继承InvocationHandler的Proxy类
在下面的例子中我们设定有三种用户普通人雇员经理权限是这样普通人可以读报告雇员和经理可以修改报告
按通常思维我们对于读权限我们设计一个具备读的角色类
public interface IpersonRO {
public String getName();
public String getAddress();
public String getPhoneNumber();
}
类里面都是读的方法这是一种粗粒度访问控制也就是说把读写权限只落实到类(对象)上这样的话我们还要为写的角色再建一个类很显然这不是一个很好的方法使用动态proxy+acl就可以实现很好的细粒度控制
public class ReportProxy implements InvocationHandler
{
private Map map;
public static Object newInstance(Map mapClass[] interfaces)
{
return ProxynewProxyInstance(mapgetClass()getClassLoader() interfacesnew ReportProxy(map));
}
public ReportProxy(Map map)
{
thismap = map;
}
public Object invoke(Object proxy Method m Object[] args) throws Throwable
{
Object result;
String methodName = mgetName();
if (methodNamestartsWith(get))
{
if (!aclcheckPermission(user read)) return null;
String name = methodNamesubstring(
methodNameindexOf(get)+);
return mapget(name);
}
else if (methodNamestartsWith(set))
{
if (!aclcheckPermission(user write)) return null;
String name = methodNamesubstring(
methodNameindexOf(set)+);
mapput(name args[]);
return null;
}
else if (methodNamestartsWith(is))
{
if (!aclcheckPermission(user read)) return null;
String name = methodNamesubstring(
methodNameindexOf(is)+);
return(mapget(name));
}
return null;
}
}