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

Current Authenticated User

In the last chapter, you might have noticed the value for createdBy field is null in the newly created Course object. Of course, we did not send the user details in the request body for the createdBy field. As every request is authenticated, it makes sense to expect the requestor (Gru) details to get mapped automatically in the createdBy field.

SecurityContextHolder

At the heart of Spring Security's authentication model is SecurityContextHolder. It contains SecurityContext holding the details of who is authenticated in the form of Authentication object. It uses ThreadLocal to store these details to make it available for all the methods in the same thread through out the request.

Authentication

Authentication represents the token for an authentication request or for an authenticated principal. Technically it serves as the input and output for the one and only authenticate() method in AuthenticationManager interface.


public interface AuthenticationManager {
    Authentication authenticate(Authentication authentication)
}

As an authentication request it carries the username and password provided by the user where the isAuthenticated flag is set to false by default. Once the request has been processed by the AuthenticationManager it returns back the Authentication object as authenticated principal. As an authenticated principal it carries either the username or UserDetails and the collection of GrantedAuthority of the authenticated user where the isAuthenticated flag is set to true.

Let's create a Facade component to get this Authentication object as an authenticated principal from SecurityContextHolder.


@Component
public class AuthenticationFacade {
    public Authentication getAuthentication() {
        return SecurityContextHolder.getContext().getAuthentication();
    }
}

Find AppUser using Authentication object

As the variable type of createdBy field in the Course entity is AppUser, we have to retrieve the AppUser record from the database using the username found in the Authentication object.

Let's define findByUsername() method in AppUserRepository interface.


@Repository
public interface AppUserRepository extends JpaRepository<AppUser, Long> {
    Optional<AppUser> findByUsername(String username);
}

Add a service method in UserService to get the AppUser object by username using the above method.


public AppUser get(String username) {
    return appUserRepository.findByUsername(username)
            .orElse(null);
}

Create course service

Now let's wire all the above pieces together in the create() service method defined in CourseService component, in order to update the createdBy field with the AppUser object before saving the new Course.


public Course create(Course newCourse) {
    String username = authenticationFacade.getAuthentication().getName();
    AppUser currentUser = userService.get(username);
    newCourse.setCreatedBy(currentUser);
    return courseRepo.save(newCourse);
}

Restart the application and send a POST request to CreateCourse API as Gru who has INSTRUCTOR role. And you can see the createdBy field automatically saved with the requestor details even though we have not sent it as part of the request body.

Newly created course with createdBy field having the requestor details
Newly created course with createdBy field having the requestor details