关闭

rop学习(三)

标签: rop
909人阅读 评论(0) 收藏 举报
分类:

上一节讲解了rop如何进行参数的传递,以及返回。本章节讲解rop如何进行应用的授权访问以及验证
当用户需要访问某个应用系统前,应用系统一般都需要对该用户进行身份认证。常见的身份认证方法是让用户输入“用户/密码” ,当通过验证后,允许进入系统,否则阻止用户登录系统和应用系统类似,服务开放平台也需要对接入的应用进行身份认证,以确保服务只向合法授权的客户端应用开放。一般的做法是:服务开放平台通过一个应用申请流程向通过
审核的开发者分配一个唯一的应用键和应用密钥(即 appKey/secret) 。应用键是公开的,而应用密钥是保密的,只有开发者自己知道。
开发者开发的应用在访问开放平台的服务时, 都必须带上这个 appKey, 以亮明自己的身份。此外,还必须通过应用密钥对请求数据进行签名,开放平台通过验证服务请求的签名判断客户端应用的合法性。也就是说,开放平台通过 appKey/secret 的机制对应用进行身份认证。
3.2 应用键/密钥管理器
由于 Rop 需要在服务端采用相同的算法计算请求参数的签名, 并和客户端传送过来的
签名进行比较,如果两者相等,便认为当前交互的客户端是合法的客户端,反之则认为是
一个非法的客户端。
因此,服务端必须知道应用键及其应用密钥的信息,这样才能顺利完成服务端签名验
证的工作, Rop 通过 com.rop.security.AppSecretManager 接口访问应用键/密钥。 您可以采用
适合的方式保存应用键/密钥,如保存在数据库、LDAP、文件系统等地方,然后编写一个
访问应用键/密钥的 AppSecretManager 实现类就可以了。
AppSecretManager 拥有两个接口方法:
 boolean isValidAppKey(String appKey):判断 appKey 是否是合法的应用键;
 String getSecret(String appKey):根据 appKey 获取对应的应用密钥。
Rop 默 认 提 供 了 一 个 基 于 文 件 存 储 的 FileBaseAppSecretManager 实 现 类 ,
FileBaseAppSecretManager 默认使用读取类路径下的 rop.appSecret.properties 属性文件,获
取应用键/密钥,属性文件中应用键/密钥采用如下方式保存:
00001=abcdeabcdeabcdeabcdeabcde
3 应用授权及验证
320
00002=abcdeabcdeabcdeabcdeaaaaa
属性名对应应用键,属性值对应应用密钥。如果属性文件放置在其它地方,则可以通
过 appSecretFile 属性指定位置,appSecretFile 支持“classpath:”等 Spring 资源类型的前缀。
由于大型服务平台一般是分布式的, 所以将应用键/密钥保存在系统文件中并不是个好
主意。如果开发者希望提供自定义的 AppSecretManager,可通过的
app-secret-manager 属性进行配置:

<rop:annotation-driven app-secret-manager="appSecretManager"/>
<bean id="appSecretManager" class="com.rop.sample.SampleAppSecretManager"/>

这样,Rop 就会使用 SampleAppSecretManager 取代默认 FileBaseAppSecretManager 进
行应用键/密钥的读取工作了。
3.3 签名算法
Rop 的签名算法直接参考了 TOP 的签名算法,该签名算法描述如下:
(1) 所有请求参数按参数名升序排序;
(2) 按 请 求 参 数 名 及 参 数 值 相 互 连 接 组 成 一 个 字 符 串 :

<paramName1><paramValue1><paramName2><paramValue2>…;

(3) 将应用密钥分别添加到以上请求参数串的头部和尾部:< 请求参数字符
串 >;
(4) 对该字符串进行 SHA1 运算,得到一个二进制数组;
(5) 将该二进制数组转换为十六进制的字符串,该字符串即是这些请求参数对应的签
名;
(6) 该签名值使用 sign 系统级参数一起和其它请求参数一起发送给服务开放平台。
假设,user.create 的服务有 3 个业务级参数,分别为 userName、age 及 sex。这些业务
级参数和系统级参数的值如下表所示:
这里写图片描述
根据 Rop 的签名算法,首先按字母顺序将所有参数名和参数值拼装成一个字符串:
age24appKey000001formatxmllocalezh_CNmethoduser.createsessionIdAAAAsex1userNameto
msonv1.0
假设,appKey 为 000001 的 secret(应用密钥)是“abcdef” ,则将“abcdef”分别添加
到以上请求参数串的头部和尾部,得到:
abcdefage24appKey000001formatxmllocalezh_CNmethoduser.createsessionIdAAAAsex1userN
ametomsonv1.0abcdef
对以上字符串进行 SHA1 签名运算,将签名值转换为十六进制的编码串,得到:
8625FD7EEAE1E68203B48C64DE495792BF59E833
最后,客户端即可使用如下的 URL 请求串对 user.create 服务方法发起请求:
http:///?appKey=000001&method=user.create&…
&sign=8625FD7EEAE1E68203B48C64DE495792BF59E833
3.4 签名功能控制
默认情况下,Rop 会对每个服务请求进行签名验证,如果签名验证报错,将直接驳回
请求并回报相应的错误信息。Rop 允许服务平台开发者开启或关闭签名验证的功能,Rop
提供了 3 个级别的控制:
 平台级:开启或关闭服务平台所有服务的签名验证功能;
 服务级:在平台级签名验证功能开启的情况下,可以关闭某个具体服务的签名验
证;
 参数级:在 10.4.3 小节中,我们知道 Rop 的签名算法要求把所有的参数拼装成一
个字符串,如果有些参数值很大(如上传文件的文件内容) ,签名算法将需要构
造一个很大的字符串,占用很大的内存。从安全上来说,仅需对一些关键的参数
进行签名就可以了,并非一定要对所有的参数进行签名。有鉴于此,Rop 在服务
签名时允许忽略某些参数,提供参数级的签名控制。
平台级控制
通过《rop:annotation-driven/》的 sign-enable 属性即可开启或关闭服务平台签名验证功
能:

<rop:annotation-driven sign-enable="false"/>

我们强烈建议在生产环境下开启服务签名验证的功能,以保证服务平台的安全性,免
受恶意客户终端的攻击。
服务级控制
在平台级签名功能开启的情况下,Rop 还允许关闭某个服务的签名验证功能。通过将
@ServiceMethod 的 ignoreSign 属性设置为 IgnoreSignType.YES 即可:

@ServiceMethod(method = "user.add", version = "5.0", ignoreSign = IgnoreSignType.YES)
public Object addUser5(CreateUserRequest request) {
CreateUserResponse response = new CreateUserResponse();
response.setCreateTime("20120101010102");
response.setUserId("4");
return response;

4 服务会话管理
这样,客户端在访问 user.add#5.0 的服务方法时,就不必提供请求参数的签名信息了。
参数级控制
在定义服务方法的 RopRequest 类时,只要在 RopRequest 的某些属性上标注了
@IgnoreSign, 这些属性所对应的请求参数就可以排除在签名参数列表之外了。 来看一个例public class LogonRequest extends AbstractRopRequest{
@Pattern(regexp = “\w{4,30}”)
private String userName;
@IgnoreSign
@Pattern(regexp = “\w{6,30}”)
private String password;

}onRequest 的 password 属性所对应的请求参数将不会纳入到签名算法的参数列表
中。使用这种办法,可以在具体的 RopRequest 类中将某些属性对应的请求参数排除在签
名算法之外。
如果希望某一类型的属性统一忽略签名,有没有简单的方法呢?Rop 提供了一种非常
便捷的方法,即在属性类定义处使用@IgnoreSign 注解即可。如用于保存上传文件的
UploadFile 类就标注了@IgnoreSign 注解:

@IgnoreSign
public class UploadFile {
private String fileType;
private byte[] content;
}

这样, UploadFile作为任何RopRequest类的属性都将排除在签名算法的参数列表之外。

9 拦截器及事件体系
9.1 拦截器
服务请求在通过 SecurityManager 的安全检查后,Rop 将依次执行 com.rop.Interceptor
拦截器的 beforeService()方法,一旦某个拦截器通过 RopRequestContext#setRopResponse()
设置了一个响应对象, Rop 将终止执行链直接返回响应。 在目标服务方法执行成功后, Rop
再依次执行这些拦截器的 beforeResponse()方法。其调用流程在图 8 中说明:
这里写图片描述
通过上图, 我们可以发现 Rop 的拦截器架构是完全参考了 Spring MVC 的拦截器体系
设计的。我们来看一下 com.rop.Interceptor 有哪些接口方法:
 void beforeService(RopRequestContext ropRequestContext): 在调用目标服务方法之
前调用,如果在该方法中,通过 RopRequestContext#setRopResponse()设置了响应
对象,Rop 将中断执行链的执行直接返回响应;
 void beforeResponse(RopRequestContext ropRequestContext): 在调用完目标服务方
法后,执行该拦截器方法;
 boolean isMatch(RopRequestContext ropRequestContext): 可以通过该方法指定拦截
器的匹配服务方法,只有该方法返回 true 时,拦截器才会实施拦截;
 int getOrder():指定拦截器的先后顺序。
rop-sample 中定义了一个 ReservedUserNameInterceptor 拦截器,来看一下它的代码:

package com.rop.sample;
import com.rop.AbstractInterceptor;
import com.rop.RopRequestContext;
import com.rop.sample.response.InterceptorResponse;
import org.springframework.stereotype.Component;
@Component
public class ReservedUserNameInterceptor extends AbstractInterceptor {
@Override
public void beforeService(RopRequestContext ropRequestContext) { ①
if ("jhonson".equals(ropRequestContext.getParamValue("userName"))) {
InterceptorResponse response = new InterceptorResponse();
response.setTestField("the userName can't be jhonson!");
ropRequestContext.setRopResponse(response);
}
}
@Override
public void beforeResponse(RopRequestContext ropRequestContext) {
}
@Override
public boolean isMatch(RopRequestContext ropRequestContext) {②
return "user.add".equals(ropRequestContext.getMethod());
}
}

ReservedUserNameInterceptor 的作用是确保调用“user.add”的服务时,新增用户的
userName 不使用到预留的用户名。该拦截器通过 isMatch()限定拦截的目标,这里,仅对
user.add 的服务实施拦截。
在开发出拦截器后,还必须将拦截器注册到 Rop 中,这样拦截器才能工作。Rop 为注
册拦截器提供了专门的 Schema:

<rop:interceptors>
<bean class="com.rop.sample.ReservedUserNameInterceptor"/>
</rop:interceptors>

在《rop:interceptors/》中可以注册任意多个拦截器。

9.2 事件及监听
Rop 框架拥有一个完成的事件体系:包括框架级事件和服务级事件。框架级事件包括
框架启动后事件(AfterStartedRopEvent)及框架关闭前事件(PreCloseRopEvent) 。而服务
级 事 件 包 括 服 务 执 行 前 事 件 ( PreDoServiceEvent ) 和 服 务 执 行 后 事 件
(AfterDoServiceEvent) 。
一般情况下,每个事件都要对应一个自己的监听器,但是 Spring 的事件体系颠覆了这
种笨拙的设计模式,它只定义了一个监听器接口,通过泛型指定监听不同的事件。Rop 传
承了 Spring 这一优秀的监听器设计理念,定义了

RopEventListenpublic interface RopEventListener extends EventListener {
void onRopEvent(E ropEvent);
int getOrder();
}

通过泛型 E 指定需要监听的事件类型就可以了, getOrder()接口方法指定
监听器的执行顺序,序号越小越先执行。下面是 rop-sample 中的一个具体

package com.rop.sampimport com.rop.RopRequest;
import com.rop.RopRequestContext;
import com.rop.event.PreDoServiceEvent;
import com.rop.event.RopEventListener;
import com.rop.marshaller.MessageMarshallerUtils;
public class SamplePreDoServiceEventListener
implements RopEventListener<PreDoServiceEvent> {①
@Override
public void onRopEvent(PreDoServiceEvent ropEvent) {
RopRequestContext ropRequestContext = ropEvent.getRopRequestContext();
if(ropRequestContext != null && ropRequestContext.getRopRequest() != null){
RopRequest ropRequest = ropRequestContext.getRopRequest();
String message = MessageMarshallerUtils.getMessage(ropRequest,
ropRequestContext.getMessageFormat());
System.out.println("message("+ropEvent.getServiceBeginTime()+")"+message);
}
}
@Override
public int getOrder() {
return 1;
}
},

事件监听器也必须注册到 Rop 中才会生效,其配置片断如下所示:

<rop:listeners>
<bean class="com.rop.sample.SamplePostInitializeEventListener"/>
<bean class="com.rop.sample.SamplePreDoServiceEventListener"/>
<bean class="com.rop.sample.SampleAfterDoServiceEventListener"/>
</rop:listeners>

以上配置片断一共注册了 3 个事件监听器,它们分别监听不同的事件。如果多个监听
器监听同一个事件,监听器的执行顺序由监听器的 getOrder()决定 。
Rop 采用异步的方式执行事件监听,服务执行的线程和事件监听执行线程是两个不同
的线程,也就是说,事件监听器的执行不会影响服务方法的执行。所以您可以开发一个监
听 AfterDoServiceEvent 事件的监听器,记录每个请求/响应报文的日志,由于事件监听器
异步运行,它不会影响正常服务执行的效率。
值得注意的是,Rop 的拦截器是工作于服务执行线程中的,拦截器的执行效率会直接
影响到服务执行的效率,因此,不宜在拦截器中做太多的事情。

0
0

查看评论
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
    个人资料
    • 访问:44880次
    • 积分:988
    • 等级:
    • 排名:千里之外
    • 原创:44篇
    • 转载:29篇
    • 译文:4篇
    • 评论:8条
    文章分类
    最新评论