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

Persist Token

As we mentioned in the last chapter we have to associate the generated token with the user by storing it in database to make it available for verification on subsequent requests. We will store these tokens along with the user record in the database.

Let's modify our AppUser entity to store the token and it's expiry time like below:


public class AppUser {
    // Other fields omitted for brevity

    private String token;

    private Date tokenExpiryTime;
}

Our next step is to identify the AppUser record by username and update it with the generated token and it's expiry time. We will do this in UserService component by implementing below updateToken() method.


public void updateToken(String username, String token, Date tokenExpiryTime) {
    AppUser appUser = this.get(username);
    appUser.setToken(token);
    appUser.setTokenExpiryTime(tokenExpiryTime);
    appUserRepository.save(appUser);
}

Now we can call the above method from AuthenticationService immediately after generating the token on successful authentication.


if (authentication.isAuthenticated()) {
    token = UUID.randomUUID().toString();
    userService.updateToken(authentication.getName(), token, this.getTokenExpiryTime());
}

Restart the application to automatically update the APP_USER table with the newly added two columns. Test the GenerateToken API to see the generated token persisted for the given user in the APP_USER table.

Secure update token service

Are we done with persisting the token? Not Completely. We should never let anyone call the updateToken() in UserService anonymously. We have to secure every service method as part of Service Layer Security. In this case we want only the user who requested the token to update his record with the generated token.

Let's define the below condition as a constant in Authority class, where #username represents one of the arguments with the same name defined in updateToken() method, and authentication represents the current authenticated user provided by Spring Security from SecurityContext.


public static final String UPDATE_TOKEN = "#username == authentication.name";

Let's add the above condition to be evaluated before the execution of updateToken() using @PreAuthroize annotation.


@PreAuthorize(Authority.UPDATE_TOKEN)
public void updateToken(String username, String token, Date tokenExpiryTime) {
    // Details omitted for brevity
}

As we are authenticating the user using AuthenticationManager by ourselves, SecurityContext will not be aware of the Authentication object returned by the authenticate() method. Unlike Basic Auth, where everything was done out of the box by Spring Security for us, here we have to explicitly set the authenticated Authentication object in SecurityContext.

Set authentication in SecurityContext

Let's define another method in AuthenticationFacade to set the SecurityContext with the authenticated Authentication object


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

    public void setAuthentication(Authentication authentication) {
        SecurityContextHolder.getContext().setAuthentication(authentication);
    }
}

Lastly we will call the above setAuthentication() from AuthenticationService immediately before calling the updateToken() in UserService. The final generateToken() thus looks like below:


public String generateToken(String username, String password) {
    Authentication authentication = UsernamePasswordAuthenticationToken.unauthenticated(username, password);
    authentication = authenticationManager.authenticate(authentication);

    String token = null;
    if (authentication.isAuthenticated()) {        
        token = UUID.randomUUID().toString();
        authenticationFacade.setAuthentication(authentication);
        userService.updateToken(authentication.getName(), token, this.getTokenExpiryTime());
    }

    return token;
}

public Date getTokenExpiryTime() {
    Calendar calendar = Calendar.getInstance();
    calendar.setTime(new Date());
    calendar.add(Calendar.MINUTE, 30);
    return calendar.getTime();
}