Springboot集成Shiro和Redis实现session共享

裴楚青
2023-12-01
  1. 首先pom引入shiro,redis包
<parent>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-starter-parent</artifactId>
   <version>2.1.3.RELEASE</version>
   <relativePath/> <!-- lookup parent from repository -->
</parent>
<dependency>
    <groupId>org.apache.shiro</groupId>
    <artifactId>shiro-spring</artifactId>
    <version>1.4.0</version>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
    <version>2.1.3.RELEASE</version>
</dependency>
  1. 继承AbstractSessionDAO类,实现Redis存储session
@Component
public class RedisSessionDao extends AbstractSessionDAO {

	private Logger logger = LoggerFactory.getLogger(RedisSessionDao.class);
	private long expireTime = 1200000;

	@Autowired
	private RedisTemplate redisTemplate;

	public RedisSessionDao() {
		super();
	}


	public RedisSessionDao(long expireTime, RedisTemplate redisTemplate) {
		super();
		this.expireTime = expireTime;
		this.redisTemplate = redisTemplate;
	}

	@Override 
	public void update(Session session) throws UnknownSessionException {
		if (session == null || session.getId() == null) {
			return;
		}
		session.setTimeout(expireTime);
		redisTemplate.opsForValue().set(session.getId(), session, expireTime,
				TimeUnit.MILLISECONDS);
	}

	@Override 
	public void delete(Session session) {
		if (null != session) {
			redisTemplate.opsForValue().getOperations().delete(session.getId());
		}
	}

	@Override // 获取活跃的session,可以用来统计在线人数,如果要实现这个功能,可以在将session加入redis时指定一个session前缀,统计的时候则使用keys("session-prefix*")的方式来模糊查找redis中所有的session集合
	public Collection<Session> getActiveSessions() {
		return redisTemplate.keys("*");
	}

	@Override // 加入session
	protected Serializable doCreate(Session session) {
		Serializable sessionId = this.generateSessionId(session);
		this.assignSessionId(session, sessionId);
		redisTemplate.opsForValue().set(session.getId(), session, expireTime,
				TimeUnit.MILLISECONDS);
		return sessionId;
	}

	@Override // 读取session
	protected Session doReadSession(Serializable sessionId) {
		if (sessionId == null) {
			logger.info("重新读取session中的sessionId----" + sessionId);
			return null;
		}
		return (Session) redisTemplate.opsForValue().get(sessionId);
	}

	public long getExpireTime() {
		return expireTime;
	}

	public void setExpireTime(long expireTime) {
		this.expireTime = expireTime;
	}

	public RedisTemplate getRedisTemplate() {
		return redisTemplate;
	}

	public void setRedisTemplate(RedisTemplate redisTemplate) {
		this.redisTemplate = redisTemplate;
	}
}

3.Shiro配置类

@Configuration
@ComponentScan({"com.****.www.*"})
public class ShiroConfiguration {

	@Autowired
	private RedisSessionDao redisSessionDao;
	@Autowired
	private MyShiroRealm myShiroRealm;
	@Autowired
	private ShiroSessionListener shiroSessionListener;
	@Bean
	public ShiroFilterFactoryBean shirFilter() {
		ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
		shiroFilterFactoryBean.setSecurityManager(securityManager());
		Map<String, String> filterChainDefinitionMap = new LinkedHashMap<String, String>();
		filterChainDefinitionMap.put("/logined/**", "authc");
		filterChainDefinitionMap.put("/actuator/**", "authc");
		shiroFilterFactoryBean.setLoginUrl("/");
		shiroFilterFactoryBean.setUnauthorizedUrl("/");
		shiroFilterFactoryBean
				.setFilterChainDefinitionMap(filterChainDefinitionMap);
		return shiroFilterFactoryBean;
	}

	/**
	 * @return
	 */
	@Bean
	public HashedCredentialsMatcher hashedCredentialsMatcher() {
		HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher();
		hashedCredentialsMatcher.setHashAlgorithmName("md5");
		hashedCredentialsMatcher.setHashIterations(1);
		return hashedCredentialsMatcher;
	}

	@Bean
	public SecurityManager securityManager() {
		myShiroRealm.setCredentialsMatcher(hashedCredentialsMatcher());
		DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
		securityManager.setRealm(myShiroRealm);
		securityManager.setSessionManager(defaultWebSessionManager());
		return securityManager;
	}

	@Bean
	public DefaultWebSessionManager defaultWebSessionManager() {
		MyWebSessionManager manager = new MyWebSessionManager();
		// manager.setCacheManager(cacheManager);
		Collection<SessionListener>  sessionListeners=new ArrayList<SessionListener>();
		sessionListeners.add(shiroSessionListener);
		manager.setSessionListeners(sessionListeners);
		redisSessionDao.setExpireTime(2400000);
		manager.setSessionDAO(redisSessionDao);
		manager.setSessionValidationInterval(10000);
		manager.setDeleteInvalidSessions(true);
		manager.setGlobalSessionTimeout(redisSessionDao.getExpireTime());
		manager.setSessionValidationSchedulerEnabled(true);
		Cookie cookie = manager.getSessionIdCookie();
		cookie.setName("mycookiename");
		return manager;
	}
	/**
	 * 
	 * 
	 * @param securityManager
	 * @return
	 */
	@Bean
	public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(
			SecurityManager securityManager) {
		AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
		authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
		return authorizationAttributeSourceAdvisor;
	}

	@Bean
	public ShiroDialect shiroDialect() {
		return new ShiroDialect();
	}

	class MyWebSessionManager extends DefaultWebSessionManager{
		public void validateSessions() {
			Collection<Session> activeSessions = getActiveSessions();
			if (activeSessions != null && !activeSessions.isEmpty()) {
				for (Session s : activeSessions) {
					try {
						SessionKey key = new DefaultSessionKey(s.getId());
						validate(s, key);
					} catch (InvalidSessionException e) {
						e.printStackTrace();
					}
				}
			}
		}
	}
}
  1. 实现SessionListener接口
@Log4j2
@Component
public class ShiroSessionListener implements SessionListener{

	private final AtomicInteger allSession=new AtomicInteger(1);

	@Override
	public void onStart(Session session) {
		allSession.incrementAndGet();
	}

	@Override
	public void onStop(Session session) {
		allSession.decrementAndGet();
	}

	@Override
	public void onExpiration(Session session) {
		allSession.decrementAndGet();
	}

	public int getAllSession(){
		return allSession.intValue();
	}
}

 5. redis配置文件
spring:
	redis:
	    lettuce:
	      pool:
	        max-active: 200
	        min-idle: 5
	        max-idle: 5
	        max-wait: 5000
	        maxWaitMillis: 5000
	    redis-a:
	      database: 1
	      hostname: ip地址
	      port: 6379
	      timeout: 5000
  1. redis配置类
@Configuration
public class StandaloneRedisConfig {

	@Bean
	@ConfigurationProperties(prefix = "spring.redis.lettuce.pool")
	@Scope(value = "prototype")
	public GenericObjectPoolConfig redisPool() {
		return new GenericObjectPoolConfig();
	}

	@Bean
	@ConfigurationProperties(prefix = "spring.redis.redis-a")
	public RedisStandaloneConfiguration redisConfigA() {
		return new RedisStandaloneConfiguration();
	}

	@Bean
	@Primary
	public LettuceConnectionFactory factoryA(GenericObjectPoolConfig config,
			RedisStandaloneConfiguration redisConfigA) {
		LettuceClientConfiguration clientConfiguration = LettucePoolingClientConfiguration
				.builder().poolConfig(config)
				.commandTimeout(Duration.ofMillis(config.getMaxWaitMillis()))
				.build();
		return new LettuceConnectionFactory(redisConfigA, clientConfiguration);
	}

	@Bean
	public StringRedisTemplate redisTemplateA(
			LettuceConnectionFactory factoryA) {
		StringRedisTemplate template = getRedisTemplate();
		template.setConnectionFactory(factoryA);
		return template;
	}

	private StringRedisTemplate getRedisTemplate() {
		StringRedisTemplate template = new StringRedisTemplate();
		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);
		objectMapper.setSerializationInclusion(JsonInclude.Include.NON_EMPTY);
		jackson2JsonRedisSerializer.setObjectMapper(objectMapper);
		template.setValueSerializer(jackson2JsonRedisSerializer);
		template.setValueSerializer(jackson2JsonRedisSerializer);
		return template;
	}
}
 类似资料: