1

I would like to remove (invalidate) an item after it was read from a cache.

So item should be present in a cache until a first read.

I've tried adding expireAfterAccess(0, TimeUnit.NANOSECONDS) but then cache is not populated.

Is there any way to use guava cache in such manner or do I need to invalidate item manually after a read?

pixel
  • 24,905
  • 36
  • 149
  • 251
  • Why do you need cache, if you want to invalidate immediately after accessing? – Oleksandr Bondarchuk Dec 04 '17 at 21:17
  • @OleksandrBondarchuk I need to have entries that are expiring after certain period of time (eg. 15 minutes) and after read they should be disposed immediately. I want to store nonces - https://en.wikipedia.org/wiki/Cryptographic_nonce – pixel Dec 04 '17 at 21:32

3 Answers3

4

This won't work. "Access" means "read or write access" and when it expires immediately after read, then it also expires immediately after write.

You can remove the entry manually. You can use the asMap() view in order to do it in a single access:

String getAndClear(String key) {
    String[] result = {null};    
    cache.asMap().compute(key, (k, v) ->
        result[0] = v;
        return null;
    });
    return result[0];
}

You could switch to Caffeine, which is sort of more advanced Guava cache and offers very flexible expireAfter(Expiry).

However, I don't think, that what you want is a job for a cache. As nonces should never be repeated, I can't imagine any reason to store them. Usually, you generate and use them immediately.

You may be doing it wrong and you may want to elaborate, so that a possible security problem gets avoided.

maaartinus
  • 44,714
  • 32
  • 161
  • 320
  • Hi, there is such case for nonces in SafetyNet implementation by Google. Phone request for a nonce and then sends JWT token which server validates - in that moment you should validate wether JWT token contains nonce that was previously requested. See: https://www.synopsys.com/blogs/software-security/wp-content/uploads/2015/10/3_safetynet_secure_750.png – pixel Dec 05 '17 at 08:42
  • @pixel I see there "WebService stores nonce for session", but I can't see any load. Anyway, I updated my answer. – maaartinus Dec 05 '17 at 10:11
  • +1 to not using a `Cache` for this purpose - you should generate the nonce at the start of the work you need to do and then let it expire (by falling out of scope and being GCed) when you're done. The 15 minute expiration is simply to prevent you from making long or delayed requests, in practice you should never be keeping such a nonce around for anything close to the expiry limit. – dimo414 Dec 07 '17 at 17:04
1

In my example nonce are created twice:

LoadingCache<String, String> cache = CacheBuilder.newBuilder().expireAfterAccess(0, TimeUnit.NANOSECONDS)
    .build(new CacheLoader<String, String>() {
        @Override
        public String load(String key) throws Exception {
            return createNonce();
        }
    });

@Test
public void test_cache_eviction() throws Exception {
    String nonce1 = cache.getUnchecked("key");
    String nonce2 = cache.getUnchecked("key");
}

public String createNonce() {
    String createdNonce = "createdNonce";
    System.out.println(createdNonce);
    return createdNonce;
}

In logs "createdNonce" are printed twice.

0

The operation get and remove is a simple remove on the map interface:

 Object cachedValue = cache.asMap().remove(key);
cruftex
  • 5,545
  • 2
  • 20
  • 36