本配置使用form表单登录的方式
使用redis保存rememberMe过程中产生的token
项目源码:https://github.com/dk980241/spring-boot-security-demo
site.yuyanjia.springbootsecuritydemo.config.FormWebSecurityConfig
/**
* http安全配置
*
* @param http
* @throws Exception
*/
@Override
protected void configure(HttpSecurity http) throws Exception {
.....
.....
.....
http
.rememberMe()
.rememberMeParameter(REMEMBER_ME)
.tokenRepository(new RedisTokenRepositoryImpl());
}
Token 仓库实现
/**
* redis保存用户token
* <p>
* remember me
* <p>
* {@link org.springframework.security.web.authentication.rememberme.InMemoryTokenRepositoryImpl}
* {@link org.springframework.security.web.authentication.rememberme.PersistentTokenBasedRememberMeServices}
* {@link org.springframework.security.web.authentication.rememberme.PersistentRememberMeToken}
* PersistentRememberMeToken 没有实现Serializable,无法进行序列化,自定义存储数据结构
*/
class RedisTokenRepositoryImpl implements PersistentTokenRepository {
@Override
public void createNewToken(PersistentRememberMeToken token) {
if (log.isDebugEnabled()) {
log.debug("token create seriesId: [{}]", token.getSeries());
}
String key = generateKey(token.getSeries());
HashMap<String, String> map = new HashMap();
map.put("username", token.getUsername());
map.put("tokenValue", token.getTokenValue());
map.put("date", String.valueOf(token.getDate().getTime()));
redisTemplate.opsForHash().putAll(key, map);
redisTemplate.expire(key, TOKEN_VALID_DAYS, TimeUnit.DAYS);
}
@Override
public void updateToken(String series, String tokenValue, Date lastUsed) {
String key = generateKey(series);
HashMap<String, String> map = new HashMap();
map.put("tokenValue", tokenValue);
map.put("date", String.valueOf(lastUsed.getTime()));
redisTemplate.opsForHash().putAll(key, map);
redisTemplate.expire(key, TOKEN_VALID_DAYS, TimeUnit.DAYS);
}
@Override
public PersistentRememberMeToken getTokenForSeries(String seriesId) {
String key = generateKey(seriesId);
List<String> hashKeys = new ArrayList<>();
hashKeys.add("username");
hashKeys.add("tokenValue");
hashKeys.add("date");
List<String> hashValues = redisTemplate.opsForHash().multiGet(key, hashKeys);
String username = hashValues.get(0);
String tokenValue = hashValues.get(1);
String date = hashValues.get(2);
if (null == username || null == tokenValue || null == date) {
return null;
}
Long timestamp = Long.valueOf(date);
Date time = new Date(timestamp);
PersistentRememberMeToken token = new PersistentRememberMeToken(username, seriesId, tokenValue, time);
return token;
}
@Override
public void removeUserTokens(String username) {
if (log.isDebugEnabled()) {
log.debug("token remove username: [{}]", username);
}
byte[] hashKey = redisTemplate.getHashKeySerializer().serialize("username");
RedisConnection redisConnection = redisTemplate.getConnectionFactory().getConnection();
try (Cursor<byte[]> 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 = (String) redisTemplate.getHashValueSerializer().deserialize(hashValue);
if (username.equals(storeName)) {
redisConnection.expire(key, 0L);
return;
}
}
} catch (IOException ex) {
log.warn("token remove exception", ex);
}
}
/**
* 生成key
*
* @param series
* @return
*/
private String generateKey(String series) {
return "spring:security:rememberMe:token:" + series;
}
}
验证
- 登录,保存cookie
curl -H "content-type:application/x-www-form-urlencoded" -X POST -c cookie.txt -d "username=yuyanjia&password=yuyanjiademima&rememberMe" http://127.0.0.1:8080/security-demo/authc/login
- image.png
- 清除掉redis中缓存的session信息,触发授权验证
- image.png
- 请求一个需要授权才能访问的页面做验证
curl -X POST -b cookie.txt http://127.0.0.1:8080/security-demo/authc/watch
网友评论