IntrospectingTokenService now takes parameters (cacheTokens, cacheNonExpiringTokens, defaultExpireTime, forceCacheExpireTime) to change the behavior or even disable the caching of responses from the IntrospectionEndpoint.

pull/839/head
Stephen Moore 2015-06-04 16:33:37 -04:00
parent 286d433da6
commit 698fe55b85
1 changed files with 57 additions and 29 deletions

View File

@ -19,6 +19,7 @@ package org.mitre.oauth2.introspectingfilter;
import java.io.IOException; import java.io.IOException;
import java.net.URI; import java.net.URI;
import java.util.Date; import java.util.Date;
import java.util.Calendar;
import java.util.HashMap; import java.util.HashMap;
import java.util.HashSet; import java.util.HashSet;
import java.util.Map; import java.util.Map;
@ -67,6 +68,11 @@ public class IntrospectingTokenService implements ResourceServerTokenServices {
private IntrospectionConfigurationService introspectionConfigurationService; private IntrospectionConfigurationService introspectionConfigurationService;
private IntrospectionAuthorityGranter introspectionAuthorityGranter = new SimpleIntrospectionAuthorityGranter(); private IntrospectionAuthorityGranter introspectionAuthorityGranter = new SimpleIntrospectionAuthorityGranter();
private int defaultExpireTime = 300000; // 5 minutes in milliseconds
private boolean forceCacheExpireTime = false; // force removal of cached tokens based on default expire time
private boolean cacheNonExpiringTokens = false;
private boolean cacheTokens = true;
private HttpClient httpClient = HttpClientBuilder.create() private HttpClient httpClient = HttpClientBuilder.create()
.useSystemProperties() .useSystemProperties()
.build(); .build();
@ -76,10 +82,22 @@ public class IntrospectingTokenService implements ResourceServerTokenServices {
private class TokenCacheObject { private class TokenCacheObject {
OAuth2AccessToken token; OAuth2AccessToken token;
OAuth2Authentication auth; OAuth2Authentication auth;
Date cacheExpire;
private TokenCacheObject(OAuth2AccessToken token, OAuth2Authentication auth) { private TokenCacheObject(OAuth2AccessToken token, OAuth2Authentication auth) {
this.token = token; this.token = token;
this.auth = auth; this.auth = auth;
// if the token doesn't have an expire time, use the default expire time
// also use the default expire time if the token is valid for longer than that time (i.e. force a check of the token, if force check is valid)
if (this.token.getExpiration() != null || (forceCacheExpireTime && (this.token.getExpiration().getTime() - System.currentTimeMillis() <= defaultExpireTime))) {
this.cacheExpire = this.token.getExpiration();
} else {
Calendar cal = Calendar.getInstance();
cal.add(Calendar.MILLISECOND, defaultExpireTime);
this.cacheExpire = cal.getTime();
}
} }
} }
@ -117,13 +135,29 @@ public class IntrospectingTokenService implements ResourceServerTokenServices {
return introspectionAuthorityGranter; return introspectionAuthorityGranter;
} }
/**
* get the default cache expire time in milliseconds
* @return
*/
public int getDefaultExpireTime() {
return defaultExpireTime;
}
/**
* set the default cache expire time in milliseconds
* @param defaultExpireTime
*/
public void setDefaultExpireTime(int defaultExpireTime) {
this.defaultExpireTime = defaultExpireTime;
}
// Check if there is a token and authentication in the cache // Check if there is a token and authentication in the cache
// and check if it is not expired. // and check if it is not expired.
private TokenCacheObject checkCache(String key) { private TokenCacheObject checkCache(String key) {
if (authCache.containsKey(key)) { if (cacheTokens && authCache.containsKey(key)) {
TokenCacheObject tco = authCache.get(key); TokenCacheObject tco = authCache.get(key);
// for this introspection service, null expiration means tokens don't expire
if (tco.token.getExpiration() == null || tco.token.getExpiration().after(new Date())) { if (tco != null && tco.cacheExpire != null && tco.cacheExpire.after(new Date())) {
return tco; return tco;
} else { } else {
// if the token is expired, don't keep things around. // if the token is expired, don't keep things around.
@ -156,9 +190,9 @@ public class IntrospectingTokenService implements ResourceServerTokenServices {
} }
// Validate a token string against the introspection endpoint, // Validate a token string against the introspection endpoint,
// then parse it and store it in the local cache. Return true on // then parse it and store it in the local cache. Return TokenCacheObject
// success, false otherwise. // if token is valid, otherwise return null
private boolean parseToken(String accessToken) { private TokenCacheObject parseToken(String accessToken) {
// find out which URL to ask // find out which URL to ask
String introspectionUrl; String introspectionUrl;
@ -168,7 +202,7 @@ public class IntrospectingTokenService implements ResourceServerTokenServices {
client = introspectionConfigurationService.getClientConfiguration(accessToken); client = introspectionConfigurationService.getClientConfiguration(accessToken);
} catch (IllegalArgumentException e) { } catch (IllegalArgumentException e) {
logger.error("Unable to load introspection URL or client configuration", e); logger.error("Unable to load introspection URL or client configuration", e);
return false; return null;
} }
// Use the SpringFramework RestTemplate to send the request to the // Use the SpringFramework RestTemplate to send the request to the
// endpoint // endpoint
@ -210,7 +244,7 @@ public class IntrospectingTokenService implements ResourceServerTokenServices {
// parse the json // parse the json
JsonElement jsonRoot = new JsonParser().parse(validatedToken); JsonElement jsonRoot = new JsonParser().parse(validatedToken);
if (!jsonRoot.isJsonObject()) { if (!jsonRoot.isJsonObject()) {
return false; // didn't get a proper JSON object return null; // didn't get a proper JSON object
} }
JsonObject tokenResponse = jsonRoot.getAsJsonObject(); JsonObject tokenResponse = jsonRoot.getAsJsonObject();
@ -218,13 +252,13 @@ public class IntrospectingTokenService implements ResourceServerTokenServices {
if (tokenResponse.get("error") != null) { if (tokenResponse.get("error") != null) {
// report an error? // report an error?
logger.error("Got an error back: " + tokenResponse.get("error") + ", " + tokenResponse.get("error_description")); logger.error("Got an error back: " + tokenResponse.get("error") + ", " + tokenResponse.get("error_description"));
return false; return null;
} }
if (!tokenResponse.get("active").getAsBoolean()) { if (!tokenResponse.get("active").getAsBoolean()) {
// non-valid token // non-valid token
logger.info("Server returned non-active token"); logger.info("Server returned non-active token");
return false; return null;
} }
// create an OAuth2Authentication // create an OAuth2Authentication
OAuth2Authentication auth = new OAuth2Authentication(createStoredRequest(tokenResponse), createAuthentication(tokenResponse)); OAuth2Authentication auth = new OAuth2Authentication(createStoredRequest(tokenResponse), createAuthentication(tokenResponse));
@ -233,14 +267,16 @@ public class IntrospectingTokenService implements ResourceServerTokenServices {
if (token.getExpiration() == null || token.getExpiration().after(new Date())) { if (token.getExpiration() == null || token.getExpiration().after(new Date())) {
// Store them in the cache // Store them in the cache
authCache.put(accessToken, new TokenCacheObject(token, auth)); TokenCacheObject tco = new TokenCacheObject(token, auth);
if (cacheTokens && (cacheNonExpiringTokens || token.getExpiration() != null)) {
return true; authCache.put(accessToken, tco);
}
return tco;
} }
} }
// If we never put a token and an authentication in the cache... // If we never put a token and an authentication in the cache...
return false; return null;
} }
@Override @Override
@ -252,13 +288,9 @@ public class IntrospectingTokenService implements ResourceServerTokenServices {
if (cacheAuth != null) { if (cacheAuth != null) {
return cacheAuth.auth; return cacheAuth.auth;
} else { } else {
if (parseToken(accessToken)) { cacheAuth = parseToken(accessToken);
cacheAuth = authCache.get(accessToken); if (cacheAuth != null) {
if (cacheAuth != null && (cacheAuth.token.getExpiration() == null || cacheAuth.token.getExpiration().after(new Date()))) { return cacheAuth.auth;
return cacheAuth.auth;
} else {
return null;
}
} else { } else {
return null; return null;
} }
@ -274,13 +306,9 @@ public class IntrospectingTokenService implements ResourceServerTokenServices {
if (cacheAuth != null) { if (cacheAuth != null) {
return cacheAuth.token; return cacheAuth.token;
} else { } else {
if (parseToken(accessToken)) { cacheAuth = parseToken(accessToken);
cacheAuth = authCache.get(accessToken); if (cacheAuth != null) {
if (cacheAuth != null && (cacheAuth.token.getExpiration() == null || cacheAuth.token.getExpiration().after(new Date()))) { return cacheAuth.token;
return cacheAuth.token;
} else {
return null;
}
} else { } else {
return null; return null;
} }