Archive for November 2008
WebServiceTemplate and Timeout
Spring WS 1.0 API doesn’t support timeout for web service invocations. This feature can be achieved by using Jakarta Commons HttpClient. SSL timeout requires moreover the use of Not Yet Commons SSL (in fact, this product is just only an extension of JSSE).
Below a sample class and a sample application context configuration.
import java.io.IOException;
import java.io.File;
// commons-httpclient
import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.protocol.Protocol;
import org.apache.commons.httpclient.protocol.ProtocolSocketFactory;
// not-yet-commons-ssl
import org.apache.commons.ssl.HttpSecureProtocol;
import org.apache.commons.ssl.KeyMaterial;
import org.apache.commons.ssl.TrustMaterial;
// spring ws
import org.springframework.oxm.xmlbeans.XmlBeansMarshaller;
import org.springframework.ws.WebServiceMessage;
import org.springframework.ws.client.core.WebServiceMessageCallback;
import org.springframework.ws.client.core.WebServiceTemplate;
import org.springframework.ws.client.core.support.WebServiceGatewaySupport;
import org.springframework.ws.transport.http.CommonsHttpMessageSender;
public class SpringWSClient extends WebServiceGatewaySupport {
// Marshalling
private XmlBeansMarshaller xmlBeansMarshaller;
// Truststore
private String pathToJksTrustStore;
private String jksTrustStorePassword;
// Keystore
private String pathToJksKeyStore;
private String jksKeyStorePassword;
// Timeout
private int timeOutMs;
// SSL Port
private static final int SSL_PORT = 443;
// Default constructor
public SpringWSClient(XmlBeansMarshaller xmlBeansMarshaller) {
this.xmlBeansMarshaller = xmlBeansMarshaller;
}
// Do WS invocation
public Object callWs(Object input) {
// SSL connection?
if (getWebServiceTemplate().getDefaultUri().toLowerCase().startsWith(
"https")) {
sslProtocolInit();
}
// Marshalling stuff (XMLBeans)
WebServiceTemplate wst = getWebServiceTemplate();
wst.setMarshaller(getXmlBeansMarshaller());
wst.setUnmarshaller(getXmlBeansMarshaller());
// Timeout (commons-httpclient)
HttpClient client = new HttpClient();
client.getParams().setSoTimeout(new Integer(getTimeOutMs()));
CommonsHttpMessageSender messageSender = new CommonsHttpMessageSender(
client);
wst.setMessageSender(messageSender);
// Invocation
Object result = wst.marshalSendAndReceive(input,
new WebServiceMessageCallback() {
public void doWithMessage(WebServiceMessage message)
throws IOException {
// ...
}
});
return result;
}
// SSL protocol (not-yet-commons-ssl)
protected void sslProtocolInit() {
HttpSecureProtocol protocolSocketFactory;
try {
protocolSocketFactory = new HttpSecureProtocol();
File jksTrustStore = new File(pathToJksTrustStore);
TrustMaterial trustMaterial = new TrustMaterial(jksTrustStore,
jksTrustStorePassword.toCharArray());
protocolSocketFactory.addTrustMaterial(trustMaterial);
// No host name verification
protocolSocketFactory.setCheckHostname(false);
File jksKeyStore = new File(pathToJksKeyStore);
KeyMaterial key = new KeyMaterial(jksKeyStore, jksKeyStorePassword
.toCharArray());
protocolSocketFactory.setKeyMaterial(key);
// Timeout
protocolSocketFactory.setConnectTimeout(getTimeOutMs());
// Register protocol
Protocol protocol = new Protocol("https",
(ProtocolSocketFactory) protocolSocketFactory, SSL_PORT);
Protocol.registerProtocol("https", protocol);
} catch (Exception e) {
e.printStackTrace();
}
}
public XmlBeansMarshaller getXmlBeansMarshaller() {
return xmlBeansMarshaller;
}
public void setXmlBeansMarshaller(XmlBeansMarshaller xmlBeansMarshaller) {
this.xmlBeansMarshaller = xmlBeansMarshaller;
}
public int getTimeOutMs() {
return timeOutMs;
}
public void setTimeOutMs(int timeOutMs) {
this.timeOutMs = timeOutMs;
}
public String getPathToJksFile() {
return pathToJksFile;
}
public void setPathToJksFile(String pathToJksFile) {
this.pathToJksFile = pathToJksFile;
}
public String getJksPassword() {
return jksPassword;
}
public void setJksPassword(String jksPassword) {
this.jksPassword = jksPassword;
}
}
<bean id="springWSClient" class="SpringWSClient">
<constructor -arg ref="xmlBeansMarshaller"/>
<property name="defaultUri" value="${ws.url}" />
<property name="pathToJksTrustStore" value="${trustStore.location}" />
<property name="jksTrustStorePassword" value="${trustStore.password}" />
<property name="pathToJksKeyStore" value="${keyStore.location}" />
<property name="jksKeyStorePassword" value="${keyStore.password}" />
<property name="timeOutMs" value="${ws.timeout}" />
</bean>
Customize target URL according to the user ROLE with Spring Security 2
It’s there some idea about this issue, but I think there’s no simple solution exposed.
You can achieve this behaviour by modifying applicationContext.xml and by extending Spring’s AuthenticationProcessingFilter.
applicationContext.xml
<!-- Spring Security Authentication --> <http auto-config="false" entry-point-ref="authenticationProcessingFilterEntryPoint"> <!-- LITERAL user can only access to literal pages --> <intercept -url pattern="/*iteral.htm" access="ROLE_LITERAL,ROLE_ADMIN" /> <!-- ADMIN user can access everywhere --> <intercept -url pattern="/**.htm" access="ROLE_ADMIN" /> </http> <!-- User list --> <authentication -provider> <password -encoder hash="md5"/> <user -service> <user name="admin" password="21232f297a57a5a743894a0e4a801fc3" authorities="ROLE_ADMIN" /> <user name="literals" password="b284ec8f1c7a6208901d2a5d27d17a32" authorities="ROLE_LITERAL" /> </user> </authentication> <!-- Entry point properties --> <authentication -manager alias="authenticationManagerAlias"/> <bean id="authenticationProcessingFilterEntryPoint" class="org.springframework.security.ui.webapp.AuthenticationProcessingFilterEntryPoint"> <property name="loginFormUrl" value="/login.htm"/> <property name="forceHttps" value="false" /> </bean> < -- Custom Authentication Processing Filter --> <bean id="customAuthenticationProcessingFilter" class="CustomAuthenticationProcessingFilter"> <property name="filterProcessesUrl" value="/j_spring_security_check" /> <property name="defaultTargetUrl" value="/index.sdf"/> <property name="authenticationManager" ref="authenticationManagerAlias"/> <custom -filter position="AUTHENTICATION_PROCESSING_FILTER"/> </bean>
Custom Authentication Processing Filter
public class CustomAuthenticationProcessingFilter extends AuthenticationProcessingFilter {
public static final String ROLE_ADMIN = "ROLE_ADMIN";
private static final String ROLE_ADMIN_TARGET_URL = "/index.htm";
private static final String NON_ROLE_ADMIN_TARGET_URL = "/literal.htm";
@Override
protected String determineTargetUrl(HttpServletRequest request) {
boolean isAdmin = hasRole(ROLE_ADMIN);
String targetUrl;
if (isAdmin) {
targetUrl = ROLE_ADMIN_TARGET_URL;
} else {
targetUrl = NON_ROLE_ADMIN_TARGET_URL;
}
return targetUrl;
}
/**
* Returns true is user authenticated has role
*/
public static boolean hasRole(String role) {
boolean userHasRole = false;
GrantedAuthority[] grantedAuthorities = SecurityContextHolder.getContext().getAuthentication().getAuthorities();
for (GrantedAuthority grantedAuthority : grantedAuthorities) {
if (role.equals(grantedAuthority.toString())) {
userHasRole = true;
break;
}
}
return userHasRole;
}
}
It seems simple, however, it becomes complex because of Spring Security documentation, which is results very poor for integration purposes.