JWT和传统的Cookie/Session会话管理相比较有着多方面的优势,因为Cookie/Session需要在服务器Session存用户信息,然后拿客户端Cookie存储的SessionId获取用户信息,这个过程需要消耗服务器的内存和对客户端的要求比较严格(需支持Cookie),而JWT最大的特性在于就是无状态、去中心化,所以JWT更适用分布式的场景,不需要在多台服务器做会话同步这种消耗服务器性能的操作。

另外JWT和Redis+Token这两种会话管理小伙伴们看项目情况选择,别有用了JWT还使用Redis存储的,因为你这种做法对JWT来说就是“伤害不大,但侮辱性极强”的做法,就当着它的面说我就看不上你的最自以为是的“无状态”特性。

方法: Redis存Session,客户使用cookie或者JWT

总之,客户需要携带登录状态,在redis进行认证,就可以访问其他模块了

基于 Redis 实现的单点登录

spring security 学习笔记 加入认证 redis 缓存(四)

Spring Security - @PreAuthorize 安全表达式 hasRole、hasAuthority 区别

Spring Secruity存入session进入Redis

依赖引入:

<dependency>  
<groupId>org.springframework.boot</groupId>  
<artifactId>spring-boot-starter-data-redis</artifactId>  
</dependency>  
<dependency>  
<groupId>org.springframework.boot</groupId>  
<artifactId>spring-boot-starter-security</artifactId>  
</dependency>  
  
<dependency>  
<groupId>org.springframework.session</groupId>  
<artifactId>spring-session-data-redis</artifactId>  
</dependency>

配置Redis链接

 
package com.hl.travel.config;  
  
  
@Configuration  
public class RedisConfig extends CachingConfigurerSupport {  
  
protected static final Logger logger = Logger.getLogger(RedisConfig.class);  
  
@Value("${spring.redis.host}")  
private String host;  
  
@Value("${spring.redis.port}")  
private int port;  
  
@Value("${spring.redis.jedis.pool.max-active}")  
private int maxTotal;  
  
@Value("${spring.redis.jedis.pool.max-idle}")  
private int maxIdle;  
  
@Value("${spring.redis.jedis.pool.min-idle}")  
private int minIdle;  
  
@Value("${spring.redis.password}")  
private String password;  
  
@Value("${spring.redis.timeout}")  
private int timeout;  
  
public JedisPool redisPoolFactory() {  
JedisPoolConfig jedisPoolConfig = new JedisPoolConfig();  
jedisPoolConfig.setMaxTotal(maxTotal);  
jedisPoolConfig.setMaxIdle(maxIdle);  
jedisPoolConfig.setMinIdle(minIdle);  
JedisPool jedisPool = new JedisPool(jedisPoolConfig, host, port, timeout, null);  
logger.info("JedisPool注入成功!!");  
logger.info("redis地址:" + host + ":" + port);  
return jedisPool;  
}  
  
  
  
  
  
// 自定义缓存key生成策略  
@Override  
@Bean  
public KeyGenerator keyGenerator() {  
return new KeyGenerator() {  
@Override  
public Object generate(Object target, java.lang.reflect.Method method, Object... params) {  
StringBuffer sb = new StringBuffer();  
sb.append(target.getClass().getName());  
sb.append(method.getName());  
for (Object obj : params) {  
sb.append(obj.toString());  
}  
return sb.toString();  
}  
};  
}  
  
// 缓存管理器  
@Bean  
public CacheManager cacheManager(RedisConnectionFactory redisConnectionFactory) {  
RedisSerializer<String> redisSerializer = new StringRedisSerializer();  
Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);  
  
//反序列化问题  
ObjectMapper om = new ObjectMapper();  
om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);  
om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);  
jackson2JsonRedisSerializer.setObjectMapper(om);  
  
// 解决存储乱码问题  
RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig()  
// 缓存过期时间1hours 以application.yml配置文件中的优先级更高  
.entryTtl(Duration.ofHours(1))  
.serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(redisSerializer))  
.serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(jackson2JsonRedisSerializer))  
.disableCachingNullValues();  
  
RedisCacheManager cacheManager = RedisCacheManager.builder(redisConnectionFactory)  
.cacheDefaults(config)  
.build();  
return cacheManager;  
}  
  
@Bean  
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {  
  
RedisTemplate<String, Object> template = new RedisTemplate<>();  
  
RedisSerializer<String> redisSerializer = new StringRedisSerializer();  
  
Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);  
ObjectMapper om = new ObjectMapper();  
om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);  
om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);  
jackson2JsonRedisSerializer.setObjectMapper(om);  
  
template.setConnectionFactory(factory);  
//key序列化方式  
template.setKeySerializer(redisSerializer);  
//value序列化  
template.setValueSerializer(jackson2JsonRedisSerializer);  
//value hashmap序列化  
template.setHashValueSerializer(jackson2JsonRedisSerializer);  
  
return template;  
}  
}
 

Spring Session 和redis整合

package com.hl.travel.config;  
  
  
@Configuration  
@EnableRedisHttpSession  
public class RedisSessionConfig {  
  
@Value("${spring.redis.host}")  
private String host;  
  
@Value("${spring.redis.port}")  
private int port;  
  
@Value("${spring.redis.jedis.pool.max-active}")  
private int maxTotal;  
  
@Value("${spring.redis.jedis.pool.max-idle}")  
private int maxIdle;  
  
@Value("${spring.redis.jedis.pool.min-idle}")  
private int minIdle;  
  
@Value("${spring.redis.password}")  
private String password;  
  
@Value("${spring.redis.timeout}")  
private int timeout;  
  
@Bean  
public static ConfigureRedisAction configureRedisAction(){  
return ConfigureRedisAction.NO_OP;  
}  
  
  
}
 

Spring security注册到Spring sesion

Java配置类中

 
@Override  
protected void configure(HttpSecurity http) throws Exception {  
// super.configure(http);  
http
.sessionManagement()  
.sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED)  
.invalidSessionUrl(MessageConstant.LOGIN_SUCCESS_URL+"/login.html")//session失效后跳转的url  
.maximumSessions(1)//同一账号同时登录最大用户数  
.maxSessionsPreventsLogin(false) // 允许新会话替换旧会话  
.sessionRegistry(sessionRegistry());// 使用Redis存储会话信息  
}
 

本文件下配置Bean注入

 
  
/* 解决session失效后 sessionRegistry中session没有同步失效的问题,启用并发session控制,首先需要在配置中增加下面监听器  
* @return  
*/  
@Bean  
public HttpSessionEventPublisher httpSessionEventPublisher() {  
return new HttpSessionEventPublisher();  
}  
  
/**  
* 注册bean sessionRegistry  
*/  
@Bean  
public SessionRegistry sessionRegistry() {  
return new SessionRegistryImpl();  
}