Skip to content

Instantly share code, notes, and snippets.

@efenderbosch
Created January 19, 2015 20:27
Show Gist options
  • Save efenderbosch/c825346e0a98de405bc2 to your computer and use it in GitHub Desktop.
Save efenderbosch/c825346e0a98de405bc2 to your computer and use it in GitHub Desktop.
Redis Token Store for Spring Security
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();
}
}
@shailesh-joshi
Copy link

where i can find PrependedKeyRedisTemplate class/java file?

@tgkprog
Copy link

tgkprog commented Apr 22, 2015

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