This guide is intended to help users migrate from Spring Security 3.x to Spring Security 4.x when using Java based configuration. If you are looking to migrate from Spring Security 3.x to Spring Security 4.x when using XML Based configuration, click here

1. Introduction

As exploits against applications evolve, so must Spring Security. As a major release version, the Spring Security team took the opportunity to make some non-passive changes which focus on:

A complete listing of non-passive changes between 3.x and 4.x can be found in JIRA

2. Sample Migration

A sample illustrating all of the changes in a migration can be found on github. The sample demonstrates migrating spring-security-3-jc to Spring Security 4. The completed migration can be found in spring-security-4-jc

You can find a diff of the changes on github.

3. Updating to Spring 4.1.x

Spring Security 4 now requires Spring 4. Conveniently, Spring Security 3.2.x works with Spring 3.2.x and Spring 4. This means your first step is to update to Spring 4.1.x.

For detailed instructions refer to:

4. Deprecations

A number of deprecations were removed in Spring Security 4 to clean up clutter. The following section describes how to migrate each deprecation.

If you are using the Java configuration, there are many instances where you will be shielded from deprecation. If you (or a non-spring library you use) do NOT use an API directly, then you will NOT be impacted. This means typically a quick search in your workspace should allow you to find all the deprecations.

For thoroughness we have include the related links in the table below.

JIRA Commits

SEC-2781

6e204ff

4.2. spring-security-acl

This section describes all of the deprecated APIs within the spring-security-acl module. If you are not using the spring-security-acl module, you can safely skip to spring-security-cas.

4.2.1. AclImpl

AclImpl had a deprecated constructor removed. Specifically, the constructor that defaults the PermissionGrantingStrategy was removed:

@Deprecated
public AclImpl(ObjectIdentity objectIdentity, Serializable id,
			AclAuthorizationStrategy aclAuthorizationStrategy,
			AuditLogger auditLogger, Acl parentAcl,
			List<Sid> loadedSids, boolean entriesInheriting, Sid owner) {
		...
}

This means that an AclImpl was being created with this constructor:

new AclImpl(objectIdentity, id, aclAuthorizationStrategy, auditLogger,
								parentAcl, loadedSids, entriesInheriting, owner);

it needs to be updated to pass in the PermissionGrantingStrategy instead of the AuditLogger

PermissionGrantingStrategy permissionGrantingStrategy =
	new DefaultPermissionGrantingStrategy(auditLogger);
new AclImpl(objectIdentity, id, aclAuthorizationStrategy, permissionGrantingStrategy,
						parentAcl, loadedSids, entriesInheriting, owner);

4.2.2. EhCacheBasedAclCache

EhCacheBasedAclCache had a deprecated constructor removed. Specifically, the constructor that defaults the PermissionGrantingStrategy was removed:

@Deprecated
public EhCacheBasedAclCache(Ehcache cache) {
	...
}

This means that an EhCacheBasedAclCache was being created with this constructor:

new EhCacheBasedAclCache(ehCache);

it needs to be updated to pass in the PermissionGrantingStrategy and AclAuthorizationStrategy too:

PermissionGrantingStrategy permissionGrantingStrategy =
				new DefaultPermissionGrantingStrategy(auditLogger);
AclAuthorizationStrategy aclAuthorizationStrategy =
		new AclAuthorizationStrategyImpl(new SimpleGrantedAuthority("ROLE_ACL_ADMIN"));
new EhCacheBasedAclCache(ehCache, permissionGrantingStrategy, aclAuthorizationStrategy);

4.3. spring-security-cas

This section describes all of the deprecated APIs within the spring-security-cas module. If you are not using the spring-security-cas module, you can safely skip to spring-security-core.

4.3.1. ServiceAuthenticationDetailsSource

ServiceAuthenticationDetailsSource removed the deprecated construtors that defaulted the ServiceProperties.

@Deprecated
public ServiceAuthenticationDetailsSource() {
	...
}

@Deprecated
public ServiceAuthenticationDetailsSource(final String artifactParameterName) {
	...
}

This means that an ServiceAuthenticationDetailsSource was being created with these constructors:

new ServiceAuthenticationDetailsSource();

new ServiceAuthenticationDetailsSource(artifactId);

it needs to be updated to pass in the ServiceProperties as shown below:

new ServiceAuthenticationDetailsSource(serviceProperties);

new ServiceAuthenticationDetailsSource(serviceProperties, artifactId);

4.4. spring-security-core

This section describes all of the deprecated APIs within the spring-security-core module. If you are not using the spring-security-core module or have already completed this task, you can safely skip to spring-security-openid.

4.4.1. SecurityConfig

SecurityConfig.createSingleAttributeList(String) was removed in favor of using SecurityConfig.createList(String…​). This means if you have something like this:

List<ConfigAttribute> attrs =
		SecurityConfig.createSingleAttributeList("ROLE_USER");

needs to be replaced with:

List<ConfigAttribute> attrs =
		SecurityConfig.createList("ROLE_USER");

4.4.2. UserDetailsServiceWrapper

UserDetailsServiceWrapper was deprecated in favor of using RoleHierarchyAuthoritiesMapper. For example, if you have something like this:

@Bean
public AuthenticationManager authenticationManager(List<AuthenticationProvider> providers) {
	return new ProviderManager(providers);
}

@Bean
public AuthenticationProvider authenticationProvider(UserDetailsServiceWrapper userDetailsService) {
	DaoAuthenticationProvider provider = new DaoAuthenticationProvider();
	provider.setUserDetailsService(userDetailsService);
	return provider;
}

@Bean
public UserDetailsServiceWrapper userDetailsServiceWrapper(RoleHierarchy roleHierarchy) {
	UserDetailsServiceWrapper wrapper = new UserDetailsServiceWrapper();
	wrapper.setRoleHierarchy(roleHierarchy);
	wrapper.setUserDetailsService(userDetailsService());
	return wrapper;
}

then it needs to be migrated with something like this:

@Bean
public AuthenticationManager authenticationManager(List<AuthenticationProvider> providers) {
	return new ProviderManager(providers);
}

@Bean
public AuthenticationProvider authenticationProvider(UserDetailsService userDetailsService, GrantedAuthoritiesMapper authoritiesMapper) {
	DaoAuthenticationProvider provider = new DaoAuthenticationProvider();
	provider.setUserDetailsService(userDetailsService);
	provider.setAuthoritiesMapper(authoritiesMapper);
	return provider;
}

@Bean
public RoleHierarchyAuthoritiesMapper roleHierarchyAuthoritiesMapper(RoleHierarchy roleHierarchy) {
	return new RoleHierarchyAuthoritiesMapper(roleHierarchy);
}

4.4.3. UserDetailsWrapper

UserDetailsWrapper was deprecated in favor of using RoleHierarchyAuthoritiesMapper. Typically users would not use the UserDetailsWrapper directly. However, if they are they can use RoleHierarchyAuthoritiesMapper For example, if the following code is present:

UserDetailsWrapper authenticate = new UserDetailsWrapper(userDetails, roleHiearchy);

then it needs to be replaced by:

Collection<GrantedAuthority> allAuthorities =
		roleHiearchy.getReachableGrantedAuthorities(userDetails.getAuthorities());
UserDetails authenticate =
		new User(userDetails.getUsername(), userDetails.getPassword(), allAuthorities);

4.4.4. AbstractAccessDecisionManager

The default constructor for AbstractAccessDecisionManager has been deprecated along with the setDecisionVoters method. Naturally, this impacts the subclasses AffirmativeBased, ConsensusBased, and UnanimousBased. For example, this means that if you are using the following:

AffirmativeBased adm = new AffirmativeBased();
adm.setDecisionVoters(voters);

it needs to be migrated to:

AffirmativeBased adm = new AffirmativeBased(voters);

4.4.5. AuthenticationException

The constructor that accepts extraInformation within AuthenticationException was removed to prevent accidental leaking of the UserDetails. Specifically, the following we removed.

public AccountExpiredException(String msg, Object extraInformation) {
...
}

This impacts the subclasses AccountStatusException, AccountExpiredException, BadCredentialsException, CredentialsExpiredException, DisabledException, LockedException, and UsernameNotFoundException. If use are using any of these constructors, simply remove the additional argument. For example, the following is changed from:

new LockedException("Message", userDetails);

to:

new LockedException("Message");

4.4.6. AnonymousAuthenticationProvider

AnonymousAuthenticationProvider default constructor and setKey method was deprecated in favor of using constructor injection. For example, if you have the following:

AnonymousAuthenticationProvider provider = new AnonymousAuthenticationProvider();
provider.setKey(key);

it should be changed to:

AnonymousAuthenticationProvider provider = new AnonymousAuthenticationProvider(key);

4.4.7. AuthenticationDetailsSourceImpl

AuthenticationDetailsSourceImpl was deprecated in favor of writing a custom AuthenticationDetailsSource. For example, if you have the following:

AuthenticationDetailsSourceImpl source = new AuthenticationDetailsSourceImpl();
source.setClazz(CustomWebAuthenticationDetails.class);

You should implement AuthenticationDetailsSource directly to return CustomSource:

public class CustomWebAuthenticationDetailsSource implements AuthenticationDetailsSource<HttpServletRequest, WebAuthenticationDetails> {

	public WebAuthenticationDetails buildDetails(HttpServletRequest context) {
		return new CustomWebAuthenticationDetails(context);
	}
}

4.4.8. ProviderManager

ProviderManager has removed the deprecated default constructor and the correspdonding setter methods in favor of using constructor injection. It has also removed the clearExtraInformation property since the AuthenticationException had the extra information property removed.

For example, if you have something like the following:

ProviderManager provider = new ProviderManager();
provider.setParent(parent);
provider.setProviders(providers);
provider.setClearExtraInformation(true);

then it should be changed to:

ProviderManager provider = new ProviderManager(providers, parent);
The clearExtraInformation property was removed since the AuthenticationException had the extra information property removed. So there is no replacement for this.

4.4.9. RememberMeAuthenticationProvider

RememberMeAuthenticationProvider had the default constructor and the setKey method removed in favor of constructor injection. For example:

RememberMeAuthenticationProvider provider = new RememberMeAuthenticationProvider();
provider.setKey(key);

should be migrated to:

RememberMeAuthenticationProvider provider = new RememberMeAuthenticationProvider(key);

4.4.10. GrantedAuthorityImpl

GrantedAuthorityImpl was removed in favor of SimpleGrantedAuthority or implementing your own. For example:

new GrantedAuthorityImpl(role);

should be replaced with

new SimpleGrantedAuthority(role);

4.4.11. InMemoryDaoImpl

InMemoryDaoImpl was replaced in favor of InMemoryUserDetailsManager

For example the following:

InMemoryDaoImpl uds = new InMemoryDaoImpl();
uds.setUserProperties(properties);

should be replaced with

InMemoryUserDetailsManager uds = new InMemoryUserDetailsManager(properties);

4.4.12. spring-security-openid

This section describes all of the deprecated APIs within the spring-security-openid module. If you are not using the spring-security-openid module or have already completed this task, you can safely skip to spring-security-taglibs.

4.4.13. OpenID4JavaConsumer

The OpenID4JavaConsumer constructors that accept List<OpenIDAttribute> have been removed in favor of using an AxFetchListFactory. For example:

new OpenID4JavaConsumer(attributes);

should be replaced with:

Map<String, List<OpenIDAttribute>> regexMap = new HashMap<String,List<OpenIDAttribute>>();
regexMap.put(".*", attributes);
RegexBasedAxFetchListFactory factory = new RegexBasedAxFetchListFactory(regexMap);
new OpenID4JavaConsumer(factory);

4.5. spring-security-taglibs

This section describes all of the deprecated APIs within the spring-security-taglibs module. If you are not using the spring-security-taglibs module or have already completed this task, you can safely skip to spring-security-web.

Spring Security’s authorize JSP tag deprecated the properties ifAllGranted, ifAnyGranted, and ifNotGranted in favor of using expressions.

For example:

<sec:authorize ifAllGranted="ROLE_ADMIN,ROLE_USER">
	<p>Must have ROLE_ADMIN and ROLE_USER</p>
</sec:authorize>
<sec:authorize ifAnyGranted="ROLE_ADMIN,ROLE_USER">
	<p>Must have ROLE_ADMIN or ROLE_USER</p>
</sec:authorize>
<sec:authorize ifNotGranted="ROLE_ADMIN,ROLE_USER">
	<p>Must not have ROLE_ADMIN or ROLE_USER</p>
</sec:authorize>

can be replaced with:

<sec:authorize access="hasRole('ROLE_ADMIN') and hasRole('ROLE_USER')">
	<p>Must have ROLE_ADMIN and ROLE_USER</p>
</sec:authorize>
<sec:authorize access="hasAnyRole('ROLE_ADMIN','ROLE_USER')">
	<p>Must have ROLE_ADMIN or ROLE_USER</p>
</sec:authorize>
<sec:authorize access="!hasAnyRole('ROLE_ADMIN','ROLE_USER')">
	<p>Must not have ROLE_ADMIN or ROLE_USER</p>
</sec:authorize>

4.6. spring-security-web

This section describes all of the deprecated APIs within the spring-security-web module. If you are not using the spring-security-web module or have already completed this task, you can safely skip to [m3to4-xmlnamespace-defaults].

4.6.1. FilterChainProxy

FilterChainProxy removed the setFilterChainMap method in favor of constructor injection. For example, if you have the following:

FilterChainProxy filter = new FilterChainProxy();
filter.setFilterChainMap(filterChainMap);

it should be replaced with:

FilterChainProxy filter = new FilterChainProxy(securityFilterChains);

FilterChainProxy also removed getFilterChainMap in favor of using getFilterChains for example:

FilterChainProxy securityFilterChain = ...
Map<RequestMatcher,List<Filter>> mappings = securityFilterChain.getFilterChainMap();
for(Map.Entry<RequestMatcher, List<Filter>> entry : mappings.entrySet()) {
	RequestMatcher matcher = entry.getKey();
	boolean matches = matcher.matches(request);
	List<Filter> filters = entry.getValue();
}

should be replaced with

FilterChainProxy securityFilterChain = ...
List<SecurityFilterChain> mappings = securityFilterChain.getFilterChains();
for(SecurityFilterChain entry : mappings) {
	boolean matches = entry.matches(request);
	List<Filter> filters = entry.getFilters();
}

4.6.2. ExceptionTranslationFilter

The default constructor for ExceptionTranslationFilter and the setAuthenticationEntryPoint method was removed in favor of using constructor injection.

ExceptionTranslationFilter filter = new ExceptionTranslationFilter();
filter.setAuthenticationEntryPoint(entryPoint);
filter.setRequestCache(requestCache);

can be replaced with

ExceptionTranslationFilter filter = new ExceptionTranslationFilter(entryPoint, requestCache);

4.6.3. AbstractAuthenticationProcessingFilter

AbstractAuthenticationProcessingFilter had its successfulAuthentication(HttpServletRequest,HttpServletResponse,Authentication) method removed. So if your application overrides the following method:

protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response,
						Authentication authResult) throws IOException, ServletException {
}

it should be replaced with:

protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response,
						FilterChain chain, Authentication authResult) throws IOException, ServletException {
}

4.6.4. AnonymousAuthenticationFilter

AnonymousAuthenticationFilter had the default constructor and the setKey and setPrincipal methods removed in favor of constructor injection. For example:

AnonymousAuthenticationFilter filter = new AnonymousAuthenticationFilter();
filter.setKey(key);
filter.setUserAttribute(attrs);

should be replaced with:

AnonymousAuthenticationFilter filter =
		new AnonymousAuthenticationFilter(key,attrs.getPassword(),attrs.getAuthorities());

4.6.5. LoginUrlAuthenticationEntryPoint

The LoginUrlAuthenticationEntryPoint default constructor and the setLoginFormUrl method was removed in favor of constructor injection. For example:

LoginUrlAuthenticationEntryPoint entryPoint = new LoginUrlAuthenticationEntryPoint();
entryPoint.setLoginFormUrl("/login");

should be replaced with

LoginUrlAuthenticationEntryPoint entryPoint = new LoginUrlAuthenticationEntryPoint(loginFormUrl);

4.6.6. PreAuthenticatedGrantedAuthoritiesUserDetailsService

PreAuthenticatedGrantedAuthoritiesUserDetailsService removed createuserDetails in favor of createUserDetails.

The new method has a correction in the case (i.e. U instead of u).

This means if you have a subclass of PreAuthenticatedGrantedAuthoritiesUserDetailsService that overrides createuserDetails

public class SubclassPreAuthenticatedGrantedAuthoritiesUserDetailsService extends PreAuthenticatedGrantedAuthoritiesUserDetailsService {

	@Override
	protected UserDetails createuserDetails(Authentication token,
			Collection<? extends GrantedAuthority> authorities) {
		// customize
	}
}

it should be changed to override createUserDetails

public class SubclassPreAuthenticatedGrantedAuthoritiesUserDetailsService extends PreAuthenticatedGrantedAuthoritiesUserDetailsService {

	@Override
	protected UserDetails createUserDetails(Authentication token,
			Collection<? extends GrantedAuthority> authorities) {
		// customize
	}
}

4.6.7. AbstractRememberMeServices

AbstractRememberMeServices and its subclasses PersistentTokenBasedRememberMeServices and TokenBasedRememberMeServices removed the default constructor and the setKey and setUserDetailsService methods in favor of constructor injection.

4.6.8. PersistentTokenBasedRememberMeServices

AbstractRememberMeServices and its subclasses PersistentTokenBasedRememberMeServices and TokenBasedRememberMeServices removed the default constructor and the setKey and setUserDetailsService methods in favor of constructor injection. For example:

PersistentTokenBasedRememberMeServices services = new PersistentTokenBasedRememberMeServices();
services.setKey(key);
services.setUserDetailsService(userDetailsService);
services.setTokenRepository(tokenRepository);

should be replaced with

PersistentTokenBasedRememberMeServices services =
		new PersistentTokenBasedRememberMeServices(key, userDetailsService, tokenRepository);

4.6.9. RememberMeAuthenticationFilter

RememberMeAuthenticationFilter default constructor and the setAuthenticationManager and setRememberMeServices methods were removed in favor of constructor injection.

RememberMeAuthenticationFilter filter = new RememberMeAuthenticationFilter();
filter.setAuthenticationManager(authenticationManager);
filter.setRememberMeServices(rememberMeServices);

should be replaced with

RememberMeAuthenticationFilter filter =
		new RememberMeAuthenticationFilter(authenticationManager,rememberMeServices);

4.6.10. TokenBasedRememberMeServices

AbstractRememberMeServices and its subclasses PersistentTokenBasedRememberMeServices and TokenBasedRememberMeServices removed the default constructor and the setKey and setUserDetailsService methods in favor of constructor injection. For example:

TokenBasedRememberMeServices services = new TokenBasedRememberMeServices();
services.setKey(key);
services.setUserDetailsService(userDetailsService);

should be replaced with

TokenBasedRememberMeServices services =
		new TokenBasedRememberMeServices(key, userDetailsService);

4.6.11. ConcurrentSessionControlStrategy

ConcurrentSessionControlStrategy was replaced with ConcurrentSessionControlAuthenticationStrategy. Previously ConcurrentSessionControlStrategy could not be decoupled from SessionFixationProtectionStrategy. Now it is completely decoupled. For example, the following:

ConcurrentSessionControlStrategy strategy = new ConcurrentSessionControlStrategy(sessionRegistry);

can be replaced with

List<SessionAuthenticationStrategy> delegates = new ArrayList<SessionAuthenticationStrategy>();
delegates.add(new ConcurrentSessionControlAuthenticationStrategy(sessionRegistry));
delegates.add(new SessionFixationProtectionStrategy());
delegates.add(new RegisterSessionAuthenticationStrategy(sessionRegistry));
CompositeSessionAuthenticationStrategy strategy = new CompositeSessionAuthenticationStrategy(delegates);

4.6.12. SessionFixationProtectionStrategy

SessionFixationProtectionStrategy removed setRetainedAttributes method in favor of users subclassing SessionFixationProtectionStrategy and overriding extractAttributes method. This means the following:

SessionFixationProtectionStrategy strategy = new SessionFixationProtectionStrategy();
strategy.setRetainedAttributes(attrsToRetain);

should be replaced with

public class AttrsSessionFixationProtectionStrategy extends SessionFixationProtectionStrategy {
	private final Collection<String> attrsToRetain;

	public AttrsSessionFixationProtectionStrategy(
			Collection<String> attrsToRetain) {
		this.attrsToRetain = attrsToRetain;
	}

	@Override
	protected Map<String, Object> extractAttributes(HttpSession session) {
		Map<String,Object> attrs = new HashMap<String, Object>();
		for(String attr : attrsToRetain) {
			attrs.put(attr, session.getAttribute(attr));
		}
		return attrs;
	}

}

SessionFixationProtectionStrategy strategy = new AttrsSessionFixationProtectionStrategy(attrsToRetain);

4.6.13. BasicAuthenticationFilter

BasicAuthenticationFilter default constructor and the setAuthenticationManager and setRememberMeServices methods were removed in favor of constructor injection.

BasicAuthenticationFilter filter = new BasicAuthenticationFilter();
filter.setAuthenticationManager(authenticationManager);
filter.setAuthenticationEntryPoint(entryPoint);
filter.setIgnoreFailure(true);

should be replaced with

BasicAuthenticationFilter filter =
		new BasicAuthenticationFilter(authenticationManager,entryPoint);
Using this constructor automatically sets ignoreFalure to true

4.6.14. SecurityContextPersistenceFilter

SecurityContextPersistenceFilter removed the setSecurityContextRepository in favor of constructor injection. For example:

SecurityContextPersistenceFilter filter = new SecurityContextPersistenceFilter();
filter.setSecurityContextRepository(securityContextRepository);

should be replaced with

SecurityContextPersistenceFilter filter = new SecurityContextPersistenceFilter(securityContextRepository);

4.6.15. RequestCacheAwareFilter

RequestCacheAwareFilter removed the setRequestCache in favor of constructor injection. For example:

RequestCacheAwareFilter filter = new RequestCacheAwareFilter();
filter.setRequestCache(requestCache);

should be replaced with

RequestCacheAwareFilter filter = new RequestCacheAwareFilter(requestCache);

4.6.16. ConcurrentSessionFilter

ConcurrentSessionFilter removed the default constructor and the setExpiredUrl and setSessionRegistry methods in favor of constructor injection. For example:

ConcurrentSessionFilter filter = new ConcurrentSessionFilter();
filter.setSessionRegistry(sessionRegistry);
filter.setExpiredUrl("/expired");

should be replaced with

ConcurrentSessionFilter filter = new ConcurrentSessionFilter(sessionRegistry,"/expired");

4.6.17. SessionManagementFilter

SessionManagementFilter removed the setSessionAuthenticationStrategy method in favor of constructor injection. For example:

SessionManagementFilter filter = new SessionManagementFilter(securityContextRepository);
filter.setSessionAuthenticationStrategy(sessionAuthenticationStrategy);

should be replaced with

SessionManagementFilter filter = new SessionManagementFilter(securityContextRepository, sessionAuthenticationStrategy);

4.6.18. RequestMatcher

The RequestMatcher and its implementations have moved from the package org.springframework.security.web.util to org.springframework.security.web.util.matcher. Specifically

  • org.springframework.security.web.util.RequestMatcherorg.springframework.security.web.util.matcher.RequestMatcher

  • org.springframework.security.web.util.AntPathRequestMatcherorg.springframework.security.web.util.matcher.AntPathRequestMatcher

  • org.springframework.security.web.util.AnyRequestMatcherorg.springframework.security.web.util.matcher.AnyRequestMatcher.INSTANCE

  • org.springframework.security.web.util.ELRequestMatcherorg.springframework.security.web.util.matcher.ELRequestMatcher

  • org.springframework.security.web.util.IpAddressMatcherorg.springframework.security.web.util.matcher.IpAddressMatcher

  • org.springframework.security.web.util.RequestMatcherEditororg.springframework.security.web.util.matcher.RequestMatcherEditor

  • org.springframework.security.web.util.RegexRequestMatcherorg.springframework.security.web.util.matcher.RegexRequestMatcher

4.6.19. WebSecurityExpressionHandler

WebSecurityExpressionHandler was removed in favor of using SecurityExpressionHandler<FilterInvocation>.

This means if you are using:

WebSecurityExpressionHandler handler = ...

it needs to be updated to

SecurityExpressionHandler<FilterInvocation> handler = ...

If you implement WebSecurityExpressionHandler:

public class CustomWebSecurityExpressionHandler implements WebSecurityExpressionHandler {
	...
}

then it must be updated to:

public class CustomWebSecurityExpressionHandler implements SecurityExpressionHandler<FilterInvocation> {
	...
}

4.6.20. @AuthenticationPrincipal

org.springframework.security.web.bind.annotation.AuthenticationPrincipal has been deprecated in favor of org.springframework.security.core.annotation.AuthenticationPrincipal. For example:

import org.springframework.security.web.bind.annotation.AuthenticationPrincipal;

// ...

@RequestMapping("/messages/inbox")
public ModelAndView findMessagesForUser(@AuthenticationPrincipal CustomUser customUser) {

	// .. find messags for this user and return them ...
}

should be replaced with

import org.springframework.security.core.annotation.AuthenticationPrincipal;

// ...

@RequestMapping("/messages/inbox")
public ModelAndView findMessagesForUser(@AuthenticationPrincipal CustomUser customUser) {

	// .. find messags for this user and return them ...
}

5. Update Spring Security

Now you can update to Spring Security 4.x. If you are using Maven and Spring Security’s BOM, you can do something like this:

<dependencyManagement>
	<dependencies>
		<dependency>
			<groupId>org.springframework.security</groupId>
			<artifactId>spring-security-bom</artifactId>
			<version>4.0.0.RELEASE</version>
			<type>pom</type>
			<scope>import</scope>
		</dependency>
	</dependencies>
</dependencyManagement>

Now all of the Spring Security dependencies that do not specify a version will use the updated Spring Security version.

Alternatively, you can update each of the Spring Security dependencies within your pom. For example, the following would update spring-security-core to use version 4.0.0.RELEASE

<dependency>
	<groupId>org.springframework.security</groupId>
	<artifactId>spring-security-core</artifactId>
	<version>4.0.0.RELEASE</version>
</dependency>

6. Migrate Default Filter URLs

A number of servlet Filter’s had their default URLs switched to help guard against information leakage.

For thoroughness we have include the related links in the table below.

JIRA Commits

SEC-2783

c67ff42

6.2. CasAuthenticationFilter

The CasAuthenticationFilter filterProcessesUrl property default value changed from "/j_spring_cas_security_check" to "/login/cas". This means if the filterProcessesUrl property is not explicitly specified, then the configuration will need updated. For example, if an application using Spring Security 3.2.x contains a configuration similar to the following:

CasAuthenticationFilter filter = new CasAuthenticationFilter();
filter.setAuthenticationManager(authenticationManager);

The configuration will need to be updated to something similar to the following when Spring Security 4.x:

CasAuthenticationFilter filter = new CasAuthenticationFilter();
filter.setFilterProcessesUrl("/j_spring_cas_security_check");
filter.setAuthenticationManager(authenticationManager);

Alternatively, the ServiceProperties can be updated to use the new default:

ServiceProperties properties = new ServiceProperties();
properties.setService("https://example.com/cas-sample/login/cas");

6.3. SwitchUserFilter

  • The SwitchUserFilter switchUserUrl property default value changed from "/j_spring_security_switch_user" to "/login/impersonate". This means if the switchUserUrl property is not explicitly specified, then the configuration will need updated.

  • The SwitchUserFilter exitUserUrl property default value changed from "/j_spring_security_exit_user" to "/logout/impersonate". This means if the exitUserUrl property is not explicitly specified, then the configuration will need updated.

For example, if an application using Spring Security 3.2.x contains a configuration similar to the following:

@Bean
public SwitchUserFilter switchUserFilter(
		UserDetailsService userDetailsService) {
	SwitchUserFilter filter = new SwitchUserFilter();
	filter.setTargetUrl("/");
	filter.setUserDetailsService(userDetailsService);
	return filter;
}

The configuration will need to be updated to something similar to the following when Spring Security 4.x:

@Bean
public SwitchUserFilter switchUserFilter(
		UserDetailsService userDetailsService) {
	SwitchUserFilter filter = new SwitchUserFilter();
	filter.setExitUserUrl("/j_spring_security_exit_user");
	filter.setSwitchUserUrl("/j_spring_security_switch_user");
	filter.setTargetUrl("/");
	filter.setUserDetailsService(userDetailsService);
	return filter;
}

Alternatively, the URL’s within the application can be updated from:

  • "/j_spring_security_switch_user" to "/login/impersonate"

  • "/j_spring_security_exit_user" to "/logout/impersonate"

6.4. LogoutFilter

The LogoutFilter filterProcessesUrl property default value changed from "/j_spring_security_logout" to "/logout". This means if the filterProcessesUrl property is not explicitly specified, then the configuration will need updated.

For example, if an application using Spring Security 3.2.x contains a configuration similar to the following:

@Bean
public LogoutFilter logoutFilter() {
	LogoutFilter filter = new LogoutFilter("/logout/success", new SecurityContextLogoutHandler());
	return filter;
}

The configuration will need to be updated to something similar to the following when Spring Security 4.x:

@Bean
public LogoutFilter logoutFilter() {
	LogoutFilter filter = new LogoutFilter("/logout/success", new SecurityContextLogoutHandler());
	filter.setFilterProcessesUrl("/j_spring_security_logout");
	return filter;
}

Alternatively, the URL’s within the application can be updated from "/j_spring_security_logout" to "/logout".

7. Header Configuration Changes

In Spring Security 3.x the HTTP Response Header configuration was difficult to customize. If an application overrode a single default, then all of the other defaults would be disabled. This was unintuitive, error prone, and most importantly not very secure.

Spring Security 4.x has changed both the Java Configuration and XML Configuration to require explicit disabling of defaults. Additionally, it has made customizing a single default much easier.

If an application has customized the HTTP Response Header Configuration in any way, they are impacted by this change. If the application used the defaults, then they are not impacted by this change.

A detailed description of how to configure Security HTTP Response Headers can be found in the reference. Below we highlight the changes in configuring the Security HTTP Response Headers between 3.x and 4.x.

For thoroughness we have include the related links in the table below.

JIRA Commits

SEC-2348

eedbf44

7.2. Header Samples

7.2.1. Migrate Headers Java Config Defaults Preserved

In Spring Security 3.x, the following configuration

http
	// ...
	.headers()
		.addHeaderWriter(new XFrameOptionsHeaderWriter(XFrameOptionsMode.SAMEORIGIN));

would add the following header:

X-Frame-Options: SAMEORIGIN

In Spring Security 4.x, the same configuration would add

Cache-Control: no-cache, no-store, max-age=0, must-revalidate
Pragma: no-cache
Expires: 0
X-Content-Type-Options: nosniff
Strict-Transport-Security: max-age=31536000 ; includeSubDomains
X-Frame-Options: SAMEORIGIN
X-XSS-Protection: 1; mode=block

If we want to the configuration the same, we must explicitly disable the other defaults.

http
	// ...
	.headers()
		// do not use any default headers unless explicitly listed
		.defaultsDisabled()
		.frameOptions()
				.sameOrigin();

would add the following header:

X-Frame-Options: SAMEORIGIN

7.2.2. Migrate Headers Java Config Method Chaining

In Spring Security 3.x, the following configuration

http
	// ...
	.headers()
		.cacheControl()
		.frameOptions();

would compile succesfully. However, Spring Security 4.x it will not compile. This is due to the fact that additional options needed to be added to support customizing the configuration. Instead, we must chain the headers customizations with .and(). For example:

http
	// ...
	.headers()
		// do not use any default headers unless explicitly listed
		.defaultsDisabled()
		.cacheControl().and()
		.frameOptions();

8. Automatic ROLE_ prefixing

Spring Security 4 automatically prefixes any role with ROLE_. The changes were made as part of SEC-2758

For thoroughness we have include the related links in the table below.

JIRA Commits

SEC-2758

6627f76

SEC-2926

09acc2b

8.2. ROLE_ Prefixing Passivity

Passivity is impacted if the application’s users' roles are not prefixed with ROLE_. If all of the application’s users' roles are prefixed with ROLE_ then it is NOT impacted.

8.3. Disable ROLE_ Prefixing

One can disable automatic ROLE_ prefixing using a BeanPostProcessor similar to the following:

package sample.role_;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.core.PriorityOrdered;
import org.springframework.security.access.annotation.Jsr250MethodSecurityMetadataSource;
import org.springframework.security.access.expression.method.DefaultMethodSecurityExpressionHandler;
import org.springframework.security.web.access.expression.DefaultWebSecurityExpressionHandler;
import org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter;

public class DefaultRolesPrefixPostProcessor implements BeanPostProcessor, PriorityOrdered {

	@Override
	public Object postProcessAfterInitialization(Object bean, String beanName)
			throws BeansException {

		// remove this if you are not using JSR-250
		if(bean instanceof Jsr250MethodSecurityMetadataSource) {
			((Jsr250MethodSecurityMetadataSource) bean).setDefaultRolePrefix(null);
		}

		if(bean instanceof DefaultMethodSecurityExpressionHandler) {
			((DefaultMethodSecurityExpressionHandler) bean).setDefaultRolePrefix(null);
		}
		if(bean instanceof DefaultWebSecurityExpressionHandler) {
			((DefaultWebSecurityExpressionHandler) bean).setDefaultRolePrefix(null);
		}
		if(bean instanceof SecurityContextHolderAwareRequestFilter) {
			((SecurityContextHolderAwareRequestFilter)bean).setRolePrefix("");
		}
		return bean;
	}

	@Override
	public Object postProcessBeforeInitialization(Object bean, String beanName)
			throws BeansException {
		return bean;
	}

	@Override
	public int getOrder() {
		return PriorityOrdered.HIGHEST_PRECEDENCE;
	}
}

and then defining it as a Bean:

@Bean
public static DefaultRolesPrefixPostProcessor defaultRolesPrefixPostProcessor() {
	return new DefaultRolesPrefixPostProcessor();
}