001/* 002 * nimbus-jose-jwt 003 * 004 * Copyright 2012-2016, Connect2id Ltd and contributors. 005 * 006 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use 007 * this file except in compliance with the License. You may obtain a copy of the 008 * License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, software distributed 013 * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR 014 * CONDITIONS OF ANY KIND, either express or implied. See the License for the 015 * specific language governing permissions and limitations under the License. 016 */ 017 018package com.nimbusds.jose.jwk.source; 019 020 021import java.util.Date; 022import java.util.concurrent.TimeUnit; 023 024import net.jcip.annotations.ThreadSafe; 025 026import com.nimbusds.jose.jwk.JWKSet; 027 028 029/** 030 * JSON Web Key (JWK) set cache implementation. 031 * 032 * @author Vladimir Dzhuvinov 033 * @author Sarvesh Sharma 034 * @version 2021-01-08 035 */ 036@ThreadSafe 037public class DefaultJWKSetCache implements JWKSetCache { 038 039 040 /** 041 * The default lifespan for cached JWK sets (15 minutes). 042 */ 043 public static final long DEFAULT_LIFESPAN_MINUTES = 15; 044 045 046 /** 047 * The default refresh time for cached JWK sets (5 minutes). 048 */ 049 public static final long DEFAULT_REFRESH_TIME_MINUTES = 5; 050 051 052 /** 053 * The lifespan of the cached JWK set, in {@link #timeUnit}s, negative 054 * means no expiration. 055 */ 056 private final long lifespan; 057 058 059 /** 060 * The refresh time of the cached JWK set, in {@link #timeUnit}s, 061 * negative means no refresh time. 062 */ 063 private final long refreshTime; 064 065 066 /** 067 * The time unit, may be {@code null} if no expiration / refresh time. 068 */ 069 private final TimeUnit timeUnit; 070 071 072 /** 073 * The cached JWK set, {@code null} if none. 074 */ 075 private volatile JWKSetWithTimestamp jwkSetWithTimestamp; 076 077 078 /** 079 * Creates a new JWK set, the default lifespan of the cached JWK set is 080 * set to 15 minutes, the refresh time to 5 minutes. 081 */ 082 public DefaultJWKSetCache() { 083 084 this(DEFAULT_LIFESPAN_MINUTES, DEFAULT_REFRESH_TIME_MINUTES, TimeUnit.MINUTES); 085 } 086 087 088 /** 089 * Creates a new JWK set cache. 090 * 091 * @param lifespan The lifespan of the cached JWK set before it 092 * expires, negative means no expiration. 093 * @param refreshTime The time after which the cached JWK set is marked 094 * for refresh, negative if not specified. Should be 095 * shorter or equal to the lifespan. 096 * @param timeUnit The lifespan time unit, may be {@code null} if no 097 * expiration or refresh time. 098 */ 099 public DefaultJWKSetCache(final long lifespan, final long refreshTime, final TimeUnit timeUnit) { 100 101 this.lifespan = lifespan; 102 this.refreshTime = refreshTime; 103 104 if ((lifespan > -1 || refreshTime > -1) && timeUnit == null) { 105 throw new IllegalArgumentException("A time unit must be specified for non-negative lifespans or refresh times"); 106 } 107 108 this.timeUnit = timeUnit; 109 } 110 111 112 @Override 113 public void put(final JWKSet jwkSet) { 114 115 final JWKSetWithTimestamp updatedJWKSetWithTs; 116 if (jwkSet != null) { 117 updatedJWKSetWithTs = new JWKSetWithTimestamp(jwkSet); 118 } else { 119 // clear cache 120 updatedJWKSetWithTs = null; 121 } 122 123 jwkSetWithTimestamp = updatedJWKSetWithTs; 124 } 125 126 127 @Override 128 public JWKSet get() { 129 130 if (jwkSetWithTimestamp == null || isExpired()) { 131 return null; 132 } 133 134 return jwkSetWithTimestamp.getJWKSet(); 135 } 136 137 138 @Override 139 public boolean requiresRefresh() { 140 141 return jwkSetWithTimestamp != null && 142 refreshTime > -1 && 143 new Date().getTime() > jwkSetWithTimestamp.getDate().getTime() + TimeUnit.MILLISECONDS.convert(refreshTime, timeUnit); 144 } 145 146 147 /** 148 * Returns the cache put timestamp. 149 * 150 * @return The cache put timestamp, negative if not specified. 151 */ 152 public long getPutTimestamp() { 153 154 return jwkSetWithTimestamp != null ? jwkSetWithTimestamp.getDate().getTime() : -1L; 155 } 156 157 158 /** 159 * Returns {@code true} if the cached JWK set is expired. 160 * 161 * @return {@code true} if expired. 162 */ 163 public boolean isExpired() { 164 165 return jwkSetWithTimestamp != null && 166 lifespan > -1 && 167 new Date().getTime() > jwkSetWithTimestamp.getDate().getTime() + TimeUnit.MILLISECONDS.convert(lifespan, timeUnit); 168 } 169 170 171 /** 172 * Returns the configured lifespan of the cached JWK. 173 * 174 * @param timeUnit The time unit to use. 175 * 176 * @return The configured lifespan, negative means no expiration. 177 */ 178 public long getLifespan(final TimeUnit timeUnit) { 179 180 if (lifespan < 0) { 181 return lifespan; 182 } 183 184 return timeUnit.convert(lifespan, this.timeUnit); 185 } 186 187 188 /** 189 * Returns the configured refresh time of the cached JWK. 190 * 191 * @param timeUnit The time unit to use. 192 * 193 * @return The configured refresh time, negative means no expiration. 194 */ 195 public long getRefreshTime(final TimeUnit timeUnit) { 196 197 if (refreshTime < 0) { 198 return refreshTime; 199 } 200 201 return timeUnit.convert(refreshTime, this.timeUnit); 202 } 203}