博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
在SpringBoot1.5.x下如何使RedisTokenStore集群化
阅读量:2168 次
发布时间:2019-05-01

本文共 12924 字,大约阅读时间需要 43 分钟。

在SpringBoot1.5.x下如何使RedisTokenStore集群化

在 spring boot 1.5.x 下 spring-boot-starter-data-redis 默认使用 jedis 作为客户端。

因为 JedisCluster 不支持集群的管道操作(pipleline),但是项目中又要用到 Redis 集群,这时候该怎么办呢?

现在,提供两种解决办法:

  1. 重写 RedisTokenStore, 用 RedisTemplateTokenStore
  2. 将 jedis 换掉,使用 spring boot 2.x 中默认的 redis 客户端 lettuce 来支持 Redis 集群(推荐)

解决办法 1:重写 RedisTokenStore

因为 JedisCluster 不支持管道操作:(源码)

public class JedisClusterConnection implements RedisClusterConnection {
/* * (non-Javadoc) * @see org.springframework.data.redis.connection.RedisConnection#openPipeline() */ @Override public void openPipeline() {
throw new UnsupportedOperationException("Pipeline is currently not supported for JedisClusterConnection."); } /* * (non-Javadoc) * @see org.springframework.data.redis.connection.RedisConnection#closePipeline() */ @Override public List closePipeline() throws RedisPipelineException {
throw new UnsupportedOperationException("Pipeline is currently not supported for JedisClusterConnection."); }}

因此可以使用 RedisTemplate 重写 RedisTokenStore,虽然会导致性能的损失,但至少能用不是吗

package com.fengxuechao.examples.auth.provider.token.store;import org.springframework.data.redis.core.RedisTemplate;import org.springframework.security.oauth2.common.OAuth2AccessToken;import org.springframework.security.oauth2.common.OAuth2RefreshToken;import org.springframework.security.oauth2.provider.OAuth2Authentication;import org.springframework.security.oauth2.provider.token.AuthenticationKeyGenerator;import org.springframework.security.oauth2.provider.token.DefaultAuthenticationKeyGenerator;import org.springframework.security.oauth2.provider.token.TokenStore;import java.util.*;import java.util.concurrent.TimeUnit;/** * @author fengxuechao * @version 0.1 * @date 2019/6/21 */public class RedisTemplateTokenStore implements TokenStore {
private static final String ACCESS = "access:"; private static final String AUTH_TO_ACCESS = "auth_to_access:"; private static final String AUTH = "auth:"; private static final String REFRESH_AUTH = "refresh_auth:"; private static final String ACCESS_TO_REFRESH = "access_to_refresh:"; private static final String REFRESH = "refresh:"; private static final String REFRESH_TO_ACCESS = "refresh_to_access:"; private static final String CLIENT_ID_TO_ACCESS = "client_id_to_access:"; private static final String UNAME_TO_ACCESS = "uname_to_access:"; private RedisTemplate
redisTemplate ; public RedisTemplate
getRedisTemplate() {
return redisTemplate; } public void setRedisTemplate(RedisTemplate
redisTemplate) {
this.redisTemplate = redisTemplate; } private AuthenticationKeyGenerator authenticationKeyGenerator = new DefaultAuthenticationKeyGenerator(); public void setAuthenticationKeyGenerator(AuthenticationKeyGenerator authenticationKeyGenerator) {
this.authenticationKeyGenerator = authenticationKeyGenerator; } @Override public OAuth2AccessToken getAccessToken(OAuth2Authentication authentication) {
String key = authenticationKeyGenerator.extractKey(authentication); OAuth2AccessToken accessToken = (OAuth2AccessToken) redisTemplate.opsForValue().get(AUTH_TO_ACCESS+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 (OAuth2Authentication) this.redisTemplate.opsForValue().get(AUTH + token); } @Override public OAuth2Authentication readAuthenticationForRefreshToken(OAuth2RefreshToken token) {
return readAuthenticationForRefreshToken(token.getValue()); } public OAuth2Authentication readAuthenticationForRefreshToken(String token) {
return (OAuth2Authentication) this.redisTemplate.opsForValue().get( REFRESH_AUTH+token); } @Override public void storeAccessToken(OAuth2AccessToken token, OAuth2Authentication authentication) {
this.redisTemplate.opsForValue().set(ACCESS+ token.getValue(), token); this.redisTemplate.opsForValue().set(AUTH +token.getValue(), authentication); this.redisTemplate.opsForValue().set(AUTH_TO_ACCESS+authenticationKeyGenerator.extractKey(authentication), token); if (!authentication.isClientOnly()) {
redisTemplate.opsForList().rightPush(UNAME_TO_ACCESS+getApprovalKey(authentication), token) ; } redisTemplate.opsForList().rightPush(CLIENT_ID_TO_ACCESS+authentication.getOAuth2Request().getClientId(), token) ; if (token.getExpiration() != null) {
int seconds = token.getExpiresIn(); redisTemplate.expire(ACCESS+ token.getValue(), seconds, TimeUnit.SECONDS) ; redisTemplate.expire(AUTH+ token.getValue(), seconds, TimeUnit.SECONDS) ; redisTemplate.expire(AUTH_TO_ACCESS+ authenticationKeyGenerator.extractKey(authentication), seconds, TimeUnit.SECONDS) ; redisTemplate.expire(CLIENT_ID_TO_ACCESS+authentication.getOAuth2Request().getClientId(), seconds, TimeUnit.SECONDS) ; redisTemplate.expire(UNAME_TO_ACCESS+ getApprovalKey(authentication), seconds, TimeUnit.SECONDS) ; } if (token.getRefreshToken() != null && token.getRefreshToken().getValue() != null) {
this.redisTemplate.opsForValue().set( REFRESH_TO_ACCESS+ token.getRefreshToken().getValue(), token.getValue()); this.redisTemplate.opsForValue().set(ACCESS_TO_REFRESH+token.getValue(), token.getRefreshToken().getValue()); } } private String getApprovalKey(OAuth2Authentication authentication) {
String userName = authentication.getUserAuthentication() == null ? "" : authentication.getUserAuthentication() .getName(); return getApprovalKey(authentication.getOAuth2Request().getClientId(), userName); } private 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 (OAuth2AccessToken) this.redisTemplate.opsForValue().get(ACCESS+tokenValue); } public void removeAccessToken(String tokenValue) {
OAuth2AccessToken removed = (OAuth2AccessToken) redisTemplate.opsForValue().get(ACCESS+tokenValue); // Don't remove the refresh token - it's up to the caller to do that OAuth2Authentication authentication = (OAuth2Authentication) this.redisTemplate.opsForValue().get(AUTH+tokenValue); this.redisTemplate.delete(AUTH+tokenValue); redisTemplate.delete(ACCESS+tokenValue); this.redisTemplate.delete(ACCESS_TO_REFRESH +tokenValue); if (authentication != null) {
this.redisTemplate.delete(AUTH_TO_ACCESS+authenticationKeyGenerator.extractKey(authentication)); String clientId = authentication.getOAuth2Request().getClientId();// redisTemplate.opsForList().rightPush("UNAME_TO_ACCESS:"+getApprovalKey(authentication), token) ; redisTemplate.opsForList().leftPop(UNAME_TO_ACCESS+getApprovalKey(clientId, authentication.getName())); redisTemplate.opsForList().leftPop(CLIENT_ID_TO_ACCESS+clientId); this.redisTemplate.delete(AUTH_TO_ACCESS+authenticationKeyGenerator.extractKey(authentication)); } } @Override public void storeRefreshToken(OAuth2RefreshToken refreshToken, OAuth2Authentication authentication) {
this.redisTemplate.opsForValue().set(REFRESH+refreshToken.getValue(), refreshToken); this.redisTemplate.opsForValue().set( REFRESH_AUTH + refreshToken.getValue(), authentication); } @Override public OAuth2RefreshToken readRefreshToken(String tokenValue) {
return (OAuth2RefreshToken) this.redisTemplate.opsForValue().get(REFRESH+tokenValue); } @Override public void removeRefreshToken(OAuth2RefreshToken refreshToken) {
removeRefreshToken(refreshToken.getValue()); } public void removeRefreshToken(String tokenValue) {
this.redisTemplate.delete( REFRESH + tokenValue); this.redisTemplate.delete( REFRESH_AUTH + tokenValue); this.redisTemplate.delete(REFRESH_TO_ACCESS +tokenValue); } @Override public void removeAccessTokenUsingRefreshToken(OAuth2RefreshToken refreshToken) {
removeAccessTokenUsingRefreshToken(refreshToken.getValue()); } private void removeAccessTokenUsingRefreshToken(String refreshToken) {
String token = (String) this.redisTemplate.opsForValue().get( REFRESH_TO_ACCESS +refreshToken) ; if (token != null) {
redisTemplate.delete(ACCESS+ token); } } @Override public Collection
findTokensByClientIdAndUserName(String clientId, String userName) {
List
result = redisTemplate.opsForList().range(UNAME_TO_ACCESS+ getApprovalKey(clientId, userName), 0, -1); if (result == null || result.size() == 0) {
return Collections.
emptySet(); } List
accessTokens = new ArrayList
(result.size()); for(Iterator
it = result.iterator(); it.hasNext();){ OAuth2AccessToken accessToken = (OAuth2AccessToken) it.next(); accessTokens.add(accessToken); } return Collections.
unmodifiableCollection(accessTokens); } @Override public Collection
findTokensByClientId(String clientId) { List
result = redisTemplate.opsForList().range((CLIENT_ID_TO_ACCESS+clientId), 0, -1); if (result == null || result.size() == 0) { return Collections.
emptySet(); } List
accessTokens = new ArrayList
(result.size()); for(Iterator
it = result.iterator();it.hasNext();){ OAuth2AccessToken accessToken = (OAuth2AccessToken) it.next(); accessTokens.add(accessToken); } return Collections.
unmodifiableCollection(accessTokens); }}

解决办法 2:使用 lettuce 替换 jedis

我们可以使用 Lettuce 来替代 jedis,况且 lettuce 也是 spring boot 2.x 中默认的 redis 客户端。

POM

org.springframework.boot
spring-boot-starter-data-redis
jedis
redis.clients
io.lettuce
lettuce-core
5.0.5.RELEASE
compile
biz.paluch.redis
lettuce
4.5.0.Final
org.apache.commons
commons-pool2

配置文件 application.yml

spring:  redis:    cluster:      nodes: 192.168.213.13:7001,192.168.213.14:7003,192.168.213.21:7006      max-redirects: 5logging:  level:    root: info    com.fengxuechao.examples.auth: debug

配置 LettuceConnectionFactory 和 RedisTokenStore

package com.fengxuechao.examples.auth.config;import org.springframework.boot.autoconfigure.data.redis.RedisProperties;import org.springframework.boot.context.properties.EnableConfigurationProperties;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;import org.springframework.data.redis.connection.RedisClusterConfiguration;import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;/** * Redis 配置 * * @author fengxuechao * @version 0.1 * @date 2019/6/24 */@EnableConfigurationProperties(RedisProperties.class)@Configurationpublic class RedisConfig {
/** * 使用 lettuce 作为 redis 的连接池 * * @param configuration * @return */ @Bean public LettuceConnectionFactory lettuceConnectionFactory(RedisClusterConfiguration configuration) {
return new LettuceConnectionFactory(configuration); } /** * lettuce 集群配置 */ @Bean public RedisClusterConfiguration getClusterConfiguration(RedisProperties redisProperties) {
RedisProperties.Cluster clusterProperties = redisProperties.getCluster(); RedisClusterConfiguration config = new RedisClusterConfiguration(clusterProperties.getNodes()); if (clusterProperties.getMaxRedirects() != null) {
config.setMaxRedirects(clusterProperties.getMaxRedirects()); } return config; } @Bean public TokenStore tokenStore(LettuceConnectionFactory lettuceConnectionFactory) {
return new RedisTokenStore(lettuceConnectionFactory); }}

转载地址:http://rtxzb.baihongyu.com/

你可能感兴趣的文章
C语言字符、字符串操作偏僻函数总结
查看>>
Git的Patch功能
查看>>
分析C语言的声明
查看>>
TCP为什么是三次握手,为什么不是两次或者四次 && TCP四次挥手
查看>>
C结构体、C++结构体、C++类的区别
查看>>
进程和线程的概念、区别和联系
查看>>
CMake 入门实战
查看>>
绑定CPU逻辑核心的利器——taskset
查看>>
Linux下perf性能测试火焰图只显示函数地址不显示函数名的问题
查看>>
c结构体、c++结构体和c++类的区别以及错误纠正
查看>>
Linux下查看根目录各文件内存占用情况
查看>>
A星算法详解(个人认为最详细,最通俗易懂的一个版本)
查看>>
利用栈实现DFS
查看>>
逆序对的数量(递归+归并思想)
查看>>
数的范围(二分查找上下界)
查看>>
算法导论阅读顺序
查看>>
Windows程序设计:直线绘制
查看>>
linux之CentOS下文件解压方式
查看>>
Django字段的创建并连接MYSQL
查看>>
div标签布局的使用
查看>>