Spring 使用 Spring 启动 在 XwsSecurityInterceptor 中使用 SpringPlainTextPasswordValidationCallbackHandler 时安全验证失败
Spring boot using Spring Security authentication failure when using SpringPlainTextPasswordValidationCallbackHandler in an XwsSecurityInterceptor
我已经设置了一个 spring 引导 (1.2.3) 应用程序,具有 spring 安全性和 spring-ws。我已配置 spring 安全性以在我的 WebSecurityConfigurerAdapter 中使用 .ldapAuthentication() 进行身份验证。我正在尝试使用我的 WsConfigurerAdapter 中的 ws-security usernametokens(纯文本)获得相同的 spring security authenticationManager 来验证我的 spring ws SOAP Web 服务。
我已经像这样配置了我的 WebSecurityConfigurerAdapter:
package za.co.switchx.config;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.ldap.core.support.LdapContextSource;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Bean
@ConfigurationProperties(prefix="ldap.contextSource")
public LdapContextSource contextSource() {
LdapContextSource contextSource = new LdapContextSource();
return contextSource;
}
@Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth
.ldapAuthentication()
.userSearchBase("cn=Users,dc=SwitchX,dc=co,dc=za")
.userSearchFilter("(uid={0})")
.groupSearchBase("cn=Groups,dc=SwitchX,dc=co,dc=za")
.groupSearchFilter("(&(cn=*)(| (objectclass=groupofUniqueNames)(objectclass=orcldynamicgroup)))")
.contextSource(contextSource());
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/ws/**").permitAll()
.anyRequest().authenticated()
.and()
.csrf().disable()
.httpBasic();
}
}
然后我像这样配置我的 WsConfigurerAdapter:
package za.co.switchx.config;
import java.util.List;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
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.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;
import org.springframework.ws.soap.security.xwss.XwsSecurityInterceptor;
import org.springframework.ws.soap.security.xwss.callback.SpringPlainTextPasswordValidationCallbackHandler;
import org.springframework.ws.server.EndpointInterceptor;
@EnableWs
@Configuration
public class WebServiceConfig extends WsConfigurerAdapter {
@Bean
public ServletRegistrationBean messageDispatcherServlet(ApplicationContext applicationContext) {
MessageDispatcherServlet servlet = new MessageDispatcherServlet();
servlet.setApplicationContext(applicationContext);
servlet.setTransformWsdlLocations(true);
return new ServletRegistrationBean(servlet, "/ws/*");
}
@Bean(name = "ApplicantTypeService")
public DefaultWsdl11Definition defaultWsdl11Definition(XsdSchema applicantTypeServiceSchema) {
DefaultWsdl11Definition wsdl11Definition = new DefaultWsdl11Definition();
wsdl11Definition.setPortTypeName("ApplicantTypePort");
wsdl11Definition.setLocationUri("/ws/ApplicantTypeService");
wsdl11Definition.setTargetNamespace("http://switchx.co.za/services/applicant/types/applicant-type-web-service");
wsdl11Definition.setSchema(applicantTypeServiceSchema);
return wsdl11Definition;
}
@Bean
public XsdSchema applicantTypeSchema() {
return new SimpleXsdSchema(new ClassPathResource("xsd/ApplicantTypeService.xsd"));
}
@Bean
public XwsSecurityInterceptor securityInterceptor() {
XwsSecurityInterceptor securityInterceptor = new XwsSecurityInterceptor();
securityInterceptor.setCallbackHandler(new SpringPlainTextPasswordValidationCallbackHandler());
securityInterceptor.setPolicyConfiguration(new ClassPathResource("securityPolicy.xml"));
return securityInterceptor;
}
@Override
public void addInterceptors(List<EndpointInterceptor> interceptors) {
interceptors.add(securityInterceptor());
}
}
如果我在 XwsSecurityInterceptor 中使用 SimplePasswordValidationCallbackHandler,它会正确验证 ws usernametoken,所以我知道 ws-security 部分没有任何问题。如果我通过 http basic 登录,它会正确验证我的 ldap 用户,所以我知道这有效。
问题是,当我尝试在 ws security usernametoken 中使用我的 ldap 用户登录时,我在日志中得到 ERROR c.s.xml.wss.logging.impl.filter - WSS1408: UsernameToken Authentication Failed
,所以看起来它没有使用我在 WebSecurityConfigAdapter[=15 中定义的全局 ldap 身份验证=]
我似乎无法弄清楚如何让 XwsSecurityInterceptor 中的 SpringPlainTextPasswordValidationCallbackHandler(应该使用 spring 安全)来使用全局 authenticationManager,请帮忙??最后一天我真的一直在抨击这个,但似乎赢不了。
好的,我想通了,所以我会 post 为将来尝试此操作的任何人提供帮助。
我通过将 spring 引导 class 更改为:
解决了这个问题
@SpringBootApplication
@EnableGlobalMethodSecurity(securedEnabled = true)
public class SwitchxApplication extends WebMvcConfigurerAdapter {
@SuppressWarnings("unused")
private static final Logger log = LoggerFactory.getLogger(SwitchxApplication.class);
@Bean
public ApplicationSecurity applicationSecurity() {
return new ApplicationSecurity();
}
@Configuration
@Order(Ordered.HIGHEST_PRECEDENCE)
protected static class AuthenticationConfiguration extends GlobalAuthenticationConfigurerAdapter {
@Bean
@ConfigurationProperties(prefix="ldap.contextSource")
public LdapContextSource contextSource() {
LdapContextSource contextSource = new LdapContextSource();
return contextSource;
}
@Override
public void init(AuthenticationManagerBuilder auth) throws Exception {
auth
.ldapAuthentication()
.userSearchBase("cn=Users,dc=Blah,dc=co,dc=za")
.userSearchFilter("(uid={0})")
.groupSearchBase("cn=Groups,dc=Blah,dc=co,dc=za")
.groupSearchFilter("(&(cn=*)(|(objectclass=groupofUniqueNames)(objectclass=orcldynamicgroup)))")
.contextSource(contextSource());
}
}
@Order(Ordered.LOWEST_PRECEDENCE - 8)
protected static class ApplicationSecurity extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/ws/**").permitAll()
.anyRequest().authenticated()
.and()
.csrf().disable()
.httpBasic();
}
}
public static void main(String[] args) {
SpringApplication.run(SwitchxApplication.class, args);
}
}
然后在我的 WsConfigurerAdapter 中进行以下相关更改:
@EnableWs
@Configuration
public class WebServiceConfig extends WsConfigurerAdapter {
private static final Logger log = LoggerFactory.getLogger(WebServiceConfig.class);
@Autowired
private AuthenticationManager authenticationManager;
@Bean
public ServletRegistrationBean messageDispatcherServlet(ApplicationContext applicationContext) {
MessageDispatcherServlet servlet = new MessageDispatcherServlet();
servlet.setApplicationContext(applicationContext);
servlet.setTransformWsdlLocations(true);
return new ServletRegistrationBean(servlet, "/ws/*");
}
.....
.....
@Bean
public SpringPlainTextPasswordValidationCallbackHandler callbackHandler() {
SpringPlainTextPasswordValidationCallbackHandler callbackHandler = new SpringPlainTextPasswordValidationCallbackHandler();
try {
callbackHandler.setAuthenticationManager(authenticationManager);
} catch(Exception e) {
log.error(e.getMessage());
}
return callbackHandler;
}
@Bean
public XwsSecurityInterceptor securityInterceptor() {
XwsSecurityInterceptor securityInterceptor = new XwsSecurityInterceptor();
securityInterceptor.setCallbackHandler(callbackHandler());
securityInterceptor.setPolicyConfiguration(new ClassPathResource("securityPolicy.xml"));
return securityInterceptor;
}
@Override
public void addInterceptors(List<EndpointInterceptor> interceptors) {
interceptors.add(securityInterceptor());
}
}
所以基本上最终结果是对于所有 /ws 路径,基本的 http 安全性被忽略,但是由于 WS Config 中的安全拦截器,它将使用基本的 ws-security 用户名令牌来验证 Web 服务调用,允许您使用 spring 安全设置和 ldap.
两种身份验证机制
我希望这对某人有所帮助,但在引导和 java 配置文档中找不到关于此特定设置的大量文档有点棘手,因为它仍然相对较新。但是在没有得到这个工作之后,它非常棒,给我留下了深刻的印象。
我已经设置了一个 spring 引导 (1.2.3) 应用程序,具有 spring 安全性和 spring-ws。我已配置 spring 安全性以在我的 WebSecurityConfigurerAdapter 中使用 .ldapAuthentication() 进行身份验证。我正在尝试使用我的 WsConfigurerAdapter 中的 ws-security usernametokens(纯文本)获得相同的 spring security authenticationManager 来验证我的 spring ws SOAP Web 服务。
我已经像这样配置了我的 WebSecurityConfigurerAdapter:
package za.co.switchx.config;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.ldap.core.support.LdapContextSource;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Bean
@ConfigurationProperties(prefix="ldap.contextSource")
public LdapContextSource contextSource() {
LdapContextSource contextSource = new LdapContextSource();
return contextSource;
}
@Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth
.ldapAuthentication()
.userSearchBase("cn=Users,dc=SwitchX,dc=co,dc=za")
.userSearchFilter("(uid={0})")
.groupSearchBase("cn=Groups,dc=SwitchX,dc=co,dc=za")
.groupSearchFilter("(&(cn=*)(| (objectclass=groupofUniqueNames)(objectclass=orcldynamicgroup)))")
.contextSource(contextSource());
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/ws/**").permitAll()
.anyRequest().authenticated()
.and()
.csrf().disable()
.httpBasic();
}
}
然后我像这样配置我的 WsConfigurerAdapter:
package za.co.switchx.config;
import java.util.List;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
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.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;
import org.springframework.ws.soap.security.xwss.XwsSecurityInterceptor;
import org.springframework.ws.soap.security.xwss.callback.SpringPlainTextPasswordValidationCallbackHandler;
import org.springframework.ws.server.EndpointInterceptor;
@EnableWs
@Configuration
public class WebServiceConfig extends WsConfigurerAdapter {
@Bean
public ServletRegistrationBean messageDispatcherServlet(ApplicationContext applicationContext) {
MessageDispatcherServlet servlet = new MessageDispatcherServlet();
servlet.setApplicationContext(applicationContext);
servlet.setTransformWsdlLocations(true);
return new ServletRegistrationBean(servlet, "/ws/*");
}
@Bean(name = "ApplicantTypeService")
public DefaultWsdl11Definition defaultWsdl11Definition(XsdSchema applicantTypeServiceSchema) {
DefaultWsdl11Definition wsdl11Definition = new DefaultWsdl11Definition();
wsdl11Definition.setPortTypeName("ApplicantTypePort");
wsdl11Definition.setLocationUri("/ws/ApplicantTypeService");
wsdl11Definition.setTargetNamespace("http://switchx.co.za/services/applicant/types/applicant-type-web-service");
wsdl11Definition.setSchema(applicantTypeServiceSchema);
return wsdl11Definition;
}
@Bean
public XsdSchema applicantTypeSchema() {
return new SimpleXsdSchema(new ClassPathResource("xsd/ApplicantTypeService.xsd"));
}
@Bean
public XwsSecurityInterceptor securityInterceptor() {
XwsSecurityInterceptor securityInterceptor = new XwsSecurityInterceptor();
securityInterceptor.setCallbackHandler(new SpringPlainTextPasswordValidationCallbackHandler());
securityInterceptor.setPolicyConfiguration(new ClassPathResource("securityPolicy.xml"));
return securityInterceptor;
}
@Override
public void addInterceptors(List<EndpointInterceptor> interceptors) {
interceptors.add(securityInterceptor());
}
}
如果我在 XwsSecurityInterceptor 中使用 SimplePasswordValidationCallbackHandler,它会正确验证 ws usernametoken,所以我知道 ws-security 部分没有任何问题。如果我通过 http basic 登录,它会正确验证我的 ldap 用户,所以我知道这有效。
问题是,当我尝试在 ws security usernametoken 中使用我的 ldap 用户登录时,我在日志中得到 ERROR c.s.xml.wss.logging.impl.filter - WSS1408: UsernameToken Authentication Failed
,所以看起来它没有使用我在 WebSecurityConfigAdapter[=15 中定义的全局 ldap 身份验证=]
我似乎无法弄清楚如何让 XwsSecurityInterceptor 中的 SpringPlainTextPasswordValidationCallbackHandler(应该使用 spring 安全)来使用全局 authenticationManager,请帮忙??最后一天我真的一直在抨击这个,但似乎赢不了。
好的,我想通了,所以我会 post 为将来尝试此操作的任何人提供帮助。
我通过将 spring 引导 class 更改为:
解决了这个问题@SpringBootApplication
@EnableGlobalMethodSecurity(securedEnabled = true)
public class SwitchxApplication extends WebMvcConfigurerAdapter {
@SuppressWarnings("unused")
private static final Logger log = LoggerFactory.getLogger(SwitchxApplication.class);
@Bean
public ApplicationSecurity applicationSecurity() {
return new ApplicationSecurity();
}
@Configuration
@Order(Ordered.HIGHEST_PRECEDENCE)
protected static class AuthenticationConfiguration extends GlobalAuthenticationConfigurerAdapter {
@Bean
@ConfigurationProperties(prefix="ldap.contextSource")
public LdapContextSource contextSource() {
LdapContextSource contextSource = new LdapContextSource();
return contextSource;
}
@Override
public void init(AuthenticationManagerBuilder auth) throws Exception {
auth
.ldapAuthentication()
.userSearchBase("cn=Users,dc=Blah,dc=co,dc=za")
.userSearchFilter("(uid={0})")
.groupSearchBase("cn=Groups,dc=Blah,dc=co,dc=za")
.groupSearchFilter("(&(cn=*)(|(objectclass=groupofUniqueNames)(objectclass=orcldynamicgroup)))")
.contextSource(contextSource());
}
}
@Order(Ordered.LOWEST_PRECEDENCE - 8)
protected static class ApplicationSecurity extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/ws/**").permitAll()
.anyRequest().authenticated()
.and()
.csrf().disable()
.httpBasic();
}
}
public static void main(String[] args) {
SpringApplication.run(SwitchxApplication.class, args);
}
}
然后在我的 WsConfigurerAdapter 中进行以下相关更改:
@EnableWs
@Configuration
public class WebServiceConfig extends WsConfigurerAdapter {
private static final Logger log = LoggerFactory.getLogger(WebServiceConfig.class);
@Autowired
private AuthenticationManager authenticationManager;
@Bean
public ServletRegistrationBean messageDispatcherServlet(ApplicationContext applicationContext) {
MessageDispatcherServlet servlet = new MessageDispatcherServlet();
servlet.setApplicationContext(applicationContext);
servlet.setTransformWsdlLocations(true);
return new ServletRegistrationBean(servlet, "/ws/*");
}
.....
.....
@Bean
public SpringPlainTextPasswordValidationCallbackHandler callbackHandler() {
SpringPlainTextPasswordValidationCallbackHandler callbackHandler = new SpringPlainTextPasswordValidationCallbackHandler();
try {
callbackHandler.setAuthenticationManager(authenticationManager);
} catch(Exception e) {
log.error(e.getMessage());
}
return callbackHandler;
}
@Bean
public XwsSecurityInterceptor securityInterceptor() {
XwsSecurityInterceptor securityInterceptor = new XwsSecurityInterceptor();
securityInterceptor.setCallbackHandler(callbackHandler());
securityInterceptor.setPolicyConfiguration(new ClassPathResource("securityPolicy.xml"));
return securityInterceptor;
}
@Override
public void addInterceptors(List<EndpointInterceptor> interceptors) {
interceptors.add(securityInterceptor());
}
}
所以基本上最终结果是对于所有 /ws 路径,基本的 http 安全性被忽略,但是由于 WS Config 中的安全拦截器,它将使用基本的 ws-security 用户名令牌来验证 Web 服务调用,允许您使用 spring 安全设置和 ldap.
两种身份验证机制我希望这对某人有所帮助,但在引导和 java 配置文档中找不到关于此特定设置的大量文档有点棘手,因为它仍然相对较新。但是在没有得到这个工作之后,它非常棒,给我留下了深刻的印象。