shiro中出现不同请求session不同的现象
Shiro提供了三个默认实现:
DefaultSessionManager:DefaultSecurityManager使用的默认实现,用于JavaSE环境;
ServletContainerSessionManager:DefaultWebSecurityManager使用的默认实现,用于Web环境,其直接使用Servlet容器的会话;
DefaultWebSessionManager:用于Web环境的实现,可以替代ServletContainerSessionManager,自己维护着会话,直接废弃了Servlet容器的会话管理。
遇到的坑:
在web环境下用ini文件配置shiro时,如果不指定SecurityManager时, shiro会默认创建DefaultSecurityManager对象,这样会导致在web环境下,发不同的请求生成的session不同,导致登录功能失效。 因为DefaultSessionManager在源码中是和本地线程绑定的,而web环境中一个请求会创建一个线程,从而导致session都不同。
当使用DefaultWebSessionManager时,shiro中的session和web中的session是一致的。 而DefaultSessionManager的session和web中的session是不一致的。
完整解决办法
ShiroConfiguration.java
import com.fire.shiro.AuthRealm;
import com.fire.shiro.RedisManager;
import com.fire.shiro.RedisSessionDAO;
import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.spring.LifecycleBeanPostProcessor;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.apache.shiro.web.servlet.SimpleCookie;
import org.apache.shiro.web.session.mgt.DefaultWebSessionManager;
import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.DependsOn;
@Slf4j
public abstract class ShiroConfiguration {
abstract ShiroFilterFactoryBean shiroFilter(SecurityManager manager);
// 配置自定义的权限登录器
@Bean
public AuthRealm authRealm() {
AuthRealm authRealm = new AuthRealm();
return authRealm;
}
// 配置核心安全事务管理器
@Bean
@ConditionalOnMissingBean
public SecurityManager securityManager(AuthRealm authRealm, RedisSessionDAO redisSessionDAO) { //, EhCacheManager ehcacheManager
DefaultWebSecurityManager manager = new DefaultWebSecurityManager();
manager.setRealm(authRealm);
// 缓存授权
//manager.setCacheManager(ehcacheManager);
DefaultWebSessionManager sessionManager = new DefaultWebSessionManager();
// sessionManager.setGlobalSessionTimeout(7200000);
sessionManager.setSessionDAO(redisSessionDAO);
sessionManager.setSessionIdCookie(new SimpleCookie("ShiroSession"));
manager.setSessionManager(sessionManager);
return manager;
}
@Bean
public RedisSessionDAO redisSessionDAO(RedisManager redisManager) {
RedisSessionDAO redisSessionDAO = new RedisSessionDAO();
redisSessionDAO.setRedisManager(redisManager);
// redisSessionDAO.setRedisTemplate(redisTemplate);
return redisSessionDAO;
}
@Bean
public RedisManager redisManager(@Value("${session.redis.host}") final String redisHost,
@Value("${session.redis.port}") final int redisPort,
@Value("${session.redis.password}") final String redisPassword) {
log.info("redisHost: {}, redisPort: {}, redisPassword: {}", redisHost, redisPort, redisPassword);
RedisManager redisManager = new RedisManager();
redisManager.setHost(redisHost);
redisManager.setPort(redisPort);
redisManager.setPassword(redisPassword);
redisManager.setExpire(1800);
return redisManager;
}
// 注解使用需要下面三个bean
@Bean
public LifecycleBeanPostProcessor lifecycleBeanPostProcessor() {
return new LifecycleBeanPostProcessor();
}
@Bean
@DependsOn("lifecycleBeanPostProcessor")
public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator() {
DefaultAdvisorAutoProxyCreator creator = new DefaultAdvisorAutoProxyCreator();
creator.setProxyTargetClass(true);
return creator;
}
@Bean
public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager manager) {
AuthorizationAttributeSourceAdvisor advisor = new AuthorizationAttributeSourceAdvisor();
advisor.setSecurityManager(manager);
return advisor;
}
}
ProdShiroConfiguration.java
import com.fire.shiro.AuthRealm;
import org.apache.shiro.cache.ehcache.EhCacheManager;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Profile;
import javax.servlet.Filter;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;
@Configuration
@Profile({"sandbox", "prod"})
public class ProdShiroConfiguration extends ShiroConfiguration {
@Bean
public ShiroFilterFactoryBean shiroFilter(SecurityManager manager) {
ShiroFilterFactoryBean bean = new ShiroFilterFactoryBean();
bean.setSecurityManager(manager);
// 配置登录的url和登录成功的url
bean.setLoginUrl("/login");
bean.setUnauthorizedUrl("/illegal");
Map<String, Filter> filters = new HashMap<>();
bean.setFilters(filters);
// 配置访问权限
LinkedHashMap<String, String> filterChainDefinitionMap = new LinkedHashMap<>();
filterChainDefinitionMap.put("/", "anon");
filterChainDefinitionMap.put("/nonuser", "anon");
filterChainDefinitionMap.put("/**", "authc");
bean.setFilterChainDefinitionMap(filterChainDefinitionMap);
return bean;
}
// 配置自定义的权限登录器
@Bean
public AuthRealm authRealm() {
AuthRealm authRealm = new AuthRealm();
return authRealm;
}
@Bean
public EhCacheManager ehCacheCacheManager() {
EhCacheManager ehCacheCacheManager = new EhCacheManager();
return ehCacheCacheManager;
}
@Bean
@ConditionalOnMissingBean
public EhCacheManager shiroCacheManager(EhCacheManager ehCacheCacheManager) {
EhCacheManager shiroCacheManager = new EhCacheManager();
shiroCacheManager.setCacheManager(ehCacheCacheManager.getCacheManager());
return shiroCacheManager;
}
}
RedisSessionDAO.java
import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.session.Session;
import org.apache.shiro.session.UnknownSessionException;
import org.apache.shiro.session.mgt.eis.AbstractSessionDAO;
import java.io.Serializable;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
@Slf4j
public class RedisSessionDAO extends AbstractSessionDAO {
private RedisManager redisManager;
/**
* The Redis key prefix for the sessions
*/
private String keyPrefix = "firehuo:";
@Override
public void update(Session session) throws UnknownSessionException {
this.saveSession(session);
}
/**
* save session
*
* @throws UnknownSessionException
*/
private void saveSession(Session session) throws UnknownSessionException {
if (session == null || session.getId() == null) {
log.error("session or session id is null");
return;
}
byte[] key = getByteKey(session.getId());
byte[] value = SerializeUtils.serialize(session);
session.setTimeout(redisManager.getExpire() * 1000);
this.redisManager.set(key, value, redisManager.getExpire());
}
@Override
public void delete(Session session) {
if (session == null || session.getId() == null) {
log.error("session or session id is null");
return;
}
redisManager.del(this.getByteKey(session.getId()));
}
@Override
public Collection<Session> getActiveSessions() {
log.info("getActiveSessions session start");
Set<Session> sessions = new HashSet<>();
try {
List<String> keys = redisManager.scan(this.keyPrefix + "*");
if (keys != null && keys.size() > 0) {
for (String key : keys) {
Session s = (Session) SerializeUtils.deserialize(redisManager.get(key.getBytes()));
sessions.add(s);
}
}
log.info("getActiveSessions session end");
} catch (Exception e) {
log.error("getActiveSessions is error", e);
}
return sessions;
}
@Override
protected Serializable doCreate(Session session) {
Serializable sessionId = this.generateSessionId(session);
this.assignSessionId(session, sessionId);
this.saveSession(session);
return sessionId;
}
@Override
protected Session doReadSession(Serializable sessionId) {
if (sessionId == null) {
log.error("session id is null");
return null;
}
Session s = (Session) SerializeUtils.deserialize(redisManager.get(this.getByteKey(sessionId)));
return s;
}
/**
* 获得byte[]型的key
*/
private byte[] getByteKey(Serializable sessionId) {
String preKey = this.keyPrefix + sessionId;
return preKey.getBytes();
}
public void setRedisManager(RedisManager redisManager) {
this.redisManager = redisManager;
//初始化redisManager
this.redisManager.init();
}
}