jackrabbit security主要用来验证登录用户的合法性,并针对相关内容节点来对用户进行维权操作
security信息配置在repository.xml文件当中,通过<security>标签来进行指定
<!ELEMENT Security (SecurityManager?, AccessManager?, LoginModule?)>
由DTD描述可以看出,security的配置主要包含了3方面的信息内容,分别是:SecurityManager、AccessManager和LoginModule.
一、SecurityManager:对Repository实例进行安全管理
xml描述:<SecurityManager class="org.apache.jackrabbit.core.DefaultSecurityManager" workspaceName="security">
其class属性所对应的java类一定要实现JackrabbitSecurityManager接口,该接口对外声明了以下几个方法:
getAccessManager():返回AccessManager用于权限管理;
getPrincipalManager():返回PrincipalManager用于principal的查询操作;
getUserManager():返回UserManager用于用户信息的管理;
getUserId():返回当前登录用户的userId。
Jackrabbit Security API提供了3个类实现了该接口,分别是:
SimpleSecurityManager:只是简单实现了接口定义,无法通过它获取UserManager来对用户信息进行管理,因此,在使用该类的情况下,用户信息一般存储于其他物理媒介当中(数据库),而不是Jackrabbit里;
DefaultSecurityManager:将用户信息存储到统一的workspace当中以达到所有workspace共享;
UserPerWorkspaceSecurityManager:将用户信息存储到自己单独的workspace当中,其他的workspace不能共享。
UserManager的配置通过<SecurityManager>的子标签<UserManager>来完成
<!ELEMENT SecurityManager (WorkspaceAccessManager?,UserManager?,UserIdClass?, param*)>针对不同的SecurityManager(DefaultSecurityManager | UserPerWorkspaceSecurityManager),jackrabbit声明了两种类型的UserManager实现,分别是UserManagerImpl和UserPerWorkspaceUserManager,用于满足不同情况下的用户管理需求。基本配置信息如下(针对jackrabbit2.*版本):
<UserManager class="org.apache.jackrabbit.core.security.user.UserManagerImpl"><!--UserManager实现-->
<param name="usersPath" value="/rep:security/rep:authorizables/rep:users"/><!--默认配置-->
<param name="groupsPath" value="/rep:security/rep:authorizables/rep:groups"/><!--默认配置-->
<!--如果该属性不设置或指定为false,则小于2.0版本的jackrabbit所创建的users和groups将不会被遍历到-->
<param name="compatibleJR16" value="false"/>
<!--所创建的用户节点距离用户根节点的层级数,默认为2级-->
<param name="defaultDepth" value="2"/>
<!--存储容量是否自动扩充,默认为false-->
<param name="autoExpandTree" value="true"/>
<!--只有当autoExpandTree为true时其作用,表示每次扩充的容量,默认为1000-->
<param name="autoExpandSize" value="1000"/>
</UserManager>
二、LoginModule:验证用户登录信息Jackrabbit默认使用JAAS服务来验证登录用户的合法性,JAAS服务主要通过javax.security.auth.spi.LoginModule接口来处理验证业务逻辑,而Jackrabbit声明了AbstractLoginModule抽象类实现了该接口,子类只需要覆盖其相关方法便可完成验证逻辑的处理
方法包括:
getAuthentication():返回登录用户的Authentication
getPrincipal():返回登录用户的Principal
针对不同的SecurityManager(SimpleSecurityManager | DefaultSecurityManager),jackrabbit声明了两种AbstractLoginModule的实现,分别是:SimpleLoginModel和DefaultLoginModel。
SimpleLoginModel:无论登录用户是否存在,其getAuthentication方法都会新建一个Authentication对象来作为结果返回,换个角度说SimpleLoginModel是不处理登录用户是否存在的验证逻辑的。因为在使用SimpleSecurityManager的时候,用户的个人信息是不存储到Jackrabbit里面的,这样无法通过UserManager来检索用户信息,所以判断用户是否存在的操作只能是独立于Jackrabbit之外来完成。
DefaultLoginModel:通过UserManager遍历检索登录用户,并返回其对应的Authentication和Principal,如果用户不存在或证书错误,则抛出javax.jcr.LoginException异常。基本配置如下:
<LoginModule class="org.apache.jackrabbit.core.security.authentication.DefaultLoginModule">
<!--anonymous user name ('anonymous' is the default value)-->
<param name="anonymousId" value="anonymous"/>
<!--administrator user id (default value if param is missing is 'admin')-->
<param name="adminId" value="zhangsan"/>
</LoginModule>
三、AccessManager:处理登录用户的授权操作同LoginModel的配置类似,针对不同的SecurityManager,jackrabbit声明了两种类型的AceessManager。
SimpleAccessManager:授权操作独立于Jackrabbit之外完成;
DefaultAccessManager:将授权操作下发给AccessControlPolicy。AccessControlPolicy的获取通过AccessControlProvider来实现。
jackrabbit提供了3中类型的AccessControlProvider,分别是:
(1)Resource-based(基于节点授权方式):
对应的java类: org.apache.jackrabbit.core.security.authorization.acl.ACLProvider
使用前提:1、节点有rep:AccessControllable引入类型;2、节点有rep:policy子节点,且子节点的类型为rep:ACL,rep:policy节点主要用来存储其父节点的访问权限控制列表(ACL),为便于理解,可把该列表看成是以下形式的表格:
表格的行 为用户标识;表格的列为权限信息;表格的内容表示授权情况(0表示未授权,1表示已授权)整个授权操作是基于节点来完成的(Resource-based),并且节点的访问权限具有继承关系(子节点具有父节点的访问权限).
(2)Principal-based(基于用户授权)
对应的java类:org.apache.jackrabbit.core.security.authorization.principalbased.ACLProvider
不同于Resource-based授权方式,Principal-based所声明的访问权限列表是基于用户的,即在用户节点下面存储一个子节点来用于声明该用户可以对哪些节点执行哪些操作。同样,为便于理解,可把该列表看成以下形式的表格:
表格的行 为节点标识,表格的列为权限信息,表格的内容表示授权情况(0表示未授权,1表示已授权)
(3)Combined(混合授权模式):
对应的java类:org.apache.jackrabbit.core.security.authorization.combined.CombinedProvider
基于以上两种方式的混合授权
AccessControlProvider的配置在workspace.xml文件中进行描述
<WorkspaceSecurity>
<AccessControlProvider class="org.apache.jackrabbit.core.security.authorization.acl.ACLProvider" />
</WorkspaceSecurity>
注意:从jackrabbit 2.0版本开始,授权机制默认基于Resource-based方式实现,即在不指定AccessControlProvider的情况下,jackrabbit默认使用org.apache.jackrabbit.core.security.authorization.acl.ACLProvider
基于以上描述,一个简单的Security配置模版大概如下:
<Security appName="Jackrabbit">
<!--使用DefaultSecurityManager,用户信息统一存储到"security" workspace中-->
<SecurityManager class="org.apache.jackrabbit.core.DefaultSecurityManager" workspaceName="security">
<UserManager class="org.apache.jackrabbit.core.security.user.UserManagerImpl"/>
</SecurityManager>
<!--由于使用了DefaultSecurityManager,AccessManager和LoginModule要选择对应的DefaultAccessManager
和DefaultLoginModule-->
<AccessManager class="org.apache.jackrabbit.core.security.DefaultAccessManager"/>
<LoginModule class="org.apache.jackrabbit.core.security.authentication.DefaultLoginModule">
<param name="anonymousId" value="anonymous"/>
<!--管理员用户ID,默认为admin-->
<!--使用管理员用户id登录时,如果jackrabbit中不存在该用户节点则自动创建-->
<param name="adminId" value="zhangsan"/>
</LoginModule>
</Security>
完成以上配置之后,可通过如下代码完成指定节点的授权操作
假定系统中存在test节点,满足授权操作的前提条件:
节点有rep:AccessControllable引入类型
节点有rep:policy子节点,且子节点的类型为rep:ACL
//首先获取AccessManager实例,即上面xml所配置的DefaultAccessManager对象
AccessControlManager acm=session.getAccessControlManager();
//AccessManager会将授权操作下发给AccessControlPolicy
AccessControlPolicy acp=acm.getPolicies(testNode.getPath())[0];
//将AccessControlPolicy转换成JackrabbitAccessControlList类型
JackrabbitAccessControlList jacl=(JackrabbitAccessControlList)acp;
//调用JackrabbitAccessControlList类的addEntry方法,为节点添加授权记录
jacl.addEntry(principal,Privilege[], isAllow, restrictions);
//最后调用setPolicy方法,完成授权操作
acm.setPolicy(testNode.getPath(), jacl);
//调用session.save()方法将授权信息持久化到存储层
session.save();
addEntry()方法参数描述如下:
其中principal表示用户,可通过PrincipalManager获取
PrincipalManager pMgr = ((JackrabbitSession) session).getPrincipalManager();
Principal principal=pMgr.getPrincipal("xiaoliu");
privilege表示权限集合,权限可通过如下方法获取
Privilege writePrivilege=acm.privilegeFromName(Privilege.JCR_WRITE);
Privilege readPrivilege=acm.privilegeFromName(Privilege.JCR_READ_ACCESS_CONTROL);
isAllow表示是授权操作还是权限的移除操作restrictions表示约束,如果没有约束可传一个空Map