引用
目录
1.快速了解Rop
2.请求服务模型
3.应用授权及验证
4.服务会话管理
5.错误处理模型
6.响应报文控制
7.文件上传
8.服务安全控制
9.拦截器及事件体系
10.性能调优
11.开发客户端SDK
12.参考资料
应用键/应用密钥
当用户需要访问某个应用系统前,应用系统一般都需要对该用户进行身份认证。常见的身份认证方法是让用户输入“用户/密码”,当通过验证后,允许进入系统,否则阻止用户登录系统。
和应用系统类似,服务开放平台也需要对接入的应用进行身份认证,以确保服务只向合法授权的客户端应用开放。一般的做法是:服务开放平台通过一个应用申请流程向通过审核的开发者分配一个唯一的应用键和应用密钥(即appKey/secret)。应用键是公开的,而应用密钥是保密的,只有开发者自己知道。
开发者开发的应用在访问开放平台的服务时,都必须带上这个appKey,以亮明自己的身份。此外,还必须通过应用密钥对请求数据进行签名,开放平台通过验证服务请求的签名判断客户端应用的合法性。也就是说,开放平台通过appKey/secret的机制对应用进行身份认证。
应用键/密钥管理器
由于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
00002=abcdeabcdeabcdeabcdeaaaaa
属性名对应应用键,属性值对应应用密钥。如果属性文件放置在其它地方,则可以通过appSecretFile属性指定位置,appSecretFile支持“classpath:”等Spring资源类型的前缀。
由于大型服务平台一般是分布式的,所以将应用键/密钥保存在系统文件中并不是个好主意。如果开发者希望提供自定义的AppSecretManager,可通过<rop:annotation-driven/>的app-secret-manager属性进行配置:
<rop:annotation-driven app-secret-manager="appSecretManager"/>
<bean id="appSecretManager" class="com.rop.sample.SampleAppSecretManager"/>
这样,Rop就会使用SampleAppSecretManager取代默认FileBaseAppSecretManager进行应用键/密钥的读取工作了。
签名算法
Rop的签名算法直接参考了TOP的签名算法,该签名算法描述如下:
(1)所有请求参数按参数名升序排序;
(2)按请求参数名及参数值相互连接组成一个字符串:<paramName1><paramValue1><paramName2><paramValue2>…;
(3)将应用密钥分别添加到以上请求参数串的头部和尾部:<secret><请求参数字符串><secret>;
(4)对该字符串进行SHA1运算,得到一个二进制数组;
(5)将该二进制数组转换为十六进制的字符串,该字符串即是这些请求参数对应的签名;
(6)该签名值使用sign系统级参数一起和其它请求参数一起发送给服务开放平台。
假设,user.create的服务有3个业务级参数,分别为userName、age及sex。这些业务级参数和系统级参数的值如下表所示:
系统级参数名称 |
参数值 v业务级参数名称 |
参数值 |
appKey |
000001 |
userName |
tomson |
sessionId |
AAAA |
age |
24 |
method |
user.create |
sex |
1 |
v |
1.0 |
format |
xml |
locale |
zh_CN |
根据Rop的签名算法,首先按字母顺序将所有参数名和参数值拼装成一个字符串:
引用
age24appKey000001formatxmllocalezh_CNmethoduser.createsessionIdAAAAsex1userNametomsonv1.0
假设,appKey为000001的secret(应用密钥)是“abcdef”,则将“abcdef”分别添加到以上请求参数串的头部和尾部,得到:
引用
abcdefage24appKey000001formatxmllocalezh_CNmethoduser.createsessionIdAAAAsex1userNametomsonv1.0abcdef
对以上字符串进行SHA1签名运算,将签名值转换为十六进制的编码串,得到:
引用
8625FD7EEAE1E68203B48C64DE495792BF59E833
最后,客户端即可使用如下的URL请求串对user.create服务方法发起请求:
引用
http://<serverUrl>/<ropServletUri>?appKey=000001&method=user.create&…&sign=8625FD7EEAE1E68203B48C64DE495792BF59E833
签名功能控制
默认情况下,Rop会对每个服务请求进行签名验证,如果签名验证报错,将直接驳回请求并回报相应的错误信息。Rop允许服务平台开发者开启或关闭签名验证的功能,Rop提供了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;
}
这样,客户端在访问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;
...
}
LogonRequest的password属性所对应的请求参数将不会纳入到签名算法的参数列表中。使用这种办法,可以在具体的RopRequest类中将某些属性对应的请求参数排除在签名算法之外。
如果希望某一类型的属性统一忽略签名,有没有简单的方法呢?Rop提供了一种非常便捷的方法,即在属性类定义处使用@IgnoreSign注解即可。如用于保存上传文件的UploadFile类就标注了@IgnoreSign注解:
@IgnoreSign
public class UploadFile {
private String fileType;
private byte[] content;
}
这样,UploadFile作为任何RopRequest类的属性都将排除在签名算法的参数列表之外。关于UploadFile的进一步信息,请参见后续的内容。