1. Overview
In this quick article, we’ll explain the subtle but significant difference between a Role and a GrantedAuthority in Spring Security. For more detailed information on roles and authorities, see the article here.
2. GrantedAuthority
In Spring Security, we can think of each GrantedAuthority as an individual privilege. Examples could include READ_AUTHORITY, WRITE_PRIVILEGE, or even CAN_EXECUTE_AS_ROOT. The important thing to understand is that the name is arbitrary.
When using a GrantedAuthority directly, such as through the use of an expression like hasAuthority(‘READ_AUTHORITY’), we are restricting access in a fine-grained manner.
As you can probably gather, we can refer to the concept of authority by using privilege as well.
3. Role as Authority
Similarly, in Spring Security, we can think of each Role as a coarse-grained GrantedAuthority that is represented as a String and prefixed with “ROLE“. When using a Role directly, such as through an expression like hasRole(“ADMIN”), we are restricting access in a coarse-grained manner.
It is worth noting that the default “ROLE” prefix is configurable, but explaining how to do that is beyond the scope of this article.
The core difference between these two is the semantics we attach to how we use the feature. For the framework, the difference is minimal – and it basically deals with these in exactly the same way.
4. Role as Container
Now that we’ve seen how the framework uses the role concept, let’s also quickly discuss an alternative – and that is using roles as containers of authorities/privileges.
This is a higher level approach to roles – making them a more business-facing concept rather than an implementation-centric one.
The Spring Security framework doesn’t give any guidance in terms of how we should use the concept, so the choice is entirely implementation specific.
5. Spring Security Configuration
We can demonstrate a fine-grained authorization requirement by restricting access to /protectedbyauthority to users with READ_AUTHORITY.
We can demonstrate a coarse-grained authorization requirement by restricting access to /protectedbyrole to users with ROLE_USER.
Let’s configure such a scenario in our security configuration:
@Override
protected void configure(HttpSecurity http) throws Exception {
// ...
.requestMatchers("/protectedbyrole").hasRole("USER")
.requestMatchers("/protectedbyauthority").hasAuthority("READ_PRIVILEGE")
// ...
}
6. Simple Data Init
Now that we understand the core concepts better, let’s talk about creating some setup data when the application starts up.
This is, of course, a very simple way of doing that, to hit the ground running with some preliminary test users during development – not the way you should handle data in production.
We’re going to be listening for the context refresh event:
@Override
@Transactional
public void onApplicationEvent(ContextRefreshedEvent event) {
MyPrivilege readPrivilege
= createPrivilegeIfNotFound("READ_PRIVILEGE");
MyPrivilege writePrivilege
= createPrivilegeIfNotFound("WRITE_PRIVILEGE");
}
The actual implementation here doesn’t really matter – and generally, depends on the persistence solution you’re using. The main point is – we’re persisting the authorities we’re using in the code.
7. UserDetailsService
Our implementation of UserDetailsService is where the authority mapping takes place. Once the user has authenticated, our getAuthorities() method populates and returns a UserDetails object:
private Collection<? extends GrantedAuthority> getAuthorities(
Collection<Role> roles) {
List<GrantedAuthority> authorities = new ArrayList<>();
for (Role role: roles) {
authorities.add(new SimpleGrantedAuthority(role.getName()));
authorities.addAll(role.getPrivileges()
.stream()
.map(p -> new SimpleGrantedAuthority(p.getName()))
.collect(Collectors.toList()));
}
return authorities;
}
8. Running and Testing the Example
We can execute the example RolesAuthoritiesApplication Java application, found in the GitHub project.
To see the role-based authorization in action, we need to:
- Access http://localhost:8082/protectedbyrole
- Authenticate as [email protected] (password is “user”)
- Note successful authorization
- Access http://localhost:8082/protectedbyauthority
- Note unsuccessful authorization
To see authority-based authorization in action, we need to log out of the application and then:
- Access http://localhost:8082/protectedbyauthority
- Authenticate as [email protected] / admin
- Note successful authorization
- Access http://localhsot:8082/protectedbyrole
- Note unsuccessful authorization
9. Conclusion
In this quick tutorial, we looked at the subtle but significant difference between a Role and a GrantedAuthority in Spring Security.