Search code examples
javaspring-bootcachingspring-securityehcache

Get current authenticated user from security context as key for Spring Cache


I have method without parameters and I want to cache return value. As cache key I want to use current authenticated user from security context

@Cacheable(value = "resultCache", key="#userPrincipal.id")
    public result getResult() {}

Is it possible or my idea is wrong.


Solution

  • You have four options to achieve this:

    1. Send the Authentication object as a method parameter:

      @Cacheable(value = "resultCache", key="#authentication.name")
      public Result getResult(Authentication authentication) {}
      
    2. Create a custom KeyGenerator and use it in your @Cacheable annotation

      public class CustomKeyGenerator implements KeyGenerator {
          @Override
          public Object generate(Object target, Method method, Object... params) {
              return SecurityContextHolder.getContext().getAuthentication().getName();
          }
      }
      
      @Configuration
      @EnableCaching
      public class CacheConfiguration {
      
          @Bean("customKeyGenerator")
          public KeyGenerator customKeyGenerator() {
              return new CustomKeyGenerator();
          }
      }
      
      @Cacheable(value = "resultCache", keyGenerator="customKeyGenerator")
      public Result getResult() {}
      
    3. Create a bean that provides you the key and references it via SPeL in the key property. I would recommend you going for this approach since it allows you to change the value later more easily.

      @Component
      public class CacheKeyProvider {
      
          public String getUsernameKey() {
              return SecurityContextHolder.getContext().getAuthentication().getName();
          }
      }
      
      @Cacheable(value = "resultCache", key="@cacheKeyProvider.getUsernameKey()")
      public Result getResult() {}
      
    4. Use the Type SpEL expression

      @Cacheable(value = "resultCache", key="T(org.springframework.security.core.context.SecurityContextHolder.getContext()?.authentication?.name)")
      public Result getResult() {}
      

    Note that I used the name property from the Principal in the examples. But if you have a custom Principal object you can cast it and return any property you want.