心路历程
首先使用Oauth2.0实现浏览器登陆注册——>Oauth2.0实现第三方登陆——>Oauth2.0实现App登陆注册——>Oauth2.0的Token redis单例存储——>Oauth2.0的Token redis集群
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
</dependency>
刚开始引入下面的依赖,结果只能实现JedisCluster形式
然而引入RedisTokenSotore需要引入RedisConnectionFactory,故换成spring-data-redis依赖
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-redis</artifactId>
<version>2.1.6.RELEASE</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
</dependency>
然后报 Pipeline is currently not supported for JedisClusterConnection
说明spirng-data-redis不支持集群模式.
左思右想如果redis挂了,那存储的token那不是都丢失了么
先把单机模式的token实现再说
然后从网上搜索发现:lettuceConnectionFactory比redisConnectionFactory的好处多多
Lettuce 和 jedis 的都是连接 Redis Server的客户端,Jedis 在实现上是直连 redis server,
多线程环境下非线程安全,除非使用连接池,为每个 redis实例增加 物理连接。
Lettuce 是 一种可伸缩,线程安全,完全非阻塞的Redis客户端,多个线程可以共享一个RedisConnection,
它利用Netty NIO 框架来高效地管理多个连接,从而提供了异步和同步数据访问方式,用于构建非阻塞的反应性应用程序
看完lettuce的好处,又说spring2.0默认都是lettuce实现的,心中就燃气烈火,但是这就是巨坑的开始
<dependency>
<groupId>io.lettuce</groupId>
<artifactId>lettuce-core</artifactId>
</dependency>
引入依赖后发现,项目必须是spinrg2.0以上版本才能支持,继续
<dependencyManagement>
<!--替我们管理maven依赖版本-->
<dependencies>
<dependency>
<groupId>io.spring.platform</groupId>
<artifactId>platform-bom</artifactId>
<version>Cairo-SR7</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Finchley.SR2</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-security</artifactId>
<exclusions>
<!--旧版本 redis操作有问题 注意此处解决旧版本问题-->
<exclusion>
<artifactId>spring-security-oauth2</artifactId>
<groupId>org.springframework.security.oauth</groupId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework.security.oauth</groupId>
<artifactId>spring-security-oauth2</artifactId>
<version>2.3.3.RELEASE</version>
</dependency>
@Bean
@ConfigurationProperties(prefix="redis.cluster")
public RedisClusterConfiguration redisClusterConfiguration(){
RedisClusterConfiguration redisClusterConfiguration = new RedisClusterConfiguration();
ArrayList<RedisNode> list = new ArrayList<>();
list.add(new RedisNode("www.weeklog.com",7000));
list.add(new RedisNode("www.weeklog.com",7001));
list.add(new RedisNode("www.weeklog.com",7002));
list.add(new RedisNode("www.weeklog.com",7003));
list.add(new RedisNode("www.weeklog.com",7004));
list.add(new RedisNode("www.weeklog.com",7005));
redisClusterConfiguration.setClusterNodes(list);
return redisClusterConfiguration;
}
/**
* 连接池配置
* @return
*/
@Bean
/**
* 表示从配置文件读取以前缀开头的信息,注入到GenericObjectPoolConfig中
*/
@ConfigurationProperties(prefix="generic.pool.config")
public GenericObjectPoolConfig genericObjectPoolConfig() {
GenericObjectPoolConfig poolConfig = new GenericObjectPoolConfig();
return poolConfig;
}
/**
* @Primary 解决重复实现类
* 加入连接池配置信息
* lettuce客户端配置信息(如果不用连接池通过LettuceClientConfiguration来builder)
*/
@Bean(name = "lettuceClientConfiguration")
public LettuceClientConfiguration lettuceClientConfiguration(GenericObjectPoolConfig genericObjectPoolConfig){
LettucePoolingClientConfiguration build = LettucePoolingClientConfiguration
.builder().poolConfig(genericObjectPoolConfig)
.build();
return build;
}
/**
*
* @param redisClusterConfiguration redis集群配置
* @param lettuceClientConfiguration Lettuce客户端配置
* @return
*/
@Bean
public LettuceConnectionFactory lettuceConnectionFactory(RedisClusterConfiguration redisClusterConfiguration, LettuceClientConfiguration lettuceClientConfiguration){
LettuceConnectionFactory lettuceConnectionFactory = new LettuceConnectionFactory(redisClusterConfiguration,lettuceClientConfiguration);
return lettuceConnectionFactory;
}
@Bean
public RedisTemplate<String, Object> redisCacheTemplate(LettuceConnectionFactory lettuceConnectionFactory) {
RedisTemplate<String, Object> template = new RedisTemplate<>();
Jackson2JsonRedisSerializer<Object> jackson2JsonRedisSerializer = new
Jackson2JsonRedisSerializer<Object>(Object.class);
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
objectMapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
jackson2JsonRedisSerializer.setObjectMapper(objectMapper);
template.setKeySerializer(new StringRedisSerializer());
template.setValueSerializer(jackson2JsonRedisSerializer);
template.setHashKeySerializer(new StringRedisSerializer());
template.setHashValueSerializer(jackson2JsonRedisSerializer);
template.setConnectionFactory(lettuceConnectionFactory);
return template;
}
@Autowired
private RedisTemplate<String, Object> redisTemplate;
public void setRedisTemplate(RedisTemplate<String, Object> redisTemplate) {
this.redisTemplate = redisTemplate;
}
//=============================common============================
/**
* 指定缓存失效时间
* @param key 键
* @param time 时间(秒)
* @return
*/
public boolean expire(String key,long time){
try {
if(time>0){
redisTemplate.expire(key, time, TimeUnit.SECONDS);
}
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 根据key 获取过期时间
* @param key 键 不能为null
* @return 时间(秒) 返回0代表为永久有效
*/
public long getExpire(String key){
return redisTemplate.getExpire(key,TimeUnit.SECONDS);
}
/**
* 判断key是否存在
* @param key 键
* @return true 存在 false不存在
*/
public boolean hasKey(String key){
try {
return redisTemplate.hasKey(key);
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 删除缓存
* @param key 可以传一个值 或多个
*/
@SuppressWarnings("unchecked")
public void del(String ... key){
if(key!=null&&key.length>0){
if(key.length==1){
redisTemplate.delete(key[0]);
}else{
redisTemplate.delete(CollectionUtils.arrayToList(key));
}
}
}
//============================String=============================
/**
* 普通缓存获取
* @param key 键
* @return 值
*/
public Object get(String key){
return key==null?null:redisTemplate.opsForValue().get(key);
}
/**
* 普通缓存放入
* @param key 键
* @param value 值
* @return true成功 false失败
*/
public boolean set(String key,Object value) {
try {
redisTemplate.opsForValue().set(key, value);
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 普通缓存放入并设置时间
* @param key 键
* @param value 值
* @param time 时间(秒) time要大于0 如果time小于等于0 将设置无限期
* @return true成功 false 失败
*/
public boolean set(String key,Object value,long time){
try {
if(time>0){
redisTemplate.opsForValue().set(key, value, time, TimeUnit.SECONDS);
}else{
set(key, value);
}
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 递增
* @param key 键
* @return
*/
public long incr(String key, long delta){
if(delta<0){
throw new RuntimeException("递增因子必须大于0");
}
return redisTemplate.opsForValue().increment(key, delta);
}
/**
* 递减
* @param key 键
* @return
*/
public long decr(String key, long delta){
if(delta<0){
throw new RuntimeException("递减因子必须大于0");
}
return redisTemplate.opsForValue().increment(key, -delta);
}
//================================Map=================================
/**
* HashGet
* @param key 键 不能为null
* @param item 项 不能为null
* @return 值
*/
public Object hget(String key,String item){
return redisTemplate.opsForHash().get(key, item);
}
/**
* 获取hashKey对应的所有键值
* @param key 键
* @return 对应的多个键值
*/
public Map<Object,Object> hmget(String key){
return redisTemplate.opsForHash().entries(key);
}
/**
* HashSet
* @param key 键
* @param map 对应多个键值
* @return true 成功 false 失败
*/
public boolean hmset(String key, Map<String,Object> map){
try {
redisTemplate.opsForHash().putAll(key, map);
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* HashSet 并设置时间
* @param key 键
* @param map 对应多个键值
* @param time 时间(秒)
* @return true成功 false失败
*/
public boolean hmset(String key, Map<String,Object> map, long time){
try {
redisTemplate.opsForHash().putAll(key, map);
if(time>0){
expire(key, time);
}
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 向一张hash表中放入数据,如果不存在将创建
* @param key 键
* @param item 项
* @param value 值
* @return true 成功 false失败
*/
public boolean hset(String key,String item,Object value) {
try {
redisTemplate.opsForHash().put(key, item, value);
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 向一张hash表中放入数据,如果不存在将创建
* @param key 键
* @param item 项
* @param value 值
* @param time 时间(秒) 注意:如果已存在的hash表有时间,这里将会替换原有的时间
* @return true 成功 false失败
*/
public boolean hset(String key,String item,Object value,long time) {
try {
redisTemplate.opsForHash().put(key, item, value);
if(time>0){
expire(key, time);
}
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 删除hash表中的值
* @param key 键 不能为null
* @param item 项 可以使多个 不能为null
*/
public void hdel(String key, Object... item){
redisTemplate.opsForHash().delete(key,item);
}
/**
* 判断hash表中是否有该项的值
* @param key 键 不能为null
* @param item 项 不能为null
* @return true 存在 false不存在
*/
public boolean hHasKey(String key, String item){
return redisTemplate.opsForHash().hasKey(key, item);
}
/**
* hash递增 如果不存在,就会创建一个 并把新增后的值返回
* @param key 键
* @param item 项
* @param by 要增加几(大于0)
* @return
*/
public double hincr(String key, String item,double by){
return redisTemplate.opsForHash().increment(key, item, by);
}
/**
* hash递减
* @param key 键
* @param item 项
* @param by 要减少记(小于0)
* @return
*/
public double hdecr(String key, String item,double by){
return redisTemplate.opsForHash().increment(key, item,-by);
}
//============================set=============================
/**
* 根据key获取Set中的所有值
* @param key 键
* @return
*/
public Set<Object> sGet(String key){
try {
return redisTemplate.opsForSet().members(key);
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
/**
* 根据value从一个set中查询,是否存在
* @param key 键
* @param value 值
* @return true 存在 false不存在
*/
public boolean sHasKey(String key,Object value){
try {
return redisTemplate.opsForSet().isMember(key, value);
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 将数据放入set缓存
* @param key 键
* @param values 值 可以是多个
* @return 成功个数
*/
public long sSet(String key, Object...values) {
try {
return redisTemplate.opsForSet().add(key, values);
} catch (Exception e) {
e.printStackTrace();
return 0;
}
}
/**
* 将set数据放入缓存
* @param key 键
* @param time 时间(秒)
* @param values 值 可以是多个
* @return 成功个数
*/
public long sSetAndTime(String key,long time,Object...values) {
try {
Long count = redisTemplate.opsForSet().add(key, values);
if(time>0) expire(key, time);
return count;
} catch (Exception e) {
e.printStackTrace();
return 0;
}
}
/**
* 获取set缓存的长度
* @param key 键
* @return
*/
public long sGetSetSize(String key){
try {
return redisTemplate.opsForSet().size(key);
} catch (Exception e) {
e.printStackTrace();
return 0;
}
}
/**
* 移除值为value的
* @param key 键
* @param values 值 可以是多个
* @return 移除的个数
*/
public long setRemove(String key, Object ...values) {
try {
Long count = redisTemplate.opsForSet().remove(key, values);
return count;
} catch (Exception e) {
e.printStackTrace();
return 0;
}
}
//===============================list=================================
/**
* 获取list缓存的内容
* @param key 键
* @param start 开始
* @param end 结束 0 到 -1代表所有值
* @return
*/
public List<Object> lGet(String key, long start, long end){
try {
return redisTemplate.opsForList().range(key, start, end);
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
/**
* 获取list缓存的长度
* @param key 键
* @return
*/
public long lGetListSize(String key){
try {
return redisTemplate.opsForList().size(key);
} catch (Exception e) {
e.printStackTrace();
return 0;
}
}
/**
* 通过索引 获取list中的值
* @param key 键
* @param index 索引 index>=0时, 0 表头,1 第二个元素,依次类推;index<0时,-1,表尾,-2倒数第二个元素,依次类推
* @return
*/
public Object lGetIndex(String key,long index){
try {
return redisTemplate.opsForList().index(key, index);
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
/**
* 将list放入缓存
* @param key 键
* @param value 值
* @return
*/
public boolean lSet(String key, Object value) {
try {
redisTemplate.opsForList().rightPush(key, value);
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 将list放入缓存
* @param key 键
* @param value 值
* @param time 时间(秒)
* @return
*/
public boolean lSet(String key, Object value, long time) {
try {
redisTemplate.opsForList().rightPush(key, value);
if (time > 0) expire(key, time);
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 将list放入缓存
* @param key 键
* @param value 值
* @return
*/
public boolean lSet(String key, List<Object> value) {
try {
redisTemplate.opsForList().rightPushAll(key, value);
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 将list放入缓存
* @param key 键
* @param value 值
* @param time 时间(秒)
* @return
*/
public boolean lSet(String key, List<Object> value, long time) {
try {
redisTemplate.opsForList().rightPushAll(key, value);
if (time > 0) expire(key, time);
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 根据索引修改list中的某条数据
* @param key 键
* @param index 索引
* @param value 值
* @return
*/
public boolean lUpdateIndex(String key, long index,Object value) {
try {
redisTemplate.opsForList().set(key, index, value);
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 移除N个值为value
* @param key 键
* @param count 移除多少个
* @param value 值
* @return 移除的个数
*/
public long lRemove(String key,long count,Object value) {
try {
Long remove = redisTemplate.opsForList().remove(key, count, value);
return remove;
} catch (Exception e) {
e.printStackTrace();
return 0;
}
}
分析:
/**
* 配置认证组件
* @param endpoints
* @throws Exception
*/
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
endpoints.userDetailsService(userDetailsService)
.authenticationManager(authenticationManager)
.tokenStore(this.tokenStore());
}
解决问题:spring-data-redis 2.0版本中set(String,String)被弃用
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 final RedisConnectionFactory connectionFactory;
private AuthenticationKeyGenerator authenticationKeyGenerator = new DefaultAuthenticationKeyGenerator();
private RedisTokenStoreSerializationStrategy serializationStrategy = new JdkSerializationStrategy();
private String prefix = "";
public MyRdisTokenStore(RedisConnectionFactory connectionFactory) {
this.connectionFactory = connectionFactory;
}
public void setAuthenticationKeyGenerator(AuthenticationKeyGenerator authenticationKeyGenerator) {
this.authenticationKeyGenerator = authenticationKeyGenerator;
}
public void setSerializationStrategy(RedisTokenStoreSerializationStrategy serializationStrategy) {
this.serializationStrategy = serializationStrategy;
}
public void setPrefix(String prefix) {
this.prefix = prefix;
}
private RedisConnection getConnection() {
return this.connectionFactory.getConnection();
}
private byte[] serialize(Object object) {
return this.serializationStrategy.serialize(object);
}
private byte[] serializeKey(String object) {
return this.serialize(this.prefix + object);
}
private OAuth2AccessToken deserializeAccessToken(byte[] bytes) {
return (OAuth2AccessToken)this.serializationStrategy.deserialize(bytes, OAuth2AccessToken.class);
}
private OAuth2Authentication deserializeAuthentication(byte[] bytes) {
return (OAuth2Authentication)this.serializationStrategy.deserialize(bytes, OAuth2Authentication.class);
}
private OAuth2RefreshToken deserializeRefreshToken(byte[] bytes) {
return (OAuth2RefreshToken)this.serializationStrategy.deserialize(bytes, OAuth2RefreshToken.class);
}
private byte[] serialize(String string) {
return this.serializationStrategy.serialize(string);
}
private String deserializeString(byte[] bytes) {
return this.serializationStrategy.deserializeString(bytes);
}
public OAuth2AccessToken getAccessToken(OAuth2Authentication authentication) {
String key = this.authenticationKeyGenerator.extractKey(authentication);
byte[] serializedKey = this.serializeKey("auth_to_access:" + key);
byte[] bytes = null;
RedisConnection conn = this.getConnection();
try {
bytes = conn.get(serializedKey);
} finally {
conn.close();
}
OAuth2AccessToken accessToken = this.deserializeAccessToken(bytes);
if (accessToken != null) {
OAuth2Authentication storedAuthentication = this.readAuthentication(accessToken.getValue());
if (storedAuthentication == null || !key.equals(this.authenticationKeyGenerator.extractKey(storedAuthentication))) {
this.storeAccessToken(accessToken, authentication);
}
}
return accessToken;
}
public OAuth2Authentication readAuthentication(OAuth2AccessToken token) {
return this.readAuthentication(token.getValue());
}
public OAuth2Authentication readAuthentication(String token) {
byte[] bytes = null;
RedisConnection conn = this.getConnection();
try {
bytes = conn.get(this.serializeKey("auth:" + token));
} finally {
conn.close();
}
OAuth2Authentication var4 = this.deserializeAuthentication(bytes);
return var4;
}
public OAuth2Authentication readAuthenticationForRefreshToken(OAuth2RefreshToken token) {
return this.readAuthenticationForRefreshToken(token.getValue());
}
public OAuth2Authentication readAuthenticationForRefreshToken(String token) {
RedisConnection conn = this.getConnection();
OAuth2Authentication var5;
try {
byte[] bytes = conn.get(this.serializeKey("refresh_auth:" + token));
OAuth2Authentication auth = this.deserializeAuthentication(bytes);
var5 = auth;
} finally {
conn.close();
}
return var5;
}
public void storeAccessToken(OAuth2AccessToken token, OAuth2Authentication authentication) {
byte[] serializedAccessToken = this.serialize(token);
byte[] serializedAuth = this.serialize(authentication);
byte[] accessKey = this.serializeKey("access:" + token.getValue());
byte[] authKey = this.serializeKey("auth:" + token.getValue());
byte[] authToAccessKey = this.serializeKey("auth_to_access:" + this.authenticationKeyGenerator.extractKey(authentication));
byte[] approvalKey = this.serializeKey("uname_to_access:" + getApprovalKey(authentication));
byte[] clientId = this.serializeKey("client_id_to_access:" + authentication.getOAuth2Request().getClientId());
RedisConnection conn = this.getConnection();
try {
conn.openPipeline();
// conn.set(accessKey, serializedAccessToken);
// conn.set(authKey, serializedAuth);
// conn.set(authToAccessKey, serializedAccessToken);
conn.stringCommands().set(accessKey, serializedAccessToken);
conn.stringCommands().set(authKey, serializedAuth);
conn.stringCommands().set(authToAccessKey, serializedAccessToken);
if (!authentication.isClientOnly()) {
conn.rPush(approvalKey, new byte[][]{serializedAccessToken});
}
conn.rPush(clientId, new byte[][]{serializedAccessToken});
if (token.getExpiration() != null) {
int seconds = token.getExpiresIn();
conn.expire(accessKey, (long)seconds);
conn.expire(authKey, (long)seconds);
conn.expire(authToAccessKey, (long)seconds);
conn.expire(clientId, (long)seconds);
conn.expire(approvalKey, (long)seconds);
}
OAuth2RefreshToken refreshToken = token.getRefreshToken();
if (refreshToken != null && refreshToken.getValue() != null) {
byte[] refresh = this.serialize(token.getRefreshToken().getValue());
byte[] auth = this.serialize(token.getValue());
byte[] refreshToAccessKey = this.serializeKey("refresh_to_access:" + token.getRefreshToken().getValue());
// conn.set(refreshToAccessKey, auth);
conn.stringCommands().set(refreshToAccessKey, auth);
byte[] accessToRefreshKey = this.serializeKey("access_to_refresh:" + token.getValue());
// conn.set(accessToRefreshKey, refresh);
conn.stringCommands().set(accessToRefreshKey, refresh);
if (refreshToken instanceof ExpiringOAuth2RefreshToken) {
ExpiringOAuth2RefreshToken expiringRefreshToken = (ExpiringOAuth2RefreshToken)refreshToken;
Date expiration = expiringRefreshToken.getExpiration();
if (expiration != null) {
int seconds = Long.valueOf((expiration.getTime() - System.currentTimeMillis()) / 1000L).intValue();
conn.expire(refreshToAccessKey, (long)seconds);
conn.expire(accessToRefreshKey, (long)seconds);
}
}
}
conn.closePipeline();
} finally {
conn.close();
}
}
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);
}
public void removeAccessToken(OAuth2AccessToken accessToken) {
this.removeAccessToken(accessToken.getValue());
}
public OAuth2AccessToken readAccessToken(String tokenValue) {
byte[] key = this.serializeKey("access:" + tokenValue);
byte[] bytes = null;
RedisConnection conn = this.getConnection();
try {
bytes = conn.get(key);
} finally {
conn.close();
}
OAuth2AccessToken var5 = this.deserializeAccessToken(bytes);
return var5;
}
public void removeAccessToken(String tokenValue) {
byte[] accessKey = this.serializeKey("access:" + tokenValue);
byte[] authKey = this.serializeKey("auth:" + tokenValue);
byte[] accessToRefreshKey = this.serializeKey("access_to_refresh:" + tokenValue);
RedisConnection conn = this.getConnection();
try {
conn.openPipeline();
conn.get(accessKey);
conn.get(authKey);
conn.del(new byte[][]{accessKey});
conn.del(new byte[][]{accessToRefreshKey});
conn.del(new byte[][]{authKey});
List<Object> results = conn.closePipeline();
byte[] access = (byte[])((byte[])results.get(0));
byte[] auth = (byte[])((byte[])results.get(1));
OAuth2Authentication authentication = this.deserializeAuthentication(auth);
if (authentication != null) {
String key = this.authenticationKeyGenerator.extractKey(authentication);
byte[] authToAccessKey = this.serializeKey("auth_to_access:" + key);
byte[] unameKey = this.serializeKey("uname_to_access:" + getApprovalKey(authentication));
byte[] clientId = this.serializeKey("client_id_to_access:" + authentication.getOAuth2Request().getClientId());
conn.openPipeline();
conn.del(new byte[][]{authToAccessKey});
conn.lRem(unameKey, 1L, access);
conn.lRem(clientId, 1L, access);
conn.del(new byte[][]{this.serialize("access:" + key)});
conn.closePipeline();
}
} finally {
conn.close();
}
}
public void storeRefreshToken(OAuth2RefreshToken refreshToken, OAuth2Authentication authentication) {
byte[] refreshKey = this.serializeKey("refresh:" + refreshToken.getValue());
byte[] refreshAuthKey = this.serializeKey("refresh_auth:" + refreshToken.getValue());
byte[] serializedRefreshToken = this.serialize((Object)refreshToken);
RedisConnection conn = this.getConnection();
try {
conn.openPipeline();
// conn.set(refreshKey, serializedRefreshToken);
// conn.set(refreshAuthKey, this.serialize((Object)authentication));
conn.stringCommands().set(refreshKey, serializedRefreshToken);
conn.stringCommands().set(refreshAuthKey, serialize(authentication));
if (refreshToken instanceof ExpiringOAuth2RefreshToken) {
ExpiringOAuth2RefreshToken expiringRefreshToken = (ExpiringOAuth2RefreshToken)refreshToken;
Date expiration = expiringRefreshToken.getExpiration();
if (expiration != null) {
int seconds = Long.valueOf((expiration.getTime() - System.currentTimeMillis()) / 1000L).intValue();
conn.expire(refreshKey, (long)seconds);
conn.expire(refreshAuthKey, (long)seconds);
}
}
conn.closePipeline();
} finally {
conn.close();
}
}
public OAuth2RefreshToken readRefreshToken(String tokenValue) {
byte[] key = this.serializeKey("refresh:" + tokenValue);
byte[] bytes = null;
RedisConnection conn = this.getConnection();
try {
bytes = conn.get(key);
} finally {
conn.close();
}
OAuth2RefreshToken var5 = this.deserializeRefreshToken(bytes);
return var5;
}
public void removeRefreshToken(OAuth2RefreshToken refreshToken) {
this.removeRefreshToken(refreshToken.getValue());
}
public void removeRefreshToken(String tokenValue) {
byte[] refreshKey = this.serializeKey("refresh:" + tokenValue);
byte[] refreshAuthKey = this.serializeKey("refresh_auth:" + tokenValue);
byte[] refresh2AccessKey = this.serializeKey("refresh_to_access:" + tokenValue);
byte[] access2RefreshKey = this.serializeKey("access_to_refresh:" + tokenValue);
RedisConnection conn = this.getConnection();
try {
conn.openPipeline();
conn.del(new byte[][]{refreshKey});
conn.del(new byte[][]{refreshAuthKey});
conn.del(new byte[][]{refresh2AccessKey});
conn.del(new byte[][]{access2RefreshKey});
conn.closePipeline();
} finally {
conn.close();
}
}
public void removeAccessTokenUsingRefreshToken(OAuth2RefreshToken refreshToken) {
this.removeAccessTokenUsingRefreshToken(refreshToken.getValue());
}
private void removeAccessTokenUsingRefreshToken(String refreshToken) {
byte[] key = this.serializeKey("refresh_to_access:" + refreshToken);
List<Object> results = null;
RedisConnection conn = this.getConnection();
try {
conn.openPipeline();
conn.get(key);
conn.del(new byte[][]{key});
results = conn.closePipeline();
} finally {
conn.close();
}
if (results != null) {
byte[] bytes = (byte[])((byte[])results.get(0));
String accessToken = this.deserializeString(bytes);
if (accessToken != null) {
this.removeAccessToken(accessToken);
}
}
}
public Collection<OAuth2AccessToken> findTokensByClientIdAndUserName(String clientId, String userName) {
byte[] approvalKey = this.serializeKey("uname_to_access:" + getApprovalKey(clientId, userName));
List<byte[]> byteList = null;
RedisConnection conn = this.getConnection();
try {
byteList = conn.lRange(approvalKey, 0L, -1L);
} finally {
conn.close();
}
if (byteList != null && byteList.size() != 0) {
List<OAuth2AccessToken> accessTokens = new ArrayList(byteList.size());
Iterator var7 = byteList.iterator();
while(var7.hasNext()) {
byte[] bytes = (byte[])var7.next();
OAuth2AccessToken accessToken = this.deserializeAccessToken(bytes);
accessTokens.add(accessToken);
}
return Collections.unmodifiableCollection(accessTokens);
} else {
return Collections.emptySet();
}
}
public Collection<OAuth2AccessToken> findTokensByClientId(String clientId) {
byte[] key = this.serializeKey("client_id_to_access:" + clientId);
List<byte[]> byteList = null;
RedisConnection conn = this.getConnection();
try {
byteList = conn.lRange(key, 0L, -1L);
} finally {
conn.close();
}
if (byteList != null && byteList.size() != 0) {
List<OAuth2AccessToken> accessTokens = new ArrayList(byteList.size());
Iterator var6 = byteList.iterator();
while(var6.hasNext()) {
byte[] bytes = (byte[])var6.next();
OAuth2AccessToken accessToken = this.deserializeAccessToken(bytes);
accessTokens.add(accessToken);
}
return Collections.unmodifiableCollection(accessTokens);
} else {
return Collections.emptySet();
}
}
WebSecurityConfigurerAdapter 优先级低于 ResourceServerConfigurerAdapter
但项目中两者都有用的必要
把WebSecurityConfigurerAdapter当作springsecurity的默认配置
public class AdminAuthenticationProvide extends DaoAuthenticationProvider{
// 换一种注入方式
// @Autowired
// UserDetailsService userDetailsService;
PasswordEncoder passwordEncoder= new MyBCryptPasswordEncoder();
/**
* 注入两个参数
*/
public AdminAuthenticationProvide(UserDetailsService userDetailsService){
this.setUserDetailsService(userDetailsService);
this.setPasswordEncoder(passwordEncoder);
}
protected void additionalAuthenticationChecks(UserDetails userDetails,
UsernamePasswordAuthenticationToken authentication)
throws AuthenticationException {
if (authentication.getCredentials() == null) {
logger.debug("Authentication failed: no credentials provided");
throw new BadCredentialsException(messages.getMessage(
"AbstractUserDetailsAuthenticationProvider.badCredentials",
"Bad credentials"));
}
String authPassword = authentication.getCredentials().toString();
PasswordEncoder passwordEncoder = PasswordEncoderFactories.createDelegatingPasswordEncoder();
// String presentedPassword =passwordEncoder.encode(authPassword);
if (!passwordEncoder.matches(authPassword,userDetails.getPassword())) { //都是编码后得参数
logger.debug("Authentication failed: password does not match stored value");
throw new BadCredentialsException(messages.getMessage(
"AbstractUserDetailsAuthenticationProvider.badCredentials",
"Bad credentials"));
}
}
}
解决问题:通过继承DaoAuthenticationProvider的方式,自定义密码匹配逻辑,使用PasswordEncoderFactories创建DelegatingPasswordEncoder授权密码编码器)
DelegatingPasswordEncoder并不是单一的密码编码器:它可以适配多种编码器,默认使用Bcrypt
PasswordEncoder passwordEncoder= new MyBCryptPasswordEncoder();
/**
* 注入两个参数
* 注意此处是巨坑:导入userDetailsService,否则报错
*/
public AdminAuthenticationProvide(UserDetailsService userDetailsService){
this.setUserDetailsService(userDetailsService);
this.setPasswordEncoder(passwordEncoder);
}
protected void additionalAuthenticationChecks(UserDetails userDetails,
UsernamePasswordAuthenticationToken authentication)
throws AuthenticationException {
if (authentication.getCredentials() == null) {
logger.debug("Authentication failed: no credentials provided");
throw new BadCredentialsException(messages.getMessage(
"AbstractUserDetailsAuthenticationProvider.badCredentials",
"Bad credentials"));
}
String authPassword = authentication.getCredentials().toString();
PasswordEncoder passwordEncoder = PasswordEncoderFactories.createDelegatingPasswordEncoder();
// String presentedPassword =passwordEncoder.encode(authPassword);
if (!passwordEncoder.matches(authPassword,userDetails.getPassword())) { //都是编码后得参数
logger.debug("Authentication failed: password does not match stored value");
throw new BadCredentialsException(messages.getMessage(
"AbstractUserDetailsAuthenticationProvider.badCredentials",
"Bad credentials"));
}
}
解决问题:统一使用DelegatingPasswordEncoder进行编码和匹配
private static final long serialVersionUID = 8840367235617058418L;
@Override
public boolean matches(CharSequence rawPassword, String encodedPassword) {
PasswordEncoder passwordEncoder = PasswordEncoderFactories.createDelegatingPasswordEncoder();
return passwordEncoder.matches(rawPassword, encodedPassword);
}
@Override
public String encode(CharSequence rawPassword) {
PasswordEncoder passwordEncoder = PasswordEncoderFactories.createDelegatingPasswordEncoder();
return passwordEncoder.encode(rawPassword);
}
报错Caused by: java.io.NotSerializableException: MyBCryptPasswordEncoder
解决:实现Serializable