I have the following implementation in place for SOAP based web service and its security.
package com.hcentive.webservice.soap;
import org.apache.log4j.Logger;
import org.springframework.boot.context.embedded.ServletRegistrationBean;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.ImportResource;
import org.springframework.core.io.ClassPathResource;
import org.springframework.ws.config.annotation.EnableWs;
import org.springframework.ws.config.annotation.WsConfigurerAdapter;
import org.springframework.ws.transport.http.MessageDispatcherServlet;
import org.springframework.ws.wsdl.wsdl11.DefaultWsdl11Definition;
import org.springframework.xml.xsd.SimpleXsdSchema;
import org.springframework.xml.xsd.XsdSchema;
/**
* @author Mebin.Jacob
* Web service configuration class
* @since 03-Sept-2014
*/
@EnableWs
@Configuration
@ImportResource({"classpath:/spring-ws-servlet.xml","classpath:/security.xml"})
public class WebServiceConfig extends WsConfigurerAdapter {
Logger logger = Logger.getLogger(WebServiceConfig.class);
@Bean
public ServletRegistrationBean dispatcherServlet(ApplicationContext applicationContext) {
MessageDispatcherServlet servlet = new MessageDispatcherServlet();
servlet.setApplicationContext(applicationContext);
servlet.setTransformWsdlLocations(true);
return new ServletRegistrationBean(servlet, "/ws/*");
}
@Bean(name = "coa_application_service")
public DefaultWsdl11Definition defaultWsdl11Definition(XsdSchema coa_applicationSchema) {
DefaultWsdl11Definition wsdl11Definition = new DefaultWsdl11Definition();
wsdl11Definition.setPortTypeName("FormsPort");
wsdl11Definition.setLocationUri("/ws");
wsdl11Definition.setTargetNamespace("http://hcentive.com/service");
wsdl11Definition.setSchema(coa_applicationSchema);
return wsdl11Definition;
}
/*@Bean
public EmbeddedServletContainerFactory servletContainer(){
TomcatEmbeddedServletContainerFactory factory = new TomcatEmbeddedServletContainerFactory();
logger.info("Setting context path ");
factory.setContextPath("/spring-ws-servlet.xml");
return factory;
}
*/
@Bean
public XsdSchema coa_applicationSchema() {
return new SimpleXsdSchema(new ClassPathResource("coa_application_service.xsd"));
}
}
//spring-ws-servlet.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
http://www.springframework.org/schema/security/spring-security-3.1.xsd">
<bean id="wsSecurityInterceptor"
class="org.springframework.ws.soap.security.xwss.XwsSecurityInterceptor">
<property name="policyConfiguration" value="classpath:securityPolicy.xml" />
<property name="callbackHandlers">
<list>
<ref bean="keyStoreHandler" />
<ref bean="springCertificateHandler" />
</list>
</property>
</bean>
<bean id="keyStoreHandler"
class="org.springframework.ws.soap.security.xwss.callback.KeyStoreCallbackHandler">
<property name="keyStore" ref="keyStore" />
<property name="privateKeyPassword" value="changeit" />
</bean>
<bean id="keyStore"
class="org.springframework.ws.soap.security.support.KeyStoreFactoryBean">
<property name="password" value="changeit" />
<property name="location" value="classpath:/keystore.jks" />
</bean>
<bean id="springCertificateHandler"
class="org.springframework.ws.soap.security.xwss.callback.SpringCertificateValidationCallbackHandler">
<property name="authenticationManager">
<bean class="com.hcentive.security.SimpleAuthenticationManager" />
</property>
</bean>
</beans>
//security.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans:beans xmlns="http://www.springframework.org/schema/security"
xmlns:beans="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/security
http://www.springframework.org/schema/security/spring-security-3.2.xsd">
<http>
<!-- Every user request needs to be authenticated and authorized -->
<intercept-url pattern="/**" access="ROLE_USER"/>
<!-- x509 Pre authentication - pull username from the Common Name
field of the client certificate and extract the last 'word', in our client
cert example, 'John Smith jsmith', the username 'jsmith' -->
<x509 subject-principal-regex="CN=.*?\s(\w+)," />
<logout />
</http>
<authentication-manager>
<authentication-provider>
<user-service>
<user name="Jacob"
authorities="ROLE_USER, ROLE_ADMIN" />
</user-service>
</authentication-provider>
</authentication-manager>
</beans:beans>
and this is what my logs shows :-
Algorithm: [SHA256withRSA]
Signature:
0000: 48 85 7A CA A7 1D DC 13 D1 0D 3D EB 10 20 A8 0E H.z.......=.. ..
0010: 1D 2A 82 AF 0D 40 04 1A 4F 57 16 AF CF 2A 13 22 .*...@..OW...*."
0020: 79 A4 EB 81 29 13 56 94 78 AF EF D6 BE 3C B2 1B y...).V.x....<..
0030: 7C B2 CC B6 69 00 56 41 58 A1 E5 03 5A 27 5B 97 ....i.VAX...Z'[.
0040: A5 49 8A 71 9C 78 13 0C 2D BC BD AE E5 26 52 9F .I.q.x..-....&R.
0050: 8B EA 43 91 E0 DA 46 D2 15 C5 8B 23 E2 98 2E 9E ..C...F....#....
0060: 67 80 07 C1 94 76 E8 89 8F E6 10 D2 C4 3A 41 80 g....v.......:A.
0070: A6 CA E9 59 4F A2 3C 6A FB D9 71 C7 29 0A A1 83 ...YO.<j..q.)...
0080: D9 9E DE 09 F7 AC 18 EE CE B2 A5 07 0C 0F C5 92 ................
0090: A8 82 3D 61 10 00 5A EC 90 E9 E7 94 72 CA 93 AA ..=a..Z.....r...
00A0: 04 C3 83 61 4C 4D AD 08 D2 54 A8 96 58 5A 06 D3 ...aLM...T..XZ..
00B0: C2 0F 52 24 B1 7A EF 7C 52 27 92 88 34 EE 44 83 ..R$.z..R'..4.D.
00C0: E0 88 94 40 03 7A C3 7B C8 D8 51 5A 0B 56 D3 22 ...@.z....QZ.V."
00D0: 36 11 04 B1 C0 8C F7 B3 B3 7C 4C F1 FF A4 50 41 6.........L...PA
00E0: 4A EE FC 2D 7C DE F2 54 3F 8A 38 B7 BF E3 6C 89 J..-...T?.8...l.
00F0: D1 3D 34 55 F5 F9 10 4B 77 A9 36 BF 23 5E 87 B4 .=4U...Kw.6.#^..
]
preAuthenticatedPrincipal = Jacob, trying to authenticate
Authentication attempt using org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationProvider
PreAuthenticated authentication request: org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationToken@fbbdfcac: Principal: Jacob; Credentials: [PROTECTED]; Authenticated: false; Details: org.springframework.security.web.authentication.WebAuthenticationDetails@957e: RemoteIpAddress: 127.0.0.1; SessionId: null; Not granted any authorities
Authentication success: org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationToken@fcc8a5: Principal: org.springframework.security.core.userdetails.User@44064bf: Username: Jacob; Password: [PROTECTED]; Enabled: true; AccountNonExpired: true; credentialsNonExpired: true; AccountNonLocked: true; Granted Authorities: ROLE_ADMIN,ROLE_USER; Credentials: [PROTECTED]; Authenticated: true; Details: org.springframework.security.web.authentication.WebAuthenticationDetails@957e: RemoteIpAddress: 127.0.0.1; SessionId: null; Granted Authorities: ROLE_ADMIN, ROLE_USER
/ at position 5 of 10 in additional filter chain; firing Filter: 'RequestCacheAwareFilter'
/ at position 6 of 10 in additional filter chain; firing Filter: 'SecurityContextHolderAwareRequestFilter'
/ at position 7 of 10 in additional filter chain; firing Filter: 'AnonymousAuthenticationFilter'
SecurityContextHolder not populated with anonymous token, as it already contained: 'org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationToken@fcc8a5: Principal: org.springframework.security.core.userdetails.User@44064bf: Username: Jacob; Password: [PROTECTED]; Enabled: true; AccountNonExpired: true; credentialsNonExpired: true; AccountNonLocked: true; Granted Authorities: ROLE_ADMIN,ROLE_USER; Credentials: [PROTECTED]; Authenticated: true; Details: org.springframework.security.web.authentication.WebAuthenticationDetails@957e: RemoteIpAddress: 127.0.0.1; SessionId: null; Granted Authorities: ROLE_ADMIN, ROLE_USER'
/ at position 8 of 10 in additional filter chain; firing Filter: 'SessionManagementFilter'
Delegating to org.springframework.security.web.authentication.session.SessionFixationProtectionStrategy@11664436
HttpSession being created as SecurityContext is non-default
SecurityContext stored to HttpSession: 'org.springframework.security.core.context.SecurityContextImpl@fcc8a5: Authentication: org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationToken@fcc8a5: Principal: org.springframework.security.core.userdetails.User@44064bf: Username: Jacob; Password: [PROTECTED]; Enabled: true; AccountNonExpired: true; credentialsNonExpired: true; AccountNonLocked: true; Granted Authorities: ROLE_ADMIN,ROLE_USER; Credentials: [PROTECTED]; Authenticated: true; Details: org.springframework.security.web.authentication.WebAuthenticationDetails@957e: RemoteIpAddress: 127.0.0.1; SessionId: null; Granted Authorities: ROLE_ADMIN, ROLE_USER'
/ at position 9 of 10 in additional filter chain; firing Filter: 'ExceptionTranslationFilter'
/ at position 10 of 10 in additional filter chain; firing Filter: 'FilterSecurityInterceptor'
Secure object: FilterInvocation: URL: /; Attributes: [ROLE_USER]
Previously Authenticated: org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationToken@fcc8a5: Principal: org.springframework.security.core.userdetails.User@44064bf: Username: Jacob; Password: [PROTECTED]; Enabled: true; AccountNonExpired: true; credentialsNonExpired: true; AccountNonLocked: true; Granted Authorities: ROLE_ADMIN,ROLE_USER; Credentials: [PROTECTED]; Authenticated: true; Details: org.springframework.security.web.authentication.WebAuthenticationDetails@957e: RemoteIpAddress: 127.0.0.1; SessionId: null; Granted Authorities: ROLE_ADMIN, ROLE_USER
Voter: org.springframework.security.access.vote.RoleVoter@6290f34d, returned: 1
Authorization successful
RunAsManager did not change Authentication object
/ reached end of additional filter chain; proceeding with original chain
SecurityContext stored to HttpSession: 'org.springframework.security.core.context.SecurityContextImpl@fcc8a5: Authentication: org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationToken@fcc8a5: Principal: org.springframework.security.core.userdetails.User@44064bf: Username: Jacob; Password: [PROTECTED]; Enabled: true; AccountNonExpired: true; credentialsNonExpired: true; AccountNonLocked: true; Granted Authorities: ROLE_ADMIN,ROLE_USER; Credentials: [PROTECTED]; Authenticated: true; Details: org.springframework.security.web.authentication.WebAuthenticationDetails@957e: RemoteIpAddress: 127.0.0.1; SessionId: null; Granted Authorities: ROLE_ADMIN, ROLE_USER'
Chain processed normally
SecurityContextHolder now cleared, as request processing completed
/error at position 1 of 10 in additional filter chain; firing Filter: 'SecurityContextPersistenceFilter'
Obtained a valid SecurityContext from SPRING_SECURITY_CONTEXT: 'org.springframework.security.core.context.SecurityContextImpl@fcc8a5: Authentication: org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationToken@fcc8a5: Principal: org.springframework.security.core.userdetails.User@44064bf: Username: Jacob; Password: [PROTECTED]; Enabled: true; AccountNonExpired: true; credentialsNonExpired: true; AccountNonLocked: true; Granted Authorities: ROLE_ADMIN,ROLE_USER; Credentials: [PROTECTED]; Authenticated: true; Details: org.springframework.security.web.authentication.WebAuthenticationDetails@957e: RemoteIpAddress: 127.0.0.1; SessionId: null; Granted Authorities: ROLE_ADMIN, ROLE_USER'
/error at position 2 of 10 in additional filter chain; firing Filter: 'WebAsyncManagerIntegrationFilter'
/error at position 3 of 10 in additional filter chain; firing Filter: 'LogoutFilter'
/error at position 4 of 10 in additional filter chain; firing Filter: 'X509AuthenticationFilter'
Checking secure context token: org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationToken@fcc8a5: Principal: org.springframework.security.core.userdetails.User@44064bf: Username: Jacob; Password: [PROTECTED]; Enabled: true; AccountNonExpired: true; credentialsNonExpired: true; AccountNonLocked: true; Granted Authorities: ROLE_ADMIN,ROLE_USER; Credentials: [PROTECTED]; Authenticated: true; Details: org.springframework.security.web.authentication.WebAuthenticationDetails@957e: RemoteIpAddress: 127.0.0.1; SessionId: null; Granted Authorities: ROLE_ADMIN, ROLE_USER
/error at position 5 of 10 in additional filter chain; firing Filter: 'RequestCacheAwareFilter'
/error at position 6 of 10 in additional filter chain; firing Filter: 'SecurityContextHolderAwareRequestFilter'
/error at position 7 of 10 in additional filter chain; firing Filter: 'AnonymousAuthenticationFilter'
SecurityContextHolder not populated with anonymous token, as it already contained: 'org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationToken@fcc8a5: Principal: org.springframework.security.core.userdetails.User@44064bf: Username: Jacob; Password: [PROTECTED]; Enabled: true; AccountNonExpired: true; credentialsNonExpired: true; AccountNonLocked: true; Granted Authorities: ROLE_ADMIN,ROLE_USER; Credentials: [PROTECTED]; Authenticated: true; Details: org.springframework.security.web.authentication.WebAuthenticationDetails@957e: RemoteIpAddress: 127.0.0.1; SessionId: null; Granted Authorities: ROLE_ADMIN, ROLE_USER'
/error at position 8 of 10 in additional filter chain; firing Filter: 'SessionManagementFilter'
/error at position 9 of 10 in additional filter chain; firing Filter: 'ExceptionTranslationFilter'
/error at position 10 of 10 in additional filter chain; firing Filter: 'FilterSecurityInterceptor'
/error reached end of additional filter chain; proceeding with original chain
Chain processed normally
SecurityContextHolder now cleared, as request processing completed
The authorization and access seems to be fine or perhaps I misunderstand something?? But the request does not seem to be going forward to my SOAP endpoint. I am a newbee with spring ws, spring boot. Have been stuck with this for a while. ....
Looks like after the loading of the filters the call to the messageDispatcherservlet is not made. Anyone any clue why that is not happening. I don't see any errors in my log!!!
Thanks, Mebin
I think you are mixing up two sorts of security here. In
security.xml
, you have enabled HTTP-based security with Spring Security, which operates on the HTTP transport layer only. InWebServiceConfig
, you have enabled WS-Security with Spring Web Services, which operates on the SOAP message level. It's wise to pick one of the two, you probably want to have only WS-Security enabled.Check here for a sample that uses WS-Security in a Spring Boot app. Specifically, see WebServiceServerConfig.