Alright, I've got a basic example working now. I had to create a custom interceptor to pass authentication information to Spring Security. Here's the code:
------- cxf.xml ------- <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:security="http://www.springframework.org/schema/security" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:jaxrs="http://cxf.apache.org/jaxrs" xmlns:jaxws="http://cxf.apache.org/jaxws" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://cxf.apache.org/jaxrs http://cxf.apache.org/schemas/jaxrs.xsd http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-2.0.1.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.0.xsd http://cxf.apache.org/jaxws http://cxf.apache.org/schemas/jaxws.xsd"> <import resource="classpath:META-INF/cxf/cxf.xml" /> <import resource="classpath:META-INF/cxf/cxf-extension-jaxrs-binding.xml"/> <import resource="classpath:META-INF/cxf/cxf-extension-soap.xml"/> <import resource="classpath:META-INF/cxf/cxf-servlet.xml" /> <jaxws:endpoint id="auth" implementor="com.company.auth.service.AuthServiceImpl" address="/corporateAuth"> <jaxws:inInterceptors> <bean class="org.apache.cxf.ws.security.wss4j.WSS4JInInterceptor"> <constructor-arg> <map> <entry key="action" value="UsernameToken" /> <entry key="passwordType" value="PasswordText" /> <entry key="passwordCallbackClass" value="com.company.auth.service.ServerPasswordCallback"/> </map> </constructor-arg> </bean> <bean class="com.company.service.web.auth.WSAuthenticationInterceptor"> <property name="authenticationManager" ref="authenticationManager"/> </bean> </jaxws:inInterceptors> </jaxws:endpoint> <security:authentication-manager alias="authenticationManager"/> <security:authentication-provider user-service-ref="myUserDetailsService"/> <security:global-method-security secured-annotations="enabled"/> <bean id="springSecurityFilterChain" class="org.springframework.security.util.FilterChainProxy"> <security:filter-chain-map path-type="ant"> <security:filter-chain pattern="/**" filters="contextIntegrationFilter"/> </security:filter-chain-map> </bean> <bean id="contextIntegrationFilter" class="org.springframework.security.context.HttpSessionContextIntegrationFilter" /> <bean id="myUserDetailsService" class="com.company.service.web.auth.AuthenticationDetailsService"/> </beans> ------- WSAuthenticationInterceptor.java ------- package com.company.service.web.auth; import java.util.Vector; import org.apache.cxf.binding.soap.SoapMessage; import org.apache.cxf.interceptor.Fault; import org.apache.cxf.phase.AbstractPhaseInterceptor; import org.apache.cxf.phase.Phase; import org.apache.ws.security.WSConstants; import org.apache.ws.security.WSSecurityEngineResult; import org.apache.ws.security.WSUsernameTokenPrincipal; import org.apache.ws.security.handler.WSHandlerConstants; import org.apache.ws.security.handler.WSHandlerResult; import org.springframework.beans.factory.InitializingBean; import org.springframework.security.Authentication; import org.springframework.security.AuthenticationManager; import org.springframework.security.context.SecurityContextHolder; import org.springframework.security.providers.UsernamePasswordAuthenticationToken; import org.springframework.util.Assert; public class WSAuthenticationInterceptor extends AbstractPhaseInterceptor<SoapMessage> implements InitializingBean { private AuthenticationManager authenticationManager; public WSAuthenticationInterceptor() { super( Phase.POST_STREAM ); } public void afterPropertiesSet() throws Exception { // ensure the 2 objects we need are not null Assert.notNull( authenticationManager, "Authentication Manager should not be null!" ); } public void handleMessage( SoapMessage message ) throws Fault { // get out the results from the message context Vector<WSHandlerResult> result = (Vector<WSHandlerResult>) message.getContextualProperty( WSHandlerConstants.RECV_RESULTS ); for (WSHandlerResult res : result) { // loop through security engine results for (WSSecurityEngineResult securityResult : (Vector<WSSecurityEngineResult>) res.getResults()) { int action = securityResult.getAction(); // determine if the action was a username token if ( ( action & WSConstants.UT ) > 0 ) { // get the principal object WSUsernameTokenPrincipal principal = (WSUsernameTokenPrincipal) securityResult.getPrincipal(); Authentication authentication = new UsernamePasswordAuthenticationToken( principal.getName(), principal.getPassword() ); authentication = authenticationManager.authenticate( authentication ); if ( !authentication.isAuthenticated() ) { System.out.println( "This user is not authentic." ); //throw new AuthenticationException( "This user is not authentic." ); } SecurityContextHolder.getContext().setAuthentication( authentication ); } } } } /** * @return the authenticationManager */ public AuthenticationManager getAuthenticationManager() { return authenticationManager; } /** * @param authenticationManager the authenticationManager to set */ public void setAuthenticationManager( AuthenticationManager authenticationManager ) { this.authenticationManager = authenticationManager; } } ------- ServerPasswordCallback.java ------- package com.company.auth.service; import java.io.IOException; import javax.security.auth.callback.Callback; import javax.security.auth.callback.CallbackHandler; import javax.security.auth.callback.UnsupportedCallbackException; import org.apache.ws.security.WSPasswordCallback; public class ServerPasswordCallback implements CallbackHandler { public void handle( Callback[] callbacks ) throws IOException, UnsupportedCallbackException { WSPasswordCallback pc = (WSPasswordCallback) callbacks[0]; // this seems ridiculous, but is necessary for passing authentication on to Spring-Security. // we're essentially bypassing CXF's WSS4JInterceptor by ensuring that the password callback always matches the client password. pc.setPassword( pc.getPassword() ); } } I hope this is useful to someone else! ;) - Dave dclane wrote: > > I've successfully implemented Spring Security in my CXF project, but I > really need to get it hooked in with WS-Security to allow client > applications to authenticate. (Spring Security is using HTTP form > authentication right now, which is fine in a web browser, but I need this > to work for service requests.) > > I'm wanting to use UsernameToken authentication, with WS-Security passing > the authentication information on to Spring Security. I've done a lot of > searching and it sounds easy enough when using the Spring Web Services > framework, but I don't know how it can be done in CXF. > > I already have a simple WS-Security service and client working, I just > don't know how to integrate that with Spring Security. > > Has anyone attempted this before? Could someone give me a few pointers? > :) > > Thank you, > - Dave > -- View this message in context: http://www.nabble.com/Attempting-WS-Security-with-Spring-Security-tp20863276p20902112.html Sent from the cxf-user mailing list archive at Nabble.com.
