文章
网格计算
作者:金辉 (Oracle)
第 1 章 — 漫说内存网格
第 2&3 章 — 安装和配置开发环境
第 4 章 — 开始第一个 Coherence 程序
第 5 章 — 在 Coherence 中使用 POF 对象
第 6 章 — 分布式内存集群的数据灌注、查询和统计
第 7 章 — 监听器处理内存集群中变化的对象
第 8 章 — 使用 ZFS 加密
第 9 章 — 内存集群与数据库的互操作
第 10 章 — Coherence 内存集群的安全访问
第 11 章 — 基于内存集群的事件处理
本章练习介绍客户端如何安全的访问内存集群,演示使用如下 API
Coherence*Extend 机制运行大范围的访问 Coherence 缓存。包含桌面应用,远程服务器和跨越广域网的机器间连接。
Coherence*Extend 由运行在集群外部的客户端和集群内部的代理服务构成,运行在一个或多个缓存服务器上。客户端API路由所有请求到代理,代理响应客户请求授权访问集群各服务,如 partitioned 或 replicated cache service 或 invocation service。
由于 extend 的客户端在集群之外,如何安全的访问集群是个大问题。本节介绍三种技术,基于口令、授权服务和调用服务。
更多的安全技术不在此处详细讨论,参见《Oracle Fusion Middleware Securing Oracle Coherence》
实现基于令牌的安全访问。按照如下方式进行配置:
使用基于令牌的安全,必须提供身份转换和断言的实现。身份转换在客户端产生令牌,断言在集群侧验证。
The following steps describe how to create and run an application for an extend client that uses token-based security to access the cluster.
本节的实例引用了 security helper 文件,定义了基于角色的安全策略和访问控制。
在 security helper 文件里定义几种不同的角色:role_reader、role_writer和role_admin。同时定义不同用户到角色上,比如 BuckarooBanzai 配置为 ROLE_ADMIN, 同时为每个角色定义唯一的 ID,比如 ROLE_ADMIN 为 9。Helper 同时定义缓存的名称和调用服务的名称。
这个文件的核心功能是 login 和 checkAccess 方法。Login 方法使用用户名和构造的唯一名 (distinguished name DN)。通过名字关联到角色。PofPrincipal 提供了具体实现。
checkAccess 方法则演示授权部分的代码实现。里面决定用户是否能够通过给定的角色访问缓存。
创业一个新的工程和 security helper 文件的步骤如下:
1. 创建一个新工程 (Application Client Project)chapter10_Security
在 Configuration 配置下拉列表找那个,确保选中的是 CoherenceConfig。
取消 Create a default main;
在 Coherence 页,选择仅仅 Coherence12.1.2。
2. 新建一个 Java文件:SecurityExampleHelper.java
Java 包路径是:com.oracle.handson.chapter10.
3. 示例代码见 appClientModule\com\oracle\handson\chapter10\SecurityExampleHelper.java
见下面
package com.oracle.handson.chapter10;
import com.tangosol.io.pof.PofPrincipal;
import com.tangosol.net.security.SecurityHelper;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import java.security.Principal;
import javax.security.auth.Subject;
/**
* This class provides extremely simplified role based policies and access
* control.
*/
public class SecurityExampleHelper {
// ----- static methods -------------------------------------------------
/**
* Login the user.
*
* @param sName
* the user name
*
* @return the authenticated user
*/
public static Subject login(String sName) {
// For simplicity, just create a Subject. Normally, this would be
// done using JAAS.
String sUserDN = "CN=" + sName + ",OU=Yoyodyne";
Set setPrincipalUser = new HashSet();
setPrincipalUser.add(new PofPrincipal(sUserDN));
// Map the user to a role
setPrincipalUser.add(new PofPrincipal((String) s_mapUserToRole.get(sName)));
return new Subject(true, setPrincipalUser, new HashSet(), new HashSet());
}
/**
* Assert that a Subject is associated with the calling thread with a
* Principal representing the required role.
*
* @param sRoleRequired
* the role required for the operation
*
* @throws SecurityException
* if a Subject is not associated with the calling thread or
* does not have the specified role Principal
*/
public static void checkAccess(String sRoleRequired) {
checkAccess(sRoleRequired, SecurityHelper.getCurrentSubject());
}
/**
* Assert that a Subject contains a Principal representing the required
* role.
*
* @param sRoleRequired
* the role required for the operation
*
* @param subject
* the Subject requesting access
*
* @throws SecurityException
* if a Subject is null or does not have the specified role
* Principal
*/
public static void checkAccess(String sRoleRequired, Subject subject) {
if (subject == null) {
throw new SecurityException("Access denied, authentication required");
}
Map mapRoleToId = s_mapRoleToId;
Integer nRoleRequired = (Integer) mapRoleToId.get(sRoleRequired);
for (Iterator iter = subject.getPrincipals().iterator(); iter.hasNext();) {
Principal principal = (Principal) iter.next();
String sName = principal.getName();
if (sName.startsWith("role_")) {
Integer nRolePrincipal = (Integer) mapRoleToId.get(sName);
if (nRolePrincipal == null) {
// invalid role
break;
}
if (nRolePrincipal.intValue() >= nRoleRequired.intValue()) {
return;
}
}
}
throw new SecurityException("Access denied, insufficient privileges");
}
// ----- constants -----------------------------------------------------
public static final String ROLE_READER = "role_reader";
public static final String ROLE_WRITER = "role_writer";
public static final String ROLE_ADMIN = "role_admin";
/**
* The cache name for security examples
*/
public static final String SECURITY_CACHE_NAME = "security";
/**
* The name of the InvocationService used by security examples.
*/
public static String INVOCATION_SERVICE_NAME = "ExtendTcpInvocationService";
// ----- static data ---------------------------------------------------
/**
* The map keyed by user name with the value being the user's role.
* Represents which user is in which role.
*/
private static Map s_mapUserToRole = new HashMap();
/**
* The map keyed by role name with the value the role id. Represents the
* numeric role identifier.
*/
private static Map s_mapRoleToId = new HashMap();
// ----- static initializer ---------------------------------------------
static {
// User to role mapping
s_mapUserToRole.put("BuckarooBanzai", ROLE_ADMIN);
s_mapUserToRole.put("JohnWhorfin", ROLE_WRITER);
s_mapUserToRole.put("JohnBigboote", ROLE_READER);
// Role to Id mapping
s_mapRoleToId.put(ROLE_ADMIN, Integer.valueOf(9));
s_mapRoleToId.put(ROLE_WRITER, Integer.valueOf(2));
s_mapRoleToId.put(ROLE_READER, Integer.valueOf(1));
}
}
一个身份转换 (identity transformer) (com.tangosol.net.security.IdentityTransformer)是一个客户端的组件,转换身份标识到令牌当中。令牌是一个 Coherence 支持的序列化对象。Coherence 在运行时自动的序列化令牌,并作为请求的一部分发送给代理。创建一个身份转换步骤如下:
1. 创建一个 Java 类:PasswordIdentityTransformer。
2. Import IdentityTransformer 接口。
确保 PasswordIdentityTransformer 实现 IdentityTransformer 接口。
3. 实现 transformIdentity 方法,处理下面的任务:
package com.oracle.handson.chapter10;
import com.tangosol.net.security.IdentityTransformer;
import java.security.Principal;
import java.util.Iterator;
import java.util.Set;
import javax.security.auth.Subject;
import com.tangosol.net.Service;
/**
* PasswordIdentityTransformer creates a security token that contains the
* required password and then adds a list of Principal names.
*
*/
public class PasswordIdentityTransformer implements IdentityTransformer
{
// ----- IdentityTransformer interface ----------------------------------
/**
* Transform a Subject to a token that asserts an identity.
*
* @param subject
* the Subject representing a user.
*
* @return the token that asserts identity.
*
* @throws SecurityException
* if the identity transformation fails.
*/
public Object transformIdentity(Subject subject, Service service) throws SecurityException {
// The service is not needed so the service argument is being ignored.
// It could be used, for example, if there were different token types
// required per service.
if (subject == null) {
throw new SecurityException("Incomplete Subject");
}
Set setPrincipals = subject.getPrincipals();
if (setPrincipals.isEmpty()) {
throw new SecurityException("Incomplete Subject");
}
String[] asPrincipalName = new String[setPrincipals.size() + 1];
int i = 0;
asPrincipalName[i++] = System.getProperty("coherence.password", "secret-password");
for (Iterator iter = setPrincipals.iterator(); iter.hasNext();) {
asPrincipalName[i++] = ((Principal) iter.next()).getName();
}
// The token consists of the password plus the principal names as an
// array of pof-able types, in this case strings.
return asPrincipalName;
}
}
一个身份断言 (identity asserter) (com.tangosol.net.security.IdentityAsserter) 是运行在缓存服务器上的,集群侧的组件。断言验证从客户端传来的令牌。创建一个身份断言的实现:
1. 创建一个 Java 类:PasswordIdentityAsserter。
2. Import IdentityAsserter 接口。确保 PasswordIdentityAsserter 实现了 IdentityAsserter 接口。
3. 实现 assertIdentity 方法:
a) 验证令牌中包含合适的口令。
package com.oracle.handson.chapter10;
import com.tangosol.net.Service;
import com.tangosol.net.security.IdentityAsserter;
import com.tangosol.io.pof.PofPrincipal;
import java.util.HashSet;
import java.util.Set;
import javax.security.auth.Subject;
/**
* PasswordIdentityAsserter asserts that the security token contains the
* required password and then constructs a Subject based on a list of Principal
* names.
*
*/
public class PasswordIdentityAsserter implements IdentityAsserter {
// ----- IdentityAsserter interface -------------------------------------
/**
* Asserts an identity based on a token-based identity assertion.
*
* @param oToken
* the token that asserts identity.
*
* @return a Subject representing the identity.
*
* @throws SecurityException
* if the identity assertion fails.
*/
public Subject assertIdentity(Object oToken, Service service) throws SecurityException {
// The service is not needed so the service argument is being ignored.
// It could be used, for example, if there were different token types
// required per service.
if (oToken instanceof Object[]) {
String sPassword = System.getProperty("coherence.password", "secret-password");
Set setPrincipalUser = new HashSet();
Object[] asName = (Object[]) oToken;
// first name must be password
if (((String) asName[0]).equals(sPassword)) {
// prints the user name to server shell to ensure we are
// communicating with it and to ensure user is validated
System.out.println("Password validated for user: " + asName[1]);
for (int i = 1, len = asName.length; i < len; i++) {
setPrincipalUser.add(new PofPrincipal((String) asName[i]));
}
return new Subject(true, setPrincipalUser, new HashSet(), new HashSet());
}
}
throw new SecurityException("Access denied");
}
}
创建一个 Java 程序处理密码。使用 SecurityExampleHelper.login("BuckarooBanzai") 来调用 SecurityExampleHelper 里的 login 方法来产生令牌。在运行时,用户名关联到 SecurityExampleHelper 类里的主题。通过 PasswordIdentityTransformer 类产生一个令牌,并且通过 PasswordIdentityAsserter 来验证。如果验证成功,连接到代理的则被授权。用 Subject.doas 方法使主题在安全上下文中可用。
1. 创建一个有 main 方法的类:PasswordExample。
2. 在 main 方法中实现获得一个缓存的引用。
3. 使用 SecurityExampleHelper.login 方法获得用户 BuckarooBanzai 的主题。
4. 实现 doAs 方法,将主题作为 java 安全上下文的一部分。主题被后面的应用所使用。在这个例子里,doAs 方法实现了验证集群访问控制的角色。
package com.oracle.handson.chapter10;
import com.tangosol.net.CacheFactory;
import com.tangosol.net.NamedCache;
import com.tangosol.net.Service;
import java.io.IOException;
import java.security.PrivilegedExceptionAction;
import javax.security.auth.Subject;
/**
* This class shows how a Coherence Proxy can require a password to get a
* reference to a cache.
* <p>
* The PasswordIdentityTransformer will generate a security token that contains
* the password. The PasswordIdentityAsserter will validate the security token
* to enforce the password. The token generation and validation occurs
* automatically when a connection to the proxy is made.
*
*/
public class PasswordExample {
// ----- static methods -------------------------------------------------
/**
* Get a reference to the cache. Password will be required.
*/
public static void main(String[] args) {
getCache();
}
public static void getCache() {
System.out.println("------password example begins------");
Subject subject = SecurityExampleHelper.login("BuckarooBanzai");
try {
NamedCache cache = (NamedCache) Subject.doAs(subject, new PrivilegedExceptionAction() {
public Object run() throws Exception {
NamedCache cache;
cache = CacheFactory.getCache(SecurityExampleHelper.SECURITY_CACHE_NAME);
System.out.println("------password example succeeded------");
return cache;
}
});
} catch (Exception e) {
// get exception if the password is invalid
System.out.println("Unable to connect to proxy");
e.printStackTrace();
}
System.out.println("------password example completed------");
}
}
配置一个操作重写文件 (tangosol-coherence-override.xml) 去标识定义身份转换的类(该类在客户侧用于转换主题到令牌)和身份断言(该类在集群侧验证身份断言)。
1. 在项目浏览器里打开 tangosol-coherence-override.xml 文件
2. 使用 identity-transformer 和 identity-asserter 单元标识两个实现类的全路径:PasswordIdentityTransformer 和 PasswordIdentityAsserter。设置主题范围参数为真,关联当前安全上下文的身份到缓存。
<?xml version="1.0" encoding="UTF-8"?>
<coherence xmlns="http://xmlns.oracle.com/coherence/coherence-operational-config" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.oracle.com/coherence/coherence-operational-config http://xmlns.oracle.com/coherence/coherence-operational-config/1.2/coherence-operational-config.xsd">
<!--coherence-version:12.1.2-->
<security-config>
<identity-asserter>
<class-name>com.oracle.handson.PasswordIdentityAsserter</class-name>
</identity-asserter>
<identity-transformer>
<class-name>com.oracle.handson.PasswordIdentityTransformer</class-name>
</identity-transformer>
<subject-scope>true</subject-scope>
</security-config>
</coherence>
扩展客户端的缓存配置文件路由缓存操作到集群的扩展代理上。在运行时,缓存操作不在本地执行,他们被发网扩展代理服务。步骤如下:
1. 在项目浏览器里打开 coherence-cache-config.xml 文件
2. 保存成 client-cache-config.xml 文件
3. 写扩展客户端缓存配置文件。注意以下几点:
a) 使用 cache-name 单元,定义缓存的名字为 security。注意在集群侧必须同时也有一个缓存名字叫 security。
b) 使用 remote-cache-scheme 单元, 定义远程缓存的细节。
c) 使用 address 和 port 单元,在tcp-initiator配置里标识远程代理服务侦听的地址,本例是 localhos t端口 9099。
d) 使用 defaults 和 serializer,定义客户 with a value of pof to call the serializer for the custom POF configuration file (which you will create later in this chapter).
<?xml version="1.0"?>
<cache-config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://xmlns.oracle.com/coherence/coherence-cache-config"
xsi:schemaLocation="http://xmlns.oracle.com/coherence/coherence-cache-config coherence-cache-config.xsd">
<defaults>
<serializer>pof</serializer>
</defaults>
<caching-scheme-mapping>
<cache-mapping>
<cache-name>security</cache-name>
<scheme-name>examples-remote</scheme-name>
</cache-mapping>
</caching-scheme-mapping>
<caching-schemes>
<remote-cache-scheme>
<scheme-name>examples-remote</scheme-name>
<service-name>ExtendTcpCacheService</service-name>
<initiator-config>
<tcp-initiator>
<remote-addresses>
<socket-address>
<address system-property="tangosol.coherence.proxy.address">localhost</address>
<port system-property="tangosol.coherence.proxy.port">9099</port>
</socket-address>
</remote-addresses>
</tcp-initiator>
</initiator-config>
</remote-cache-scheme>
<remote-invocation-scheme>
<scheme-name>remote-invocation-scheme</scheme-name>
<service-name>ExtendTcpInvocationService</service-name>
<initiator-config>
<connect-timeout>2s</connect-timeout>
<tcp-initiator>
<remote-addresses>
<socket-address>
<address system-property="tangosol.coherence.proxy.address">localhost</address>
<port system-property="tangosol.coherence.proxy.port">9099</port>
</socket-address>
</remote-addresses>
</tcp-initiator>
<outgoing-message-handler>
<request-timeout>5s</request-timeout>
</outgoing-message-handler>
</initiator-config>
</remote-invocation-scheme>
</caching-schemes>
</cache-config>
为扩展缓存代理创建缓存配置,步骤如下:
1. 工程浏览器打开coherence-cache-config.xml文件。
2. 另存为examples-cache-config.xml。
3. 配置扩展代理的缓存配置文件,以下是配置重点:
a) 使用cache-name单元,定义安全的缓存名称,这个名字必须同时定义在扩展代理的缓存配置文件里。
b) 使用address和port单元,指定扩展代理监听的地址和端口,本例是localhost:9099。
c) 使用autostart单元,通过tangosol.coherence.extend.enabled系统属性来防止缓存服务器运行代理服务。
d) 使用defaults和serializer来调用客户POF配置序列化。
配置文件如下:
<?xml version="1.0"?>
<cache-config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://xmlns.oracle.com/coherence/coherence-cache-config"
xsi:schemaLocation="http://xmlns.oracle.com/coherence/coherence-cache-config coherence-cache-config.xsd">
<defaults>
<serializer>pof</serializer>
</defaults>
<caching-scheme-mapping>
<cache-mapping>
<cache-name>security</cache-name>
<scheme-name>ExamplesPartitionedPofScheme</scheme-name>
</cache-mapping>
</caching-scheme-mapping>
<caching-schemes>
<distributed-scheme>
<scheme-name>ExamplesPartitionedPofScheme</scheme-name>
<service-name>PartitionedPofCache</service-name>
<backing-map-scheme>
<local-scheme>
<!-- each node will be limited to 32MB -->
<high-units>32M</high-units>
<unit-calculator>binary</unit-calculator>
</local-scheme>
</backing-map-scheme>
<autostart>true</autostart>
</distributed-scheme>
<!--
Proxy Service scheme that allows remote clients to connect to the
cluster over TCP/IP.
-->
<proxy-scheme>
<scheme-name>secure-proxy</scheme-name>
<service-name>ProxyService</service-name>
<thread-count system-property="tangosol.coherence.extend.threads">2</thread-count>
<acceptor-config>
<tcp-acceptor>
<local-address>
<address system-property="tangosol.coherence.extend.address">localhost</address>
<port system-property="tangosol.coherence.extend.port">9099</port>
</local-address>
</tcp-acceptor>
</acceptor-config>
<autostart system-property="tangosol.coherence.extend.enabled">false</autostart>
</proxy-scheme>
</caching-schemes>
</cache-config>
新建一个缓存集群节点的启动配置。配置必须包括系统属性指定代理服务和集群侧缓存配置文件。你必须包括应用的类和 XML 配置文件在 class path 上。步骤如下:
1. 在 Eclipse 里创建启动配置。右键点 chapter10_Security,Run As ->Run Configurations。起名 为chapter10CacheServerSecurityCacheServer.
2. Main选卡
| Project | chapter10_Security |
| Main class | com.tangosol.net.DefaultCacheServer |
3. Coherence 选卡
| 集群侧缓存配置 | examples-cache-config.xml |
| Local Storage | Enabel(cache server) |
| Cluster port | 3155 |
4. Classpath 选卡
5. Common 选卡
| Shared file | \chapter10_Security |
最后 Apply。
创建一个集群内带扩展代理的缓存服务器启动配置。扩展客户端连接这个服务。运行配置必须知道代理服务和集群配置。运行应用程序类和 XML 必须包含在 class path 当中。
简单点说,启动脚本必须包含缓存服务器启动配置,及确保扩展代理。步骤如下:
1. 在 Eclipse 里创建启动配置。右键点 chapter10_Security,Run As ->Run Configurations。起名为 chapter10CacheServerSecurityRunProxy。
2. Main 选卡
| Project | chapter10_Security |
| Main class | com.tangosol.net.DefaultCacheServer |
3. Coherence选卡
| 集群侧缓存配置 | examples-cache-config.xml(同10.2.8) |
| Local Storage | Enabel(cache server)(同10.2.8) |
| Cluster port | 3155 (同10.2.8) |
4. Arguments 选卡
| VM 参数 | -Dtangosol.coherence.extend.enabled=true |
5. Classpath 选卡
6. Common 选卡
运行 password 程序产生产生和验证令牌,传给代理服务。
1. 为 PasswordExample.java 创建运行配置。
a) 右键 PasswordExample.java, Run As->Run Configurations。
b) 新运行配置 chapter10CacheClientPasswordExample
| 运行配置名称 | chapter10CacheClientPasswordExample |
| Project | chapter10_Security |
| Main Class | com.oracle.handson.chapter10.PasswordExample |
c) Coherence 选卡
| Client 缓存配置 | appClientModule/client-cache-config.xml |
| Local storage | Disabled (cache client) |
| Cluster Port | 3155 |
d) Classpath选卡
2. 停止所有运行的缓存服务器
3. 运行安全代理服务,运行安全缓存服务,然后运行 PasswordExample
a) 运行 Proxy:chapter10CacheServerSecurityRunProxy
b) 运行 CacheServer:chapter10CacheServerSecurityCacheServer
c) 运行 Client: chapter10CacheClientPasswordExample
| PasswordExample 输出如下: |
| |
d) 代理服务输出如下:
本节描述如何使用基于角色的策略访问集群。登录代码 获得主题和用户 ID,关联到具体角色。 It gets a cache reference running in the context of the subject and then attempt various cache operations. Depending on the role granted to the user, the cache operation is allowed or denied. Note that the role mapping and role-based authorization in the example is simplified and not intended for real security use.
举例,一个用户有 writer 角色,可以 put 和 get 方法。一个用户有 reader 角色,能执行 get 方法,不能执行 put 方法。一个用户有 writer 角色不能删除一个缓存;只有有 admin 角色的用户才可以。
注意当缓存的引用被建立起来时,标识身份的主题会永久关联到引用的的上下文,任何使用这个缓存引用的都使用这个身份。
| PasswordIdentityTransformer | 前一章节 |
| PasswordIdentityAsserter |
新建一个 Java 程序,根据用户角色访问缓存。若要执行此操作,你可以对使用由客户端传递的主题对象进行包装。执行权限仅能访问指定的缓存。
本节创建的类扩展了 com.tangosol.net.cache.WrapperNamedCache 类。次类可以让你方便的使用命名缓存接口上安全方法。
若要确定哪个用户角色能够访问缓存方法,包括对 SecurityExampleHelper.checkAccess 的调用。参数 checkAccess,提供了 provide the user role that is permitted to access the method. Close the implementation with a call to super.
举例,下段代码示例管理员角色可以删除缓存。
public void destroy()
{
SecurityExampleHelper.checkAccess(SecurityExampleHelper.ROLE_ADMIN);
super.destroy();
}
本例中,用户使用 reader 角色能够调用 aggregate 方法。
public Object aggregate(Filter filter, EntryAggregator agent)
{
SecurityExampleHelper.checkAccess(SecurityExampleHelper.ROLE_READER);
return super.aggregate(filter, agent);
}
1. 创建一个 Java 类 EntitledNamedCache
2. 扩展自 WrapperNamedCache。
3. Import 以下 Java 类,WrapperNamedCache 方法使用这些类型
4. 实现 EntitledNamedCache 里的方法,指定的角色可以调用方法。
具体代码见
| \chapter10_Security\appClientModule\com\oracle\handson\chapter10\EntitledNamedCache.java |
Create a file that demonstrates how access entitlements can be applied to a wrapped CacheService using the Subject passed from the client through Coherence*Extend. The implementation delegates access control for cache operations to the EntitledNamedCache you created in the previous section.
新创建的类扩展 com.tangosol.net.WrapperCacheService 类。这是一个允许缓存上安全方法的便利功能。同时提供了代理和客户端间的委托机制。
实现 ensureCache、releaseCache 和 destroyCache 方法,确保只有指定用户才能使用它们。在实现里,包含调用一个带有用户参数的 SecurityExampleHelper.checkAccess 方法。下面的例子,只有 admin 才能删除缓存。
public void destroyCache(NamedCache map)
{
if (map instanceof EntitledNamedCache)
{
EntitledNamedCache cache = (EntitledNamedCache) map;
SecurityExampleHelper.checkAccess(SecurityExampleHelper.ROLE_ADMIN);
map = cache.getNamedCache();
}
super.destroyCache(map);
}
创建申请权利的应用程序:
1. Java 类:EntitledCacheService
2. 确保类 imports 和扩展了 WrapperCacheService 类
3. 实现 ensureCache、releaseCache 和 destroyCache 方法,确保只有授权用户才能使用它。
具体代码见:
| chapter10_Security\appClientModule\com\oracle\handson\chapter10\EntitledCacheService.java |
| |
创建一个运行访问控制的示例程序。角色策略被定义在 SecurityExampleHelper 类里。EntitledCacheService 和 EntitledNamedCacheclasses 确保执行这个策略。
这个程序将用不同的用户做为参数传给 SecurityHelperFile.login 方法,尝试进行缓存的 read、write 和 destroy 操作。基于定义在 EntitledCacheService 和 EntitledNamedCache 的授权策略返回操作是否成功的标识。
1. 新 Java 类:AccessControlExample
2. 实现 main 方法
3. 指定用户给 login 方法
4. 每个用户执行 read(get)、write(put) 和 destroy 操作
具体见:
| \chapter10_Security\appClientModule\com\oracle\handson\chapter10\AccessControlExample.java |
| |
修改集群侧的缓存配置文件 examples-cache-config.xml。指定缓存服务完整的类名字路径。本例中,是 com.oracle.handson.chapter10.EntitledCacheServic 代理 param-type 是 com.tangosol.net.CacheService。
...
<proxy-config>
<cache-service-proxy>
<class-name>com.oracle.handson.chapter10.EntitledCacheService</class-name>
<init-params>
<init-param>
<param-type>com.tangosol.net.CacheService</param-type>
<param-value>{service}</param-value>
</init-param>
</init-params>
</cache-service-proxy>
</proxy-config>
1. 为 AccessControlExample.java 创建运行配置
a) 项目浏览器里,右键 AccessControlExample,Run As -> Run Configurations。
b) 新运行配置 chapter10CacheClientAccessControlExample
| 运行配置 | chapter10CacheClientAccessControlExample |
| Project | chapter10_Security |
| Main Class | com.oracle.handson.chapter10.AccessControlExample |
c) Coherence 选卡
| Client缓存配置 | appClientModule/client-cache-config.xml |
| Local storage | Disabled (cache client) |
| Cluster Port | 3155 |
d) Classpath 选卡
2. 停止所有运行的缓存服务器
3. 运行安全代理服务,安全缓存服务,然后运行 AccessControlExample 程序。
a) 运行 Proxy:chapter10CacheServerSecurityRunProxy
b) 运行 CacheServer:chapter10CacheServerSecurityCacheServer
c) 运行 Client: chapter10CacheClientAccessControlExample
安全代理的控制台有如下输出
一个调用服务集群服务能够扩展客户端执行调用集群对象的功能。这个例子演示如何通过基于角色的策略决定哪个用户可以运行调用对象。
举例,一个有 writer 角色的用户可以运行调用对象,有 reader 角色不行。在这个例子里,你创建一个简单调用对象,能够被客户端调用。所以一个调用对象必须是被序列化的。同时你要创建一个调用服务,测试哪个用户能基于角色执行服务上的方法。
同前面的例子,本例用 PasswordIdentityTransformer 类产生包含口令,用户ID和角色的安全令牌。PasswordIdentityAsserter(运行在代理上)将用来验证安全令牌,确保口令并构造相应用户 ID 和角色为主题对象。The production and assertion of the security token happens automatically.
新建一个实现简单调用对象,这个对象将被有资格用于调用服务。举例,调用对象能被增加并返回整数。创建调用对象步骤如下:
1. 新建 Java类:ExampleInvocable
2. Import Invocable 和 InvocationService 接口。因为这个类将是序列化对象,还有 import PortableObject、PofReader 和 PofWriter 类。
3. 确定 ExampleInvocable 实现了 Invocable 和 PortableObject 的方法。
4. 实现 ExampleInvocable 类,增加技术并返回结果。
5. 实现 PofReader.readExternal 和 PofWriter.writeExternal 方法。
这个例子演示远程调用服务被封装提供成访问控制。访问授权单元 Access entitlements can be applied to a wrapped InvocationService using the Subject passed from the client by using Coherence*Extend. This implementation enables only clients with a specified role to access the wrapped invocation service.
这个类扩展自 com.tangosol.net.WrapperInvocationService 类,这是一个 This is a convenience function that enables you to secure the methods onInvocationService. It also provides a mechanism to delegate between the invocation service on the proxy and the client request.
实现一个授权调用服务:
1. 新建 Java 类:EntitledInvocationService
2. Import Invocable、InvocationObserver、InvocationService、WrapperInvocationService、Map 和 Set 接口。确保 EntitledInvocationService 类扩展 extends WrapperInvocationService 类。
3. 实现 query 和 execute 方法。在实现中,包含调用 SecurityExampleHelper.checkAccess 方法却决定是否有合适的用户角色,本例ROLE_WRITER 可以访问这些操作。
4. 下面是代码示例 EntitledInvocationService
chapter10_Security\appClientModule\com\oracle\handson\chapter10\EntitledInvocationService.java
新建一个实例程序,运行访问调用服务。这个程序的目的是测试定义在 SecurityExampleHelper 的不同用户能够访问和运行调用服务。The enforcement of the role-based policies is provided by the EntitledInvocationService class.
创建一个运行访问调用服务端示例程序,步骤如下:
1. 建一个有 main 方法的 java 程序:AccessInvocationServiceExample
2. Import 如下类或接口 ExampleInvocable、CacheFactory 和 InvocationService
3. 实现 main 方法,调用 accessInvocationService 方法。
4. 实现 accessInvocationService 类,使用不同用户尝试登录
示例程序见:
chapter10_Security\appClientModule\com\oracle\handson\chapter10\AccessInvocationServiceExample.java
编辑 examples-cache-config.xml 文件,invocation 服务增加 proxy-config 的全路径。本例中,invocation 服务的类名是 com.oracle.handson.EntitledInvocationService,其参数类型是 com.tangosol.net.InvocationService。
创建一个 POF 文件,声明 ExampleInvocable 作为一个用户类型。
1. 打开 pof-config.xml 文件
2. 声明 ExampleInvocable 为一个用户对象,并保存文件。
用户配置的 POF 配置文件必须先于定义在 coherence.jar 里的文件,确保优先做类加载 (classloader)。如果没有,客户端 POF 文件将被默认在 coherence.jar 里的文件所默认忽略。
确保应用使用的配置文件在 chapter10_Security\appModule 下,确保 Coherence12.1.2 库不出现在根启动的服务器 classpath 上。同样, coherence.jar 文件放在 chapter10_Security 用户目录后。
如果 Coherence12.1.2 库出现在服务器类路径的 Bootstrap 上,按照如下步骤删除:
1. 右键点工程,Run As->Run Configurations。
2. 复制 chapter10CacheServerSecurityRunProxy,命名为 chapter10CacheServerSecurityRunProxyII。移除 Coherence 12.1.2。点 Add External jars,选择 Oracle_Home\cohrence\lib 目录下的 coherence.jar。
| 运行配置 | chapter10CacheServerSecurityRunProxyII |
| Project | chapter10_Security |
| Main Class | com.tangosol.net.DefaultCacheServer |
3. 复制 chapter10CacheServerSecurityCacheServer,更名为 chapter10CacheServerSecurityCacheServerII。移除 Coherence 12.1.2。点 Add External jars,选择 Oracle_Home\cohrence\lib 目录下的 coherence.jar。
基于用户角色,运行访问调用服务端例子演示调用对象时是否被许可或拒绝:
1. 为 AccessInvocationServiceExample 创建一个运行配置
a) 右键 AccessInvocationServiceExample,Run As->Run Configurations。
b) 选 Oracle Coherence,新建 configuration。
| name | chapter10CacheClientAccessInvocationService |
| Main Class | com.oracle.handson.chapter10.AccessInvocationServiceExample |
| Coherence | |
| Cache 配置 | client-cache-config.xml |
| Local storage | Disabled (cache client) |
| Classpath | |
| remove | Coherence12.1.2 |
| Add External Jars | coherence.jar |
2. 停止所有运行的缓存服务
3. 运行 proxy,运行 cache,然后运行 chapter10CacheClientAccessInvocationService。
a) 运行 Proxy:chapter10CacheServerSecurityRunProxyII
b) 运行 CacheServer:chapter10CacheServerSecurityCacheServerII
c) 运行 Client: chapter10CacheClientAccessInvocationService
chapter10CacheClientAccessInvocationService输出如下
chapter10CacheServerSecurityRunProxyII 输出如下
![]() | 金辉,在 Oracle 负责云和中间件产品的资深售前顾问和团队经理,有十多年的中间件和项目管理经验,专注在企业云构建,企业集成解决方案领域,熟悉业内主要的 SOA 集成产品。参加过北京马拉松和 TNF50 越野比赛。你可以通过邮件 arthur.jin@oracle.com 与他联系。 |