Skip to content

Instantly share code, notes, and snippets.

@romanoffs
Last active July 28, 2020 10:55
Show Gist options
  • Save romanoffs/728effb5e88c9b4e210b1889cdfe6153 to your computer and use it in GitHub Desktop.
Save romanoffs/728effb5e88c9b4e210b1889cdfe6153 to your computer and use it in GitHub Desktop.
Implement for Spring Security Remember Me Redis repository
import lombok.RequiredArgsConstructor;
import lombok.extern.log4j.Log4j2;
import lombok.val;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ScanOptions;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import org.springframework.lang.Nullable;
import org.springframework.security.web.authentication.rememberme.PersistentRememberMeToken;
import org.springframework.security.web.authentication.rememberme.PersistentTokenRepository;
import org.springframework.stereotype.Component;
import java.io.IOException;
import java.util.Arrays;
import java.util.Date;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.TimeUnit;
@Component
@Log4j2
@RequiredArgsConstructor(onConstructor = @__(@Autowired))
class RedisTokenRepositoryImpl implements PersistentTokenRepository {
private final static int TOKEN_VALID_DAYS = 14;
private final static StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
private final static String USERNAME = "username";
private final static String TOKEN = "token";
private final static String LAST_USED_DATE = "last_used_date";
private final static String NAME_SPACE = "spring:security:rememberMe:token:";
private final RedisTemplate<String, String> redisTemplate;
@Override
public void createNewToken(PersistentRememberMeToken token) {
if (log.isDebugEnabled()) {
log.debug("token create seriesId: [{}]", token.getSeries());
}
val key = generateKey(token.getSeries());
val data = Map.of(
USERNAME, token.getUsername(),
TOKEN, token.getTokenValue(),
LAST_USED_DATE, String.valueOf(token.getDate().getTime())
);
redisTemplate.opsForHash().putAll(key, data);
redisTemplate.expire(key, TOKEN_VALID_DAYS, TimeUnit.DAYS);
}
@Override
public void updateToken(String series, String tokenValue, Date lastUsed) {
val key = generateKey(series);
val data = Map.of(
TOKEN, tokenValue,
LAST_USED_DATE, String.valueOf(lastUsed.getTime())
);
redisTemplate.opsForHash().putAll(key, data);
redisTemplate.expire(key, TOKEN_VALID_DAYS, TimeUnit.DAYS);
}
@Override
@Nullable
public PersistentRememberMeToken getTokenForSeries(String seriesId) {
val key = generateKey(seriesId);
val hashValues = redisTemplate.opsForHash().multiGet(key, Arrays.asList(USERNAME, TOKEN, LAST_USED_DATE));
val username = hashValues.get(0);
val tokenValue = hashValues.get(1);
val date = hashValues.get(2);
if (username == null || tokenValue == null || date == null) {
return null;
}
val timestamp = Long.parseLong(String.valueOf(date));
val time = new Date(timestamp);
return new PersistentRememberMeToken(String.valueOf(username), seriesId, String.valueOf(tokenValue), time);
}
@Override
public void removeUserTokens(String username) {
if (log.isDebugEnabled()) {
log.debug("token remove username: [{}]", username);
}
byte[] hashKey = stringRedisSerializer.serialize(USERNAME);
Objects.requireNonNull(redisTemplate.getConnectionFactory());
val redisConnection = redisTemplate.getConnectionFactory().getConnection()
try (val cursor = redisConnection.scan(ScanOptions.scanOptions().match(generateKey("*")).count(1024).build())) {
while (cursor.hasNext()) {
byte[] key = cursor.next();
byte[] hashValue = redisConnection.hGet(key, hashKey);
String storeName = stringRedisSerializer.deserialize(hashValue);
if (username.equals(storeName)) {
redisConnection.expire(key, 0L);
return;
}
}
} catch (IOException ex) {
log.error("token remove exception", ex);
}
}
private static String generateKey(String series) {
return NAME_SPACE + series;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment