Learn Spring Security
Contents
Assign Permissions
In this chapter we will provide all the roles and permissions associated with each user in the UserDetails object using authorities().
ROLE_ Prefix
Remember we have applied both role-based as well as permission-based access restrictions to different APIs using hasRole() and hasAuthority() in HttpSecurity configuration. We have to provide both roles and permissions for each user in UserDetails object.
But as mentioned in Role Based Authorization chapter, we can not use them both on UserBuilder class, as the latter will always override the authorities set by the former. So we can only use authorities() by combining roles and permissions, where each role must be prefixed with ROLE_.
Let's update the existing getRoles() to append the prefix ROLE_ with each role assigned to the user as below:
private Set<String> getRoles(Set<AppRole> roles) {
return roles.stream()
.map(role -> String.format("ROLE_%s", role.getName().name()))
.collect(Collectors.toSet());
}
Consolidate Permissions
As one user has many roles and each role has many permissions, we need to combine all the permissions associated with each roles assigned to the user like below in a private method in DbUserDetailService.
private Set<String> getPermissions(Set<AppRole> roles) {
return roles.stream()
.flatMap(role -> role.getPermissions().stream())
.map(permission -> permission.getName().name())
.collect(Collectors.toSet());
}
Combine Roles and Permissions in Authorities
Now we will remove the roles() method and use only authorities() in User.builder() to combine roles and permissions for each user as below:
public List<UserDetails> getAllUserDetails() {
return appUserRepository.findAll()
.stream()
.map(appUser -> User.builder()
.username(appUser.getUsername())
.password(appUser.getPassword())
.authorities(this.getRolesAndPermissions(appUser.getRoles()))
.build()
)
.collect(Collectors.toList());
}
private String[] getRolesAndPermissions(Set<AppRole> appRoles) {
Set<String> roles = this.getRoles(appRoles);
Set<String> permissions = this.getPermissions(appRoles);
return new HashSet<String>() {
{
addAll(roles);
addAll(permissions);
}
}.toArray(new String[0]);
}
Finally we can expect each users to have their roles and permissions combined in the form of authorities as mentioned in the below table.
User | Authorities |
---|---|
Gru, Lucy | ROLE_INSTRUCTOR CREATE_COURSE UPDATE_COURSE PLAY_COURSE VIEW_PROFILE |
Bob, Kevin, Stuart | ROLE_STUDENT PLAY_COURSE VIEW_PROFILE |
Admin | ROLE_ADMIN LIST_STUDENTS LIST_INSTRUCTORS |
Restart the application and send a request to PlayCourse API for any course as either a Student (Bob) or an Instructor (Gru). We can see the Course detail as the response (the response is similar to GetCourse API, let's not worry about what the API is doing, we are only interested in securing it from Admin).
We will get 403 Forbidden error while we send the same request as an Admin user as he does not have PLAY_COURSE authority.
Note
So far we have just restricted the access to the PlayCourse API for the Admin user using Permissions instead of Roles. But we are yet to restrict the Student user to play only the course they are enrolled with, as per the Security Objective defined in Introduction chapter.