自定义WebLogic Role Mapper Provider样例

【概述】

开发环境:WebLogic Server 8.1

本文仅记录了主要的操作流程,涉及到的一些概念性的东西,就不详细叙述。需要了解的话,可以参考其在线文档中心

 

【Role Mapper Provider简介】

在请求者与Weblogic 资源之间建立动态关联(比如将用户所属的用户组与某个role进行映射)。当一个Weblogic资源被访问时,WebLogic Security Framework通过以下两种方式来确定哪个security roles将应用到某个特定的Subject上:

l  J2EEWebLogic部署描述符文件中获得security roles

l  使用业务逻辑和当前操作参数来决定security roles

在一个security realm中,至少需要有一个Role Mapping Provider。

 

【开发流程】

    首先,需要一个weblogic.security.service.SecurityRole的实现类:

package examples.security.providers.rolemapper;

import weblogic.security.service.SecurityRole;

public class SampleSecurityRoleImpl implements SecurityRole {
	private String _roleName;
	private String _description;
	private int _hashCode;

	public SampleSecurityRoleImpl(String roleName, String description) {
		_roleName = roleName;
		_description = description;
		_hashCode = roleName.hashCode() + 17;
	}

	public boolean equals(Object secRole) {
		if (secRole == null) {
			return false;
		}
		if (this == secRole) {
			return true;
		}
		if (!(secRole instanceof SampleSecurityRoleImpl)) {
			return false;
		}
		SampleSecurityRoleImpl anotherSecRole = (SampleSecurityRoleImpl) secRole;
		if (!_roleName.equals(anotherSecRole.getName())) {
			return false;
		}
		return true;
	}

	public String toString() {
		return _roleName;
	}

	public int hashCode() {
		return _hashCode;
	}

	public String getName() {
		return _roleName;
	}

	public String getDescription() {
		return _description;
	}
}

 

 实现代码很简单,一个role基本上就包含名称和描述。

 

然后,为了简化起见,这里用一个类SampleRoleMapperDatabase来代替数据库里面存储的相关映射信息。实际应用中,可以改造这个类,让它读写数据库或其他存储设备。

 

package examples.security.providers.rolemapper;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Vector;

import weblogic.management.security.ProviderMBean;
import weblogic.security.spi.Resource;

public class SampleRoleMapperDatabase {
	private Map roleMap = new Hashtable();
	private Map principalMap = new Hashtable();

	public SampleRoleMapperDatabase(ProviderMBean mbean) {
	}

	public void setRole(Resource resource, String roleName,
			String[] principalNames) {
		Vector principalVector = (Vector) principalMap.get(roleName);
		if (principalVector == null) {
			principalVector = new Vector();
			principalMap.put(roleName, principalVector);
		}
		principalVector.addAll(Arrays.asList(principalNames));
		Vector roleVector = (Vector) roleMap.get(resource);
		if (roleVector == null) {
			roleVector = new Vector();
			roleMap.put(resource, roleVector);
		}
		roleVector.add(roleName);
	}

	public void removeRole(Resource resource, String roleName) {
		if (resource != null)
			principalMap.remove(roleName);
		if (roleName != null)
			roleMap.remove(resource);
	}

	public Enumeration getRoles(Resource resource) {
		if (resource == null)
			return null;
		if (roleMap.get(resource) != null)
			return Collections.enumeration((Vector) roleMap.get(resource));
		else
			return null;
	}

	public Enumeration getPrincipalsForRole(Resource resource, String role) {
		List pricipalList = new ArrayList();
		if (role != null)
			pricipalList.addAll((Vector) roleMap.get(role));
		if (resource != null) {
			Vector roleVector = (Vector) roleMap.get(resource);
			if (roleVector != null) {
				Iterator iter = roleVector.iterator();
				while (iter.hasNext()) {
					Vector principalVector = (Vector) principalMap
							.get((String) iter.next());
					pricipalList.addAll(principalVector);
				}
			}
		}
		return Collections.enumeration(pricipalList);
	}

}

 

接下来,就可以开始实现weblogic.security.spi.RoleMapper接口了。该接口只有一个方法:

public Map getRoles(Subject subject, Resource resource,
			ContextHandler handler)

 

给定一个登录用户的Subject,和其请求的资源,返回在该资源上分配给该用户的所有role,返回的对象是一个Map,key是对应role的名称,value则是weblogic.security.service.SecurityRole对象(也就是上文中我们自己写的那个实现类)。

 

另外,我们还可以同时实现weblogic.security.spi.DeployableRoleProvider接口,这样如果在web.xml中配置了某些角色,那么在服务器启动时,WebLogic Server会将这些信息注入到我们的这个实现类里面去。该接口有两个方法:

public void deployRole(Resource resource, String roleName,
			String[] principalNames) throws RoleCreationException;

public void undeployRole(Resource resource, String roleName)
			throws RoleRemovalException;

分别对应添加和删除映射关系,我们根据传入的参数,修改数据库或其他地方的持久化映射数据。

 

完整实现类代码如下:

package examples.security.providers.rolemapper;

import java.security.Principal;
import java.util.Collections;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;

import javax.security.auth.Subject;

import weblogic.management.security.ProviderMBean;
import weblogic.security.WLSPrincipals;
import weblogic.security.service.ContextHandler;
import weblogic.security.spi.DeployableRoleProvider;
import weblogic.security.spi.Resource;
import weblogic.security.spi.RoleCreationException;
import weblogic.security.spi.RoleMapper;
import weblogic.security.spi.RoleRemovalException;
import weblogic.security.spi.SecurityServices;

public final class SampleRoleMapperProviderImpl implements
		DeployableRoleProvider, RoleMapper {
	private String description;
	private SampleRoleMapperDatabase database;
	private static final Map NO_ROLES = Collections
			.unmodifiableMap(new HashMap(1));

	public void initialize(ProviderMBean mbean, SecurityServices services) {
		SampleRoleMapperMBean myMBean = (SampleRoleMapperMBean) mbean;
		description = myMBean.getDescription() + "\n" + myMBean.getVersion();
		database = new SampleRoleMapperDatabase(mbean);
	}

	public String getDescription() {
		return description;
	}

	public void shutdown() {
		System.out.println("SampleRoleMapperProviderImpl.shutdown");
	}

	public RoleMapper getRoleMapper() {
		return this;
	}

	public Map getRoles(Subject subject, Resource resource,
			ContextHandler handler) {
		System.out.println("SampleRoleMapperProviderImpl.getRoles");
		System.out.println("\tsubject\t= " + subject);
		System.out.println("\tresource\t= " + resource);
		Map roles = new HashMap();
		Set principals = subject.getPrincipals();
		for (Resource res = resource; res != null; res = res
				.getParentResource()) {
			getRoles(res, principals, roles);
		}
		getRoles(null, principals, roles);
		if (roles.isEmpty()) {
			return NO_ROLES;
		}
		return roles;
	}

	public void deployRole(Resource resource, String roleName,
			String[] principalNames) throws RoleCreationException {
		System.out.println("SampleRoleMapperProviderImpl.deployRole");
		System.out.println("\tresource\t\t= " + resource);
		System.out.println("\troleName\t\t= " + roleName);
		for (int i = 0; principalNames != null && i < principalNames.length; i++) {
			System.out.println("\tprincipalNames[" + i + "]\t= "
					+ principalNames[i]);
		}
		database.setRole(resource, roleName, principalNames);
	}

	public void undeployRole(Resource resource, String roleName)
			throws RoleRemovalException {
		System.out.println("SampleRoleMapperProviderImpl.undeployRole");
		System.out.println("\tresource\t= " + resource);
		System.out.println("\troleName\t= " + roleName);
		database.removeRole(resource, roleName);
	}

	private void getRoles(Resource resource, Set principals, Map roles) {
		for (Enumeration e = database.getRoles(resource); e != null
				&& e.hasMoreElements();) {
			String role = (String) e.nextElement();
			if (roleMatches(resource, role, principals)) {
				roles.put(role, new SampleSecurityRoleImpl(role,
						"no description"));
			}
		}
	}

	private boolean roleMatches(Resource resource, String role,
			Set principalsHave) {
		for (Enumeration e = database.getPrincipalsForRole(resource, role); e
				.hasMoreElements();) {
			String principalWant = (String) e.nextElement();
			if (principalMatches(principalWant, principalsHave)) {
				return true;
			}
		}
		return false;
	}

	private boolean principalMatches(String principalWant, Set principalsHave) {
		if (WLSPrincipals.getEveryoneGroupname().equals(principalWant)
				|| (WLSPrincipals.getUsersGroupname().equals(principalWant) && !principalsHave
						.isEmpty())
				|| (WLSPrincipals.getAnonymousUsername().equals(principalWant) && principalsHave
						.isEmpty())
				|| principalsContain(principalsHave, principalWant)) {
			return true;
		}
		return false;
	}

	private boolean principalsContain(Set principalsHave,
			String principalNameWant) {
		for (Iterator i = principalsHave.iterator(); i.hasNext();) {
			Principal principal = (Principal) i.next();
			String principalNameHave = principal.getName();
			if (principalNameWant.equals(principalNameHave)) {
				return true;
			}
		}
		return false;
	}

}

 

initialize()方法在WebLogic Server启动时会被自动调用,在这里我们可以根据需要进行一些初始化操作。

 

代码写完了,接下来,就要使用WebLogic提供的命令行工具来生成一个mbi文件,这个文件在后面往WebLogic Server里面部署我们的Provider时需要用到。

 

为了告诉WebLogic如何生成我们的这个mbi文件,我们需要编写一个xml文档。

SampleRoleMapper.xml内容如下:

<?xml version="1.0" ?>
<!DOCTYPE MBeanType SYSTEM "commo.dtd">
<MBeanType
	Name="SampleRoleMapper"
	DisplayName="自定义RoleMapper"
	Package="examples.security.providers.rolemapper"
	Extends="weblogic.management.security.authorization.DeployableRoleMapper"
	PersistPolicy="OnUpdate"
	Description="This MBean represents configuration attributes for the Sample RoleMapper Provider.&lt;p&gt;"
>
	<MBeanAttribute
		Name="ProviderClassName"
		Type="java.lang.String"
		Writeable="false"
		Default="&quot;examples.security.providers.rolemapper.SampleRoleMapperProviderImpl&quot;"
		Description="The name of the Java class that my first role mapper provider." />
	<MBeanAttribute
		Name="Description"
		Type="java.lang.String"
		Writeable="false"
		Default="&quot;Provider that performs role mapper&quot;"
		Description="None." />
	<MBeanAttribute
		Name="Version"
		Type="java.lang.String"
		Writeable="false"
		Default="&quot;1.0&quot;"
		Description="The version of the Sample Role Mapper Provider." />
</MBeanType>

 MBeanType标签的Name属性,跟后面自动生成的java文件名有关,这里不可以命名成跟上文中我们自己写的Provider实现类同名;DisplayName随便写;Package跟上文相关类的包名保持一致即可;Extends属性比较关键,根据WebLogic Server的API来,只不过API里面的类名结尾有个MBean,这边去掉这个后缀即可。

 

三个MBeanAttribute标签的Name是固定的,直接拷贝。当然Name为ProviderClassName的这个MBeanAttribute要修改一下Default属性,改为我们自己的那个Provider实现类的全类名,注意要用转义的双引号(&quot;)括起来。

 

比较关键的修改就这些,其他属性看着改就好了。特别需要强调的是,这个xml文件比较关键,稍微错一点,后面代码生成就会出错。

 

编写完这个xml文件(MBeanType定义文件)后,还需要拷贝一个commo.dtd文件到相同目录,这个文件在C:\bea\weblogic81\server\lib目录可以找到。

运行cmd命令,打开命令行窗口。先运行:C:\bea\weblogic81\server\bin\setWLSEnv.cmd进行环境变量设置。

然后运行以下命令,生成mbi文件:

java -DMDF=C:\bea\user_projects\applications\Test3\Test3Web\WEB-INF\src\examples\security\providers\rolemapper\SampleRoleMapper.xml -Dfiles=D:\temp\0209 -DcreateStubs=true -cp .;%CLASS_PATH%;C:\bea\weblogic81\server\lib\weblogic.jar;C:\bea\weblogic81\server\lib\mbeantypes\wlManagement.jar;C:\bea\jdk142_08\lib\tools.jar weblogic.management.commo.WebLogicMBeanMaker

其中-DMDF参数指定SampleRoleMapper.xml文件位置;-Dfiles参数指定要生成的文件存放位置,这里我指定了一个临时目录;注意需要将weblogic.jar、wlManagement.jar和对应jdk的tools.jar文件在classpath中明确指定。

 

如果一切顺利的话,WebLogicMBeanMaker工具将生成两个java文件和一个mbi文件。如果上述SampleRoleMapper.xml文件中稍有错误(比如少了一个&quot;),过程中就会提示编译错误,其实是该工具根据我们编写的xml自动生成的java文件出现编译错误。此时应该仔细检查xml文件,甚至需要重新编写一次该文件(有一次我就遇到怎么查也没错,把文件删了重写一遍就好了)。

 

文件生成完了之后,接下来就是打包发布了。WebLogic也有提供相应工具,不过我还是自己用ant做比较方便。把两个java文件拷贝到eclipse里面,跟自己写的那三个文件放一起。另外再找个目录放生成的mbi文件。

 

然后编写一个ant脚本来打包发布jar文件,方便后续修改代码进行调试。

<?xml version="1.0" encoding="UTF-8"?>
<project name="Test3" default="自定义授权Provider">
	<property name="classBase" value="${basedir}/Test3Web/WEB-INF/classes"/>
	<property name="mbiBase" value="${classBase}/examples/security/providers/mbi"/>
	<property name="mbeantypesBase" value="C:/bea/weblogic81/server/lib/mbeantypes"/>
    <description>
		WebLogic Security Provider 自动打包发布
    </description>

        <target name="自定义RoleMapperProvider">
    	<jar destfile="${mbeantypesBase}/myRoleMapperProvider.jar">
	    	<fileset 
	    		dir="${classBase}" 
	    		includes="examples/security/providers/rolemapper/,examples/util/"/>
			<fileset file="${mbiBase}/SampleRoleMapper.mbi"/>
    	</jar>
    </target>

</project>

 

这里我们把用到的class文件和mbi文件打包后,直接拷贝到C:/bea/weblogic81/server/lib/mbeantypes目录中去,这样就完成了部署操作。

 

接下来,启动WebLogic服务器,打开浏览器,进入http://localhost:7001/console控制台页面,登录后就能看到我们自己的这个provider了。

 

配置完了以后,重启WebLogic服务器,即可生效。

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值