Created
January 19, 2015 20:27
-
-
Save efenderbosch/c825346e0a98de405bc2 to your computer and use it in GitHub Desktop.
Redis Token Store for Spring Security
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public class RedisTokenStore implements TokenStore { | |
private static final StringRedisSerializer STRING_SERIALIZER = new StringRedisSerializer(); | |
private static final ObjectMapper OBJECT_MAPPER = new JodaMapper().disable(WRITE_DATES_AS_TIMESTAMPS); | |
private final BoundHashOperations<String, String, OAuth2AccessToken> accessTokenStore; | |
private final BoundHashOperations<String, String, OAuth2AccessToken> authenticationToAccessTokenStore; | |
private final ListOperations<String, OAuth2AccessToken> userNameToAccessTokenStore; | |
private final ListOperations<String, OAuth2AccessToken> clientIdToAccessTokenStore; | |
private final BoundHashOperations<String, String, OAuth2RefreshToken> refreshTokenStore; | |
private final BoundHashOperations<String, String, String> accessTokenToRefreshTokenStore; | |
private final BoundHashOperations<String, String, String> refreshTokenToAccessTokenStore; | |
private final BoundHashOperations<String, String, OAuth2Authentication> authenticationStore; | |
private final BoundHashOperations<String, String, OAuth2Authentication> refreshTokenAuthenticationStore; | |
private AuthenticationKeyGenerator authenticationKeyGenerator = new DefaultAuthenticationKeyGenerator(); | |
public RedisTokenStore(RedisConnectionFactory connectionFactory) { | |
RedisSerializer<OAuth2AccessToken> accessTokenSerializer = jsonSerializerFor(OAuth2AccessToken.class); | |
RedisTemplate<String, OAuth2AccessToken> accessTokenTemplate = constructTemplate(connectionFactory, | |
accessTokenSerializer); | |
accessTokenStore = accessTokenTemplate.boundHashOps("access"); | |
authenticationToAccessTokenStore = accessTokenTemplate.boundHashOps("auth_to_access"); | |
PrependedKeyRedisTemplate<OAuth2AccessToken> userNameToAccessTokenTemplate = constructPrependedKeyTemplate( | |
connectionFactory, accessTokenSerializer, "uname_to_access:"); | |
userNameToAccessTokenStore = userNameToAccessTokenTemplate.opsForList(); | |
PrependedKeyRedisTemplate<OAuth2AccessToken> clientIdToAccessTokenTemplate = constructPrependedKeyTemplate( | |
connectionFactory, accessTokenSerializer, "client_id_to_access:"); | |
clientIdToAccessTokenStore = clientIdToAccessTokenTemplate.opsForList(); | |
RedisTemplate<String, OAuth2RefreshToken> refreshTokenTemplate = constructTemplate(connectionFactory, | |
jsonSerializerFor(OAuth2RefreshToken.class)); | |
refreshTokenStore = refreshTokenTemplate.boundHashOps("refresh"); | |
RedisTemplate<String, String> mappingTokenTemplate = constructTemplate(connectionFactory, STRING_SERIALIZER); | |
accessTokenToRefreshTokenStore = mappingTokenTemplate.boundHashOps("access_to_refresh"); | |
refreshTokenToAccessTokenStore = mappingTokenTemplate.boundHashOps("refresh_to_access"); | |
RedisTemplate<String, OAuth2Authentication> authenticationTemplate = constructTemplate(connectionFactory, | |
jsonSerializerFor(OAuth2Authentication.class)); | |
authenticationStore = authenticationTemplate.boundHashOps("auth"); | |
refreshTokenAuthenticationStore = authenticationTemplate.boundHashOps("refresh_auth"); | |
} | |
private static <V> RedisTemplate<String, V> constructTemplate(RedisConnectionFactory connectionFactory, | |
RedisSerializer<V> valueSerializer) { | |
RedisTemplate<String, V> template = new RedisTemplate<>(); | |
template.setConnectionFactory(connectionFactory); | |
template.setKeySerializer(STRING_SERIALIZER); | |
template.setValueSerializer(valueSerializer); | |
template.afterPropertiesSet(); | |
return template; | |
} | |
private static <V> PrependedKeyRedisTemplate<V> constructPrependedKeyTemplate( | |
RedisConnectionFactory connectionFactory, RedisSerializer<V> valueSerializer, String prependedKey) { | |
PrependedKeyRedisTemplate<V> template = new PrependedKeyRedisTemplate<>(prependedKey); | |
template.setConnectionFactory(connectionFactory); | |
template.setKeySerializer(STRING_SERIALIZER); | |
template.setValueSerializer(valueSerializer); | |
template.afterPropertiesSet(); | |
return template; | |
} | |
private static <T> RedisSerializer<T> jsonSerializerFor(Class<T> clazz) { | |
Jackson2JsonRedisSerializer<T> serializer = new Jackson2JsonRedisSerializer<>(clazz); | |
serializer.setObjectMapper(OBJECT_MAPPER); | |
return serializer; | |
} | |
@Override | |
public OAuth2AccessToken getAccessToken(OAuth2Authentication authentication) { | |
String key = authenticationKeyGenerator.extractKey(authentication); | |
OAuth2AccessToken accessToken = authenticationToAccessTokenStore.get(key); | |
if (accessToken != null | |
&& !key.equals(authenticationKeyGenerator.extractKey(readAuthentication(accessToken.getValue())))) { | |
// Keep the stores consistent (maybe the same user is represented by | |
// this authentication but the details have changed) | |
storeAccessToken(accessToken, authentication); | |
} | |
return accessToken; | |
} | |
@Override | |
public OAuth2Authentication readAuthentication(OAuth2AccessToken token) { | |
return readAuthentication(token.getValue()); | |
} | |
@Override | |
public OAuth2Authentication readAuthentication(String token) { | |
return authenticationStore.get(token); | |
} | |
@Override | |
public OAuth2Authentication readAuthenticationForRefreshToken(OAuth2RefreshToken token) { | |
return readAuthenticationForRefreshToken(token.getValue()); | |
} | |
public OAuth2Authentication readAuthenticationForRefreshToken(String token) { | |
return refreshTokenAuthenticationStore.get(token); | |
} | |
@Override | |
public void storeAccessToken(OAuth2AccessToken token, OAuth2Authentication authentication) { | |
accessTokenStore.put(token.getValue(), token); | |
authenticationStore.put(token.getValue(), authentication); | |
authenticationToAccessTokenStore.put(authenticationKeyGenerator.extractKey(authentication), token); | |
if (!authentication.isClientOnly()) { | |
userNameToAccessTokenStore.rightPush(getApprovalKey(authentication), token); | |
} | |
clientIdToAccessTokenStore.rightPush(authentication.getOAuth2Request().getClientId(), token); | |
if (token.getExpiration() != null) { | |
// TODO | |
// TokenExpiry expiry = new TokenExpiry(token.getValue(), | |
// token.getExpiration()); | |
// // Remove existing expiry for this token if present | |
// expiryQueue.remove(expiryMap.put(token.getValue(), expiry)); | |
// expiryQueue.put(expiry); | |
} | |
if (token.getRefreshToken() != null && token.getRefreshToken().getValue() != null) { | |
refreshTokenToAccessTokenStore.put(token.getRefreshToken().getValue(), token.getValue()); | |
accessTokenToRefreshTokenStore.put(token.getValue(), token.getRefreshToken().getValue()); | |
} | |
} | |
private static String getApprovalKey(OAuth2Authentication authentication) { | |
String userName = authentication.getUserAuthentication() == null ? "" : authentication.getUserAuthentication() | |
.getName(); | |
return getApprovalKey(authentication.getOAuth2Request().getClientId(), userName); | |
} | |
private static String getApprovalKey(String clientId, String userName) { | |
return clientId + (userName == null ? "" : ":" + userName); | |
} | |
@Override | |
public void removeAccessToken(OAuth2AccessToken accessToken) { | |
removeAccessToken(accessToken.getValue()); | |
} | |
@Override | |
public OAuth2AccessToken readAccessToken(String tokenValue) { | |
return accessTokenStore.get(tokenValue); | |
} | |
public void removeAccessToken(String tokenValue) { | |
// TODO pipeline | |
OAuth2AccessToken removed = accessTokenStore.get(tokenValue); | |
accessTokenStore.delete(tokenValue); | |
accessTokenToRefreshTokenStore.delete(tokenValue); | |
// Don't remove the refresh token - it's up to the caller to do that | |
OAuth2Authentication authentication = authenticationStore.get(tokenValue); | |
authenticationStore.delete(tokenValue); | |
if (authentication != null) { | |
authenticationToAccessTokenStore.delete(authenticationKeyGenerator.extractKey(authentication)); | |
userNameToAccessTokenStore.remove(authentication.getName(), 0, removed); | |
String clientId = authentication.getOAuth2Request().getClientId(); | |
clientIdToAccessTokenStore.remove(clientId, 0, removed); | |
accessTokenStore.delete(authenticationKeyGenerator.extractKey(authentication)); | |
} | |
} | |
@Override | |
public void storeRefreshToken(OAuth2RefreshToken refreshToken, OAuth2Authentication authentication) { | |
refreshTokenStore.put(refreshToken.getValue(), refreshToken); | |
refreshTokenAuthenticationStore.put(refreshToken.getValue(), authentication); | |
} | |
@Override | |
public OAuth2RefreshToken readRefreshToken(String tokenValue) { | |
return refreshTokenStore.get(tokenValue); | |
} | |
@Override | |
public void removeRefreshToken(OAuth2RefreshToken refreshToken) { | |
removeRefreshToken(refreshToken.getValue()); | |
} | |
public void removeRefreshToken(String tokenValue) { | |
refreshTokenStore.delete(tokenValue); | |
refreshTokenToAccessTokenStore.delete(tokenValue); | |
accessTokenToRefreshTokenStore.delete(tokenValue); | |
} | |
@Override | |
public void removeAccessTokenUsingRefreshToken(OAuth2RefreshToken refreshToken) { | |
removeAccessTokenUsingRefreshToken(refreshToken.getValue()); | |
} | |
private void removeAccessTokenUsingRefreshToken(String refreshToken) { | |
// TODO pipeline | |
String accessToken = refreshTokenToAccessTokenStore.get(refreshToken); | |
refreshTokenToAccessTokenStore.delete(refreshToken); | |
if (accessToken != null) { | |
removeAccessToken(accessToken); | |
} | |
} | |
@Override | |
public Collection<OAuth2AccessToken> findTokensByClientIdAndUserName(String clientId, String userName) { | |
Collection<OAuth2AccessToken> result = userNameToAccessTokenStore | |
.range(getApprovalKey(clientId, userName), 0, -1); | |
return result != null ? Collections.<OAuth2AccessToken> unmodifiableCollection(result) : Collections | |
.<OAuth2AccessToken> emptySet(); | |
} | |
@Override | |
public Collection<OAuth2AccessToken> findTokensByClientId(String clientId) { | |
Collection<OAuth2AccessToken> result = clientIdToAccessTokenStore.range(clientId, 0, -1); | |
return result != null ? Collections.<OAuth2AccessToken> unmodifiableCollection(result) : Collections | |
.<OAuth2AccessToken> emptySet(); | |
} | |
} |
hi Eric , Is this a working version? What exactly does Pipeline mean - pure redis?
What is the status of the TODOs - working and tested in a different way or still has issues/ to be tested?
Do you have the version with the redis template that you can share?? Or a version with spring cacheable ? I was planning on using
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
where i can find PrependedKeyRedisTemplate class/java file?