Caching the current user

The sample code from https://start.vaadin.com/ has

@Component
public class AuthenticatedUser {

    private final UserRepository userRepository;
    private final AuthenticationContext authenticationContext;

    public AuthenticatedUser(AuthenticationContext authenticationContext, UserRepository userRepository) {
        this.userRepository = userRepository;
        this.authenticationContext = authenticationContext;
    }

    @Transactional
    public Optional<User> get() {
        return authenticationContext.getAuthenticatedUser(UserDetails.class)
                .flatMap(userDetails -> userRepository.findByUsername(userDetails.getUsername()));
    }

    public void logout() {
        authenticationContext.logout();
    }
}

Every call to AuthenticatedUser.get() results in a DB call.

What is the recommended way to cache the current user?
-Inside the AuthenticatedUser?
-In the VaadinSession?
-Anything else?

I usually use default Spring caching via annotations see Caching :: Spring Boot

  1. enable caching in your main class via @EnableCaching

  2. cache the result of your userRepository method like so:

@Cacheable(value = CACHE_USER_BY_USERNAME, key = "#username")
public UserDetails findByUsername(String username) {
 // ... DB calls etc. 
}
  1. evict this cache on every save/update method for the userDetails
@Caching(evict = {
        @CacheEvict(value = CACHE_USER_BY_USERNAME, allEntries = false, key = "#user.username", condition = "#user.username != null"),
    })
    public UserDetails save(User user) {
  // ... DB calls etc.
}

Some hints:

  • use static final String constants for cache name for reusing in CacheEvict
  • use allEntries = true if your’re unsure about your pojo … but cache performace gets worser

But be aware:
There are only two hard things in Computer Science: cache invalidation and naming things.
– Phil Karlton
:wink:

1 Like

Thats quite interesting! Where is that Cache persisted?

Thats depends on the implementation :slight_smile:
The Spring default is in-memory using simple map structures

Since my requirement was to cache only the currently logged in user, I ended up caching it in the VaadinSession

I was actually not aware of the spring caching mechanism. Learnt something new today!
Thank you Dominik!

1 Like

Just asking the obvious question: why not use the default of spring security? Servlet Authentication Architecture :: Spring Security

@knoobie What did you mean with default of spring security ?

The usage of UserDetails is default and the use of a default cache mechanism is … default :slight_smile:

Spring Security already handles the current user via SecurityContext(Holder) so I was wondering why you wanna reinvent the wheel :grimacing:

IMO the SpringSecurityContextHolder contains an Authentication object, which is normally kind of UsernamePasswordAuthenticationToken containing a username. If you need more data than the userDetails object already contains and you need to fetch those from your database you can benefit from such caching strategy. But you’re right, my example was a little simplified regarding that extrapolation :slight_smile:
But here @Vaadin projects, we can benefit from AuthenticationContext which contains the authenticated user too and is injectable as Spring Bean :slight_smile:

I was wondering because I normally just call Authentication::getPrincipal (from the security context) and there you get the UserDetails that was previously fetched from your method mentioned above

1 Like

I’ve handled this by storing the user info in the session instead of trying to cache it manually. Vaadin keeps the session pretty clean, so just grabbing the user from there when needed works well and avoids weird sync issues. Simple and reliable.