I recently switched a majority of my Spring configuration to use the code based config in Spring 3.1. However, now that I\'ve switched, my Spring Security is not working correc
Thank you everyone for the above hints concerning a springSecurityFilterChain when Java configuration of Spring is used. As I've worked along to apply these hints to fix a comparable error in my own code, I noted that the Spring documentation for org.springframework.web.WebApplicationInitializer (the interface implemented by AbstractAnnotationConfigDispatcherServletInitializer) has the following relevant "Caveat":
web.xml versioning
WEB-INF/web.xml and WebApplicationInitializer use are not mutually exclusive; for example, web.xml can register one servlet, and a WebApplicationInitializer can register another. An initializer can even modify registrations performed in web.xml through methods such as ServletContext.getServletRegistration(String).
However, if WEB-INF/web.xml is present in the application, its version attribute must be set to "3.0" or greater, otherwise ServletContainerInitializer bootstrapping will be ignored by the servlet container.
I point this out because I noticed that your web.xml version noted above is still 2.5.
I got it working with Spring 3.2.3 and Servlet 3.0. The tick is to extend DelegatingFilterProxy class and named it SpringSecurityFilterChain.
public class ServiceInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
@Override
protected Class<?>[] getRootConfigClasses() {
return new Class<?>[] { ApplicationConfig.class };
}
@Override
protected Class<?>[] getServletConfigClasses() {
return new Class<?>[] { WebConfig.class };
}
@Override
protected String[] getServletMappings() {
return new String[] { "/" };
}
@Override
protected Filter[] getServletFilters() {
return new Filter[] { new OpenEntityManagerInViewFilter(), new SpringSecurityFilterChain() };
}
public class SpringSecurityFilterChain extends DelegatingFilterProxy {
}
}
For those still looking for a way to use SpringSecurity XML configuration with a Java config web application. I got this working using Spring 3.2.0.RELEASE and SpringSecurity 3.2.0.M1. Here's the important parts of the solution
WebAppConfig.java
package com.foo.webapp;
@Configuration
@ComponentScan(basePackages = { "com.foo" })
@ImportResource(value = { "/WEB-INF/spring-security.xml" })
public class WebAppConfig {
// other beans go here
}
spring-security.xml
Note that I had to remove the default xmlns="..." from the <beans:beans> definition.
<?xml version="1.0" encoding="UTF-8"?>
<beans:beans
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:beans="http://www.springframework.org/schema/beans"
xmlns:security="http://www.springframework.org/schema/security"
xsi:schemaLocation="
http://www.springframework.org/schema/security
http://www.springframework.org/schema/security/spring-security-3.1.xsd
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.1.xsd">
<security:http auto-config="true">
<security:intercept-url pattern="/secured/**" access="ROLE_USER" />
<security:form-login login-page="/login" default-target-url="/main"
authentication-failure-url="/loginfailed" />
<security:logout logout-success-url="/logout" />
</security:http>
<security:authentication-manager>
<security:authentication-provider>
<security:user-service>
<security:user name="user" password="user" authorities="ROLE_USER" />
</security:user-service>
</security:authentication-provider>
</security:authentication-manager>
</beans:beans>
web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.5" xmlns="http://java.sun.com/xml/ns/j2ee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="
http://java.sun.com/xml/ns/j2ee
http://java.sun.com/xml/ns/j2ee/web-app_2_5.xsd">
<context-param>
<param-name>contextClass</param-name>
<param-value>
org.springframework.web.context.support.AnnotationConfigWebApplicationContext
</param-value>
</context-param>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>com.foo.webapp.WebAppConfig</param-value>
</context-param>
<!-- Spring Security -->
<filter>
<filter-name>springSecurityFilterChain</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>
<filter-mapping>
<filter-name>springSecurityFilterChain</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<servlet>
<servlet-name>foo</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>foo</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
</web-app>
Do not use security
namespace shortcuts and migrate all spring configuration from XML to Java. It will make fine tuning of your security much easier. I'm going to do it for our project soon after migrating to 3.1.
You can find non-trivial almost plain-bean security XML config example here.
edit:
Finished config (linked above) migration. All config was put into one method deliberately to make it shorter and to demonstrate, that you don't need separate spring bean for every filter. Of course it's better to move complex init parts to separate methods (marked @Bean
if necessary). You can find working example in the X509AnnotationTest.Config
on the link above.
@Bean
public FilterChainProxy springSecurityFilterChain() throws Exception {
// AuthenticationEntryPoint
BasicAuthenticationEntryPoint entryPoint = new BasicAuthenticationEntryPoint();
entryPoint.setRealmName("AppName Realm");
// accessDecisionManager
List<AccessDecisionVoter> voters = Arrays.<AccessDecisionVoter>asList(new RoleVoter(), new WebExpressionVoter());
AccessDecisionManager accessDecisionManager = new AffirmativeBased(voters);
// SecurityExpressionHandler
SecurityExpressionHandler<FilterInvocation> securityExpressionHandler = new DefaultWebSecurityExpressionHandler();
// AuthenticationUserDetailsService
UserDetailsByNameServiceWrapper<PreAuthenticatedAuthenticationToken> authenticationUserDetailsService = new UserDetailsByNameServiceWrapper<PreAuthenticatedAuthenticationToken>(authUserDetailService);
authenticationUserDetailsService.afterPropertiesSet();
// PreAuthenticatedAuthenticationProvider
PreAuthenticatedAuthenticationProvider preAuthenticatedAuthenticationProvider = new PreAuthenticatedAuthenticationProvider();
preAuthenticatedAuthenticationProvider.setPreAuthenticatedUserDetailsService(authenticationUserDetailsService);
preAuthenticatedAuthenticationProvider.afterPropertiesSet();
// AuthenticationManager
List<AuthenticationProvider> providers = Arrays.<AuthenticationProvider>asList(preAuthenticatedAuthenticationProvider);
AuthenticationManager authenticationManager = new ProviderManager(providers);
// HttpSessionSecurityContextRepository
HttpSessionSecurityContextRepository httpSessionSecurityContextRepository = new HttpSessionSecurityContextRepository();
// SessionRegistry
SessionRegistry sessionRegistry = new SessionRegistryImpl();
// ConcurrentSessionControlStrategy
ConcurrentSessionControlStrategy concurrentSessionControlStrategy = new ConcurrentSessionControlStrategy(sessionRegistry);
// ConcurrentSessionFilter
ConcurrentSessionFilter concurrentSessionFilter = new ConcurrentSessionFilter(sessionRegistry);
concurrentSessionFilter.afterPropertiesSet();
// SecurityContextPersistenceFilter
SecurityContextPersistenceFilter securityContextPersistenceFilter = new SecurityContextPersistenceFilter(httpSessionSecurityContextRepository);
// X509AuthenticationFilter
X509AuthenticationFilter x509AuthenticationFilter = new X509AuthenticationFilter();
x509AuthenticationFilter.setAuthenticationManager(authenticationManager);
x509AuthenticationFilter.afterPropertiesSet();
// RequestCacheAwareFilter
RequestCacheAwareFilter requestCacheAwareFilter = new RequestCacheAwareFilter();
// SecurityContextHolderAwareRequestFilter
SecurityContextHolderAwareRequestFilter securityContextHolderAwareRequestFilter = new SecurityContextHolderAwareRequestFilter();
// SessionManagementFilter
SessionManagementFilter sessionManagementFilter = new SessionManagementFilter(httpSessionSecurityContextRepository, concurrentSessionControlStrategy);
// ExceptionTranslationFilter
ExceptionTranslationFilter exceptionTranslationFilter = new ExceptionTranslationFilter(entryPoint);
exceptionTranslationFilter.setAccessDeniedHandler(new AccessDeniedHandlerImpl());
exceptionTranslationFilter.afterPropertiesSet();
// FilterSecurityInterceptor
FilterSecurityInterceptor filterSecurityInterceptor = new FilterSecurityInterceptor();
filterSecurityInterceptor.setAuthenticationManager(authenticationManager);
filterSecurityInterceptor.setAccessDecisionManager(accessDecisionManager);
LinkedHashMap<RequestMatcher, Collection<ConfigAttribute>> map = new LinkedHashMap<RequestMatcher, Collection<ConfigAttribute>>();
map.put(new AntPathRequestMatcher("/**"), Arrays.<ConfigAttribute>asList(new SecurityConfig("isAuthenticated()")));
ExpressionBasedFilterInvocationSecurityMetadataSource ms = new ExpressionBasedFilterInvocationSecurityMetadataSource(map, securityExpressionHandler);
filterSecurityInterceptor.setSecurityMetadataSource(ms);
filterSecurityInterceptor.afterPropertiesSet();
// SecurityFilterChain
SecurityFilterChain chain = new DefaultSecurityFilterChain(new AntPathRequestMatcher("/**"),
concurrentSessionFilter,
securityContextPersistenceFilter,
x509AuthenticationFilter,
requestCacheAwareFilter,
securityContextHolderAwareRequestFilter,
sessionManagementFilter,
exceptionTranslationFilter,
filterSecurityInterceptor);
return new FilterChainProxy(chain);
}