鉴于很多系统需要实施WS-Security的标准,我们在SpringSide中提供了XFire+WSS4J的Demo,本文介绍SpringSide中Spring+XFire+WSS4J的基本配置
[WebService Server端配置]
第一,创建一个基本的BookService
public
interface
BookService
{
/** *//**
* 按书名模糊查询图书
*/
List findBooksByName(String name);
/** *//**
* 查找目录下的所有图书
*
* @param categoryId 如果category为null或“all”, 列出所有图书。
*/
List findBooksByCategory(String categoryId);
/** *//**
* 列出所有分类.
*
* @return List<Category>,或是null。
*/
List getAllCategorys();
}
第二,接口扩展,即Extend基本的BookService,在XFire中,不同的WSS4J策略需要针对不同的ServiceClass,否则<inHandlers>里面的定义会Overlap。
public
interface
BookServiceWSS4JEnc
extends
BookService
{
}
public
interface
BookServiceWSS4JSign
extends
BookService
{
}
第三,配置Spring的ApplicationContext文件
<!--
BookService 基类
-->
<
bean
id
="baseWebService"
class
="org.codehaus.xfire.spring.remoting.XFireExporter"
abstract
="true"
>
<
property
name
="serviceFactory"
ref
="xfire.serviceFactory"
/>
<
property
name
="xfire"
ref
="xfire"
/>
</
bean
>
<
bean
class
="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping"
>
<
property
name
="mappings"
>
<
value
>
/BookService=bookService
/BookServiceWSS4J=bookServiceWSS4J
/BookServiceWSS4JEnc=bookServiceWSS4JEnc
/BookServiceWSS4JSign=bookServiceWSS4JSign
</
value
>
</
property
>
</
bean
>
<!--
(1)BookWebService 不需要认证
-->
<
bean
id
="bookService"
class
="org.codehaus.xfire.spring.remoting.XFireExporter"
>
<
property
name
="serviceFactory"
ref
="xfire.serviceFactory"
/>
<
property
name
="xfire"
ref
="xfire"
/>
<
property
name
="serviceBean"
ref
="bookManager"
/>
<
property
name
="serviceClass"
value
="org.springside.bookstore.plugins.xfire.service.BookService"
/>
</
bean
>
<!--
(3)BookWebService 使用 WSS4J验证
-->
<
bean
id
="bookServiceWSS4J"
class
="org.codehaus.xfire.spring.remoting.XFireExporter"
>
<
property
name
="serviceBean"
ref
="bookManager"
/>
<
property
name
="serviceClass"
value
="org.springside.bookstore.plugins.xfire.service.BookServiceWSS4J"
/>
<
property
name
="inHandlers"
>
<
list
>
<
ref
bean
="domInHandler"
/>
<
ref
bean
="wss4jInHandler"
/>
<
ref
bean
="validateUserTokenHandler"
/>
</
list
>
</
property
>
</
bean
>
<
bean
id
="domInHandler"
class
="org.codehaus.xfire.util.dom.DOMInHandler"
/>
<
bean
id
="wss4jInHandler"
class
="org.codehaus.xfire.security.wss4j.WSS4JInHandler"
>
<
property
name
="properties"
>
<
props
>
<
prop
key
="action"
>
UsernameToken
</
prop
>
<
prop
key
="passwordCallbackClass"
>
org.springside.bookstore.plugins.xfire.wss4j.PasswordHandler
</
prop
>
</
props
>
</
property
>
</
bean
>
<
bean
id
="validateUserTokenHandler"
class
="org.springside.bookstore.plugins.xfire.wss4j.WSS4JTokenHandler"
/>
<!--
(4)BookWebService 使用 WSS4J验证 Encrypt模式
-->
<
bean
id
="bookServiceWSS4JEnc"
class
="org.codehaus.xfire.spring.remoting.XFireExporter"
>
<
property
name
="serviceBean"
ref
="bookManager"
/>
<
property
name
="serviceClass"
value
="org.springside.bookstore.plugins.xfire.service.BookServiceWSS4JEnc"
/>
<
property
name
="inHandlers"
>
<
list
>
<
ref
bean
="domInHandler"
/>
<
ref
bean
="wss4jInHandlerEnc"
/>
<
ref
bean
="validateUserTokenHandler"
/>
</
list
>
</
property
>
</
bean
>
<
bean
id
="wss4jInHandlerEnc"
class
="org.codehaus.xfire.security.wss4j.WSS4JInHandler"
>
<
property
name
="properties"
>
<
props
>
<
prop
key
="action"
>
Encrypt
</
prop
>
<
prop
key
="decryptionPropFile"
>
org/springside/bookstore/plugins/xfire/wss4j/insecurity_enc.properties
</
prop
>
<
prop
key
="passwordCallbackClass"
>
org.springside.bookstore.plugins.xfire.wss4j.PasswordHandler
</
prop
>
</
props
>
</
property
>
</
bean
>
<!--
(5)BookWebService 使用 WSS4J验证 Signature模式
-->
<
bean
id
="bookServiceWSS4JSign"
class
="org.codehaus.xfire.spring.remoting.XFireExporter"
>
<
property
name
="serviceBean"
ref
="bookManager"
/>
<
property
name
="serviceClass"
value
="org.springside.bookstore.plugins.xfire.service.BookServiceWSS4JSign"
/>
<
property
name
="inHandlers"
>
<
list
>
<
ref
bean
="domInHandler"
/>
<
ref
bean
="wss4jInHandlerSign"
/>
<
ref
bean
="validateUserTokenHandler"
/>
</
list
>
</
property
>
</
bean
>
<
bean
id
="wss4jInHandlerSign"
class
="org.codehaus.xfire.security.wss4j.WSS4JInHandler"
>
<
property
name
="properties"
>
<
props
>
<
prop
key
="action"
>
Signature
</
prop
>
<
prop
key
="signaturePropFile"
>
org/springside/bookstore/plugins/xfire/wss4j/insecurity_sign.properties
</
prop
>
<
prop
key
="passwordCallbackClass"
>
org.springside.bookstore.plugins.xfire.wss4j.PasswordHandler
</
prop
>
</
props
>
</
property
>
</
bean
>
</
beans
>
第四,配置insecurity_enc.properties和insecurity_sign.properties两个密钥库配置文件
insecurity_enc.properties:
org.apache.ws.security.crypto.provider
=
org.apache.ws.security.components.crypto.Merlin
org.apache.ws.security.crypto.merlin.keystore.type
=
jks
org.apache.ws.security.crypto.merlin.keystore.password
=
SpringSide
org.apache.ws.security.crypto.merlin.alias.password
=
SpringSide
org.apache.ws.security.crypto.merlin.keystore.alias
=
david
org.apache.ws.security.crypto.merlin.file
=
org/springside/bookstore/plugins/xfire/wss4j/springside_private.jks
outsecurity_sign.properties:
org.apache.ws.security.crypto.provider
=
org.apache.ws.security.components.crypto.Merlin
org.apache.ws.security.crypto.merlin.keystore.type
=
jks
org.apache.ws.security.crypto.merlin.keystore.password
=
SpringSide
org.apache.ws.security.crypto.merlin.keystore.alias
=
david
org.apache.ws.security.crypto.merlin.file
=
org/springside/bookstore/plugins/xfire/wss4j/springside_public.jks
第五,使用SecureX生成了两个keystore文件
springside_private.jks
别名名称: david
创建日期:
2006
-
8
-
6
输入类型:KeyEntry
认证链长度:
1
认证
[
1
]
:
Owner: CN
=
david
,
OU
=
SpringSide
,
O
=
org
,
L
=
gz
,
ST
=
gd
,
C
=
cn
发照者: CN
=
david
,
OU
=
SpringSide
,
O
=
org
,
L
=
gz
,
ST
=
gd
,
C
=
cn
序号: 44d4cdcd
有效期间: Sun Aug
06
00
:
56
:
45
CST
2006
至: Mon Aug
06
00
:
56
:
45
CST
2007
认证指纹:
MD5: CF:
97
:
13
:0C:
70
:D0:4D:B6:B4:
27
:0F:1A:0B:CF:D9:F2
SHA1: 8E:8E:E8:BC:
64
:
39
:C8:
43
:E4:F7:1B:3B:CE:
48
:1D:6B:A0:2B:
58
:B5
springside_public.jks
别名名称: david
创建日期:
2006
-
8
-
6
输入类型: trustedCertEntry
Owner: CN
=
david
,
OU
=
SpringSide
,
O
=
org
,
L
=
gz
,
ST
=
gd
,
C
=
cn
发照者: CN
=
david
,
OU
=
SpringSide
,
O
=
org
,
L
=
gz
,
ST
=
gd
,
C
=
cn
序号: 44d4cdcd
有效期间: Sun Aug
06
00
:
56
:
45
CST
2006
至: Mon Aug
06
00
:
56
:
45
CST
2007
认证指纹:
MD5: CF:
97
:
13
:0C:
70
:D0:4D:B6:B4:
27
:0F:1A:0B:CF:D9:F2
SHA1: 8E:8E:E8:BC:
64
:
39
:C8:
43
:E4:F7:1B:3B:CE:
48
:1D:6B:A0:2B:
58
:B5
第五,新版本SpringSide需要
http://www.bouncycastle.org/download/bcprov-jdk15-133.jar
并且要配置java.security
另外,还要使用jdk加密增强策略
http://www.blogjava.net/openssl/archive/2006/03/08/34381.html
用户要使用WSS4J,需要配置Bouncycastle这个SecurityProvider,否则
运行Enc模式的XFire认证的时候,会抛出异常:
org.apache.ws.security.WSSecurityException: An unsupported signature or encryption algorithm was used unsupported key
配合java.security也是非常简单:
在最后加入BouncycastleProvider。
security.provider.1=sun.security.provider.Sun
security.provider.2=com.sun.net.ssl.internal.ssl.Provider
security.provider.3=com.sun.rsajca.Provider
security.provider.4=com.sun.crypto.provider.SunJCE
security.provider.5=sun.security.jgss.SunProvider
security.provider.6=org.bouncycastle.jce.provider.BouncyCastleProvider
[WebService Client端配置]
1,Encrypt模式的Client是在客户端用david的公钥加密Soap里面的usernameToken,然后发送到Web服务,Web服务用david的私钥来验证。这种模式需要客户端预先知道服务器端的公钥。
在Encrypt模式中,需要这样配置ClientHandler:
Service serviceModel
=
new
ObjectServiceFactory().create(BookServiceWSS4JEnc.
class
);
XFireProxyFactory factory
=
new
XFireProxyFactory(getXFire());
BookService service
=
(BookService) factory.create(serviceModel,
"
xfire.local://BookServiceWSS4JEnc
"
);
Client client
=
((XFireProxy) Proxy.getInvocationHandler(service)).getClient();
//
挂上WSS4JOutHandler,提供认证
client.addOutHandler(
new
DOMOutHandler());
Properties properties
=
new
Properties();
configureOutProperties(properties);
client.addOutHandler(
new
WSS4JOutHandler(properties));
List list
=
service.getAllCategorys();
configureOutProperties函数负责指定Client使用何种安全策略,没错,使用outsecurity_enc.properties,这个properties是跟Server端的insecurity_enc.properties一起使用的。
protected
void
configureOutProperties(Properties config) {
config.setProperty(WSHandlerConstants.ACTION, WSHandlerConstants.ENCRYPT);
config.setProperty(WSHandlerConstants.USER,
"
david
"
);
//
config.setProperty(WSHandlerConstants.PW_CALLBACK_CLASS, PasswordHandler.class.getName());
//
Configuration of public key used to encrypt message goes to properties file.
config.setProperty(WSHandlerConstants.ENC_PROP_FILE,
"
org/springside/bookstore/plugins/xfire/outsecurity_enc.properties
"
);
}
outsecurity_enc.properties:
org.apache.ws.security.crypto.provider
=
org.apache.ws.security.components.crypto.Merlin
org.apache.ws.security.crypto.merlin.keystore.type
=
jks
org.apache.ws.security.crypto.merlin.keystore.password
=
SpringSide
org.apache.ws.security.crypto.merlin.keystore.alias
=
david
org.apache.ws.security.crypto.merlin.file
=
org/springside/bookstore/plugins/xfire/wss4j/springside_public.jks
2, Sign模式的Client同样也是很简单,这种模式是Client端用自己的私钥为usernameToken签名,服务器端用Client的公钥来验证签名,因此,服务器端需要预先知道客户端的公钥。
对应于Encrypt模式,这里的configureOutProperties需要这样来配置:
protected
void
configureOutProperties(Properties properties) {
properties.setProperty(WSHandlerConstants.ACTION,WSHandlerConstants.SIGNATURE);
//
User in keystore
properties.setProperty(WSHandlerConstants.USER,
"
david
"
);
//
This callback is used to specify password for given user for keystore
properties.setProperty(WSHandlerConstants.PW_CALLBACK_CLASS, PasswordHandler.
class
.getName());
//
Configuration for accessing private key in keystore
properties.setProperty(WSHandlerConstants.SIG_PROP_FILE,
"
org/springside/bookstore/plugins/xfire/outsecurity_sign.properties
"
);
properties.setProperty(WSHandlerConstants.SIG_KEY_ID,
"
IssuerSerial
"
);
}
outsecurity_sign.properties:
org.apache.ws.security.crypto.provider
=
org.apache.ws.security.components.crypto.Merlin
org.apache.ws.security.crypto.merlin.keystore.type
=
jks
org.apache.ws.security.crypto.merlin.keystore.password
=