FC
FACADE
CODE
Lesson 16 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

Authorize Using Spring Beans

Simple authorization rules can be written using SpEL expressions inside Method security annotations. But there can be rules which are quite harder to write in SpEL, where the expression itself could be better off reside in its own method rather than as a complex String constant.

Make Instructor's profile public

Let's extend the ViewProfile usecase to make Instructor's profiles public in addition to viewing one's own profile. This can only be done by checking if the requested user profile has Instructor role something like below.


appuser.getRoles()
    .stream()
    .anyMatch(appRole ->
            appRole.getName().equals(RoleEnum.INSTRUCTOR)
    );

Is there any simpler way to implement this in SpEL expressions? Well, we can do this using Spring Beans. Let's define a Spring Bean @Component to hold a collection of helper methods which can be used to secure your Service layer.


@Component("serviceSecurity")  
public class ServiceSecurity {
    public Boolean isInstructor(AppUser appUser) {
      appuser.getRoles()
          .stream()
          .anyMatch(appRole ->
                  appRole.getName().equals(RoleEnum.INSTRUCTOR)
          );
    }
}

We can call the above Bean method with the requested user profile as the argument. We can refer the Bean component using it's name prefixed with @ [@serviceSecurity] and we can access the requested user profile from returnObject inside @PostAuthorize(). We should combine it with the existing condition with an or condition like below.


@PreAuthorize(Authority.VIEW_PROFILE)
@PostAuthorize("@serviceSecurity.isInstructor(returnObject) or returnObject.username == authentication.name")
public AppUser get(Long userId) {
    ...
}

Let's send the last unauthorized request again to view Gru's user profile using Bob's credential. We can view the Instructor's profile this time.

Authorized request to view Gru (Instructor) profile with Bob's credential
Authorized request to view Gru (Instructor) profile with Bob's credential