第 54 章 管理acl

这章介绍一些有关管理acl的内容,包括管理多个domain类和动态授权与收回授权。

54.1. 管理多个domain类

上一章中我们演示了如何使用自定义Voter对单个domain类进行权限控制,如果我们需要对多个domain类实现acl权限控制时,不必给每一个类都配置一个Voter,只需要定义一个接口,让所有需要进行acl权限控制的domain类实现这个接口,然后在Voter中配置这个统一接口就可以了。

示例中我们定义了一个AclDomainClass接口。

package com.family168.springsecuritybook.ch302;

public interface AclDomainClass {
}
        

然后让其他domain都实现这个接口。

public class Account implements Serializable, AclDomainClass {
    // ....
}
        

这样就可以在配置文件中配置统一的Voter。

<bean id="aclDeleteVoter" class="org.springframework.security.acls.AclEntryVoter">
    <constructor-arg ref="aclService"/>
    <constructor-arg value="ACL_DELETE"/>
    <constructor-arg>
        <list>
            <util:constant static-field="org.springframework.security.acls.domain.BasePermission.ADMINISTRATION"/>
            <util:constant static-field="org.springframework.security.acls.domain.BasePermission.DELETE"/>
        </list>
    </constructor-arg>
    <property name="processDomainObjectClass" value="com.family168.springsecuritybook.ch302.AclDomainClass"/>
</bean>
        

这样,aclDeleteVoter就可以处理所有实现了AclDomainClass接口的类,而在数据库中依然会保存实现类的具体类型,不会因为在Voter中使用了同一个接口类而造成影响。

54.2. 动态授权与收回授权

现在acl的好处是可以通过aclService自由管理acl和ace的信息,比如我们可以为其他人授权,让其他人可以查看自己的消息,也可以收回这些授权。

54.2.1. 获得对象的acl权限

可以通过domain类的类型和id,获得对应acl中的所有ace信息。

public void list(HttpServletRequest request,
    HttpServletResponse response) throws Exception {
    Long id = Long.valueOf(request.getParameter("id"));
    String clz = request.getParameter("clz");
    Acl acl = null;
    if (clz.equals("account")) {
        Account account = new Account();
        account.setId(id);
        acl = getAclService().readAclById(new ObjectIdentityImpl(account));
    } else if (clz.equals("contract")) {
        Contract contract = new Contract();
        contract.setId(id);
        acl = getAclService().readAclById(new ObjectIdentityImpl(contract));
    } else if (clz.equals("message")) {
        Message message = new Message();
        message.setId(id);
        acl = getAclService().readAclById(new ObjectIdentityImpl(message));
    }
    request.setAttribute("acl", acl);
    request.getRequestDispatcher("/permission-list.jsp").forward(request, response);
}
            

在jsp中使用如下方式显示ace中的权限实体以及对应的权限。

<c:forEach var="item" items="${acl.entries}">
      <tr>
        <td>${item.sid.principal} | ${item.permission.mask}&nbsp;</td>
        <td><a href="permission.do?action=remove&clz=${param.clz}&id=${param.id}&sid=${item.sid.principal}&permission=${item.permission.mask}">delete</a>&nbsp;</td>
      </tr>
</c:forEach>
            

ace列表如下图所示:

实现domain对象对应的所有ace信息

图 54.1. 实现domain对象对应的所有ace信息


user表示权限实体的名称,16表示administration权限。这里表示当前的对应已经赋予了user用户管理权限。

54.2.2. 添加授权

可以将一个domain对象的acl权限授予其他人。

可以选择授权的人或角色,然后选择授权的具体权限。

添加授权

图 54.2. 添加授权


提交后可以看到domain对象对应的acl权限增加了admin用户的read权限,这时admin用户就可以查看这个domain对象的而信息了。

对应代码如下所示:

@Transactional
public void addPermission(long id, String clz, String recipient,
    int mask) {
    PrincipalSid sid = new PrincipalSid(recipient);
    Permission permission = defaultPermissionFactory.buildFromMask(mask);

    ObjectIdentity oid = null;

    if (clz.equals("account")) {
        oid = new ObjectIdentityImpl(Account.class, id);
    } else if (clz.equals("contract")) {
        oid = new ObjectIdentityImpl(Contract.class, id);
    } else if (clz.equals("message")) {
        oid = new ObjectIdentityImpl(Message.class, id);
    }

    MutableAcl acl;

    try {
        acl = (MutableAcl) mutableAclService.readAclById(oid);
    } catch (NotFoundException nfe) {
        acl = mutableAclService.createAcl(oid);
    }

    acl.insertAce(acl.getEntries().size(), permission, sid, true);
    mutableAclService.updateAcl(acl);
}
            

54.2.3. 收回授权

可以将已经授权的权限删除。

对应代码如下:

@Transactional
public void deletePermission(long id, String clz, String recipient,
    int mask) {
    PrincipalSid sid = new PrincipalSid(recipient);
    Permission permission = defaultPermissionFactory.buildFromMask(mask);

    ObjectIdentity oid = null;

    if (clz.equals("account")) {
        oid = new ObjectIdentityImpl(Account.class, id);
    } else if (clz.equals("contract")) {
        oid = new ObjectIdentityImpl(Contract.class, id);
    } else if (clz.equals("message")) {
        oid = new ObjectIdentityImpl(Message.class, id);
    }

    MutableAcl acl = (MutableAcl) mutableAclService.readAclById(oid);
    List<AccessControlEntry> entries = acl.getEntries();

    for (int i = 0; i < entries.size(); i++) {
        if (entries.get(i).getSid().equals(sid)
                && entries.get(i).getPermission().equals(permission)) {
            acl.deleteAce(i);
        }
    }

    mutableAclService.updateAcl(acl);
}
            

介于权限主体和权限可能有多种组合,所以我们只删除两者都完全匹配的ace信息,操作成功后可以看到用户的acl权限已经被收回了。

实例在ch302。