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:
-
Ensuring Spring Security is more secure by default
-
Minimizing Information Leakage
-
Removing deprecated APIs
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.
4.1. Related Links
For thoroughness we have include the related links in the table below.
JIRA | Commits |
---|---|
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.RequestMatcher
→org.springframework.security.web.util.matcher.RequestMatcher
-
org.springframework.security.web.util.AntPathRequestMatcher
→org.springframework.security.web.util.matcher.AntPathRequestMatcher
-
org.springframework.security.web.util.AnyRequestMatcher
→org.springframework.security.web.util.matcher.AnyRequestMatcher.INSTANCE
-
org.springframework.security.web.util.ELRequestMatcher
→org.springframework.security.web.util.matcher.ELRequestMatcher
-
org.springframework.security.web.util.IpAddressMatcher
→org.springframework.security.web.util.matcher.IpAddressMatcher
-
org.springframework.security.web.util.RequestMatcherEditor
→org.springframework.security.web.util.matcher.RequestMatcherEditor
-
org.springframework.security.web.util.RegexRequestMatcher
→org.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.
6.1. Related Links
For thoroughness we have include the related links in the table below.
JIRA | Commits |
---|---|
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.
7.1. Related Links
For thoroughness we have include the related links in the table below.
JIRA | Commits |
---|---|
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
8.1. Related Links
For thoroughness we have include the related links in the table below.
JIRA | Commits |
---|---|
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();
}