Here is a simple gotcha to keep in mind while migrating to newer Spring Security version 4.x from Spring Security 3.2.x
What’s the problem?
The Spring security configuration expressions
hasAuthority('role_name') are no longer the same.
The catch is:
hasAuthority checks for any role name passed to the expression without prepending ‘ROLE_’ (which is the default role prefix), where as
hasRole checks for any role name passed to the expression by prepending ‘ROLE_’ to the role name.
Below is the snapshot of the class definition for SecurityExpressionRoot for both the versions of Spring Security which defines the methods hasRole and hasAuthority.
In Spring Security 3.2.x
hasRole are checking for the presence of given role name in
getAuthoritySet() [The getAuthoritySet() retrieves the GrantedAuthority list for the user]
In Spring Security 4.x
hasAuthority is invoking the API
hasAnyAuthorityName passing the prefix as null whereas hasRole is invoking the API
hasAnyAuthorityName passing the default prefix which is ‘ROLE_’ (the same has been highlighted in the image above)
There is another interesting API in Spring Security 4.x (again highlighted in the image above) called
getRoleWithDefaultPrefix() as shown in image below:
Interesting to see above how the role name is being prefixed with the role prefix.
What is the fix?
- Either you append all your roles with ‘ROLE_’ prefix. OR
- Use hasAuthority as the replacement for hasRole expression without the need for changing the role names OR
- Override the defaultRolePrefix with null or empty string so that the same expression hasRole works with the same role names. [I need to figure out how to do this. It should be possible because the setter for the property is public]
Ad3. I noticed that is not simple. If you find the fix/solution please write.
What I found: https://github.com/spring-projects/spring-security/issues/2984
and solution with DefaultRolesPrefixPostProcessor