FC
FACADE
CODE
Lesson 12 of 28
FC
FACADE
CODE

Learn Spring Security

Contents

01. Bootstrap The Application
1. Introduction
2. Install Spring Security
3. Enable Basic Auth
4. Authentication with AppUser
5. Password Encoder
02. Web Layer Security - RBAC
6. Permit Public APIs
7. Role Based Authorization
8. Disable CSRF
9. Current Authenticated User
03. Web Layer Security - PBAC
10. Permission Based Authorization
11. Define Permissions
12. Assign Permissions
13. Remove Role Based Access
04. Service Layer Security
14. PreAuthorize
15. PostAuthorize
16. Authorize Using Spring Beans
05. Domain Object Instance Security
17. Domain Object Instance Security
18. PermissionEvaluator Interface
19. PermissionEvaluator Strategy
20. DB Backed UserDetailsService
06. Token Based Authentication
21. Basic Authentication Revisited
22. Generate Token
23. Persist Token
24. Verify Token
25. Invalidate Token
07. Token Based Authentication and Authorization
26. JSON Web Token
27. Generate JWT
28. Verify JWT

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.

UserAuthorities
Gru, LucyROLE_INSTRUCTOR
CREATE_COURSE
UPDATE_COURSE
PLAY_COURSE
VIEW_PROFILE
Bob, Kevin, StuartROLE_STUDENT
PLAY_COURSE
VIEW_PROFILE
AdminROLE_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).

Course detail response for Gru (Instructor) who has PLAY_COURSE authority
Course detail response for Gru (Instructor) who has PLAY_COURSE authority

We will get 403 Forbidden error while we send the same request as an Admin user as he does not have PLAY_COURSE authority.

403 Forbidden error for Admin who does not have PLAY_COURSE authority
403 Forbidden error for Admin who 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.