在项目中我们可以使用redis进行shiro session的持久化和集群共享
但在实际项目中,用户进行一次登录操作,日志如下:
2018-04-25 14:30:20.075 ahut [http-nio-8080-exec-1] INFO com.ahut.shiro.RedisShiroSessionDao - 创建session:b0029f80-dbc5-40e6-8cbe-0dddaad69eb3
2018-04-25 14:30:20.075 ahut [http-nio-8080-exec-1] INFO com.ahut.shiro.RedisShiroCache - 保存信息到缓存中:b0029f80-dbc5-40e6-8cbe-0dddaad69eb3
2018-04-25 14:30:20.076 ahut [http-nio-8080-exec-1] INFO com.ahut.shiro.RedisShiroSessionDao - 更新session:b0029f80-dbc5-40e6-8cbe-0dddaad69eb3
2018-04-25 14:30:20.076 ahut [http-nio-8080-exec-1] INFO com.ahut.shiro.RedisShiroCache - 保存信息到缓存中:b0029f80-dbc5-40e6-8cbe-0dddaad69eb3
2018-04-25 14:30:20.077 ahut [http-nio-8080-exec-1] INFO com.ahut.shiro.RedisShiroCache - 从缓存中获取数据:b0029f80-dbc5-40e6-8cbe-0dddaad69eb3
2018-04-25 14:30:20.078 ahut [http-nio-8080-exec-1] INFO com.ahut.shiro.RedisShiroSessionDao - 更新session:b0029f80-dbc5-40e6-8cbe-0dddaad69eb3
2018-04-25 14:30:20.079 ahut [http-nio-8080-exec-1] INFO com.ahut.shiro.RedisShiroCache - 保存信息到缓存中:b0029f80-dbc5-40e6-8cbe-0dddaad69eb3
2018-04-25 14:30:20.083 ahut [http-nio-8080-exec-1] INFO com.ahut.shiro.RedisShiroCache - 从缓存中获取数据:b0029f80-dbc5-40e6-8cbe-0dddaad69eb3
2018-04-25 14:30:20.084 ahut [http-nio-8080-exec-1] INFO com.ahut.shiro.RedisShiroCache - 从缓存中获取数据:b0029f80-dbc5-40e6-8cbe-0dddaad69eb3
2018-04-25 14:30:20.084 ahut [http-nio-8080-exec-1] INFO com.ahut.shiro.RedisShiroCache - 从缓存中获取数据:b0029f80-dbc5-40e6-8cbe-0dddaad69eb3
2018-04-25 14:30:20.085 ahut [http-nio-8080-exec-1] INFO com.ahut.shiro.RedisShiroCache - 从缓存中获取数据:b0029f80-dbc5-40e6-8cbe-0dddaad69eb3
2018-04-25 14:30:20.085 ahut [http-nio-8080-exec-1] INFO com.ahut.shiro.RedisShiroCache - 从缓存中获取数据:b0029f80-dbc5-40e6-8cbe-0dddaad69eb3
2018-04-25 14:30:20.086 ahut [http-nio-8080-exec-1] INFO com.ahut.shiro.RedisShiroSessionDao - 更新session:b0029f80-dbc5-40e6-8cbe-0dddaad69eb3
2018-04-25 14:30:20.086 ahut [http-nio-8080-exec-1] INFO com.ahut.shiro.RedisShiroCache - 保存信息到缓存中:b0029f80-dbc5-40e6-8cbe-0dddaad69eb3
2018-04-25 14:30:20.087 ahut [http-nio-8080-exec-1] INFO com.ahut.shiro.RedisShiroCache - 从缓存中获取数据:b0029f80-dbc5-40e6-8cbe-0dddaad69eb3
2018-04-25 14:30:20.087 ahut [http-nio-8080-exec-1] INFO com.ahut.shiro.RedisShiroCache - 从缓存中获取数据:b0029f80-dbc5-40e6-8cbe-0dddaad69eb3
2018-04-25 14:30:20.088 ahut [http-nio-8080-exec-1] INFO com.ahut.shiro.RedisShiroSessionDao - 更新session:b0029f80-dbc5-40e6-8cbe-0dddaad69eb3
2018-04-25 14:30:20.089 ahut [http-nio-8080-exec-1] INFO com.ahut.shiro.RedisShiroCache - 保存信息到缓存中:b0029f80-dbc5-40e6-8cbe-0dddaad69eb3
2018-04-25 14:30:20.091 ahut [http-nio-8080-exec-1] INFO com.ahut.shiro.RedisShiroCache - 从缓存中获取数据:b0029f80-dbc5-40e6-8cbe-0dddaad69eb3
2018-04-25 14:30:20.092 ahut [http-nio-8080-exec-1] INFO com.ahut.shiro.RedisShiroCache - 从缓存中获取数据:b0029f80-dbc5-40e6-8cbe-0dddaad69eb3
由于 Session 都持久化在 redis 中,并且我们使用了redis作为缓存服务器,导致 shiro 在请求处理中需要用到 session 的时候都要从 redis 中取数据并且反序列化。
这样的一次简单访问,访问redis的次数过多,查看DefaultSessionManager源码:
// 获取session
protected Session retrieveSession(SessionKey sessionKey) throws UnknownSessionException {
Serializable sessionId = getSessionId(sessionKey);
if (sessionId == null) {
log.debug("Unable to resolve session ID from SessionKey [{}]. Returning null to indicate a " +
"session could not be found.", sessionKey);
return null;
}
// 获取session
Session s = retrieveSessionFromDataSource(sessionId);
if (s == null) {
//session ID was provided, meaning one is expected to be found, but we couldn't find one:
String msg = "Could not find session with ID [" + sessionId + "]";
throw new UnknownSessionException(msg);
}
return s;
}
// 调用sessionDao,获取session
protected Session retrieveSessionFromDataSource(Serializable sessionId) throws UnknownSessionException {
return sessionDAO.readSession(sessionId);
}
基本步骤:
分析:
突破点:
解决方案:
自定义SessionManager,重写retrieveSession方法,改变获取session的方法:
package com.ahut.shiro;
import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.session.Session;
import org.apache.shiro.session.UnknownSessionException;
import org.apache.shiro.session.mgt.SessionKey;
import org.apache.shiro.web.session.mgt.DefaultWebSessionManager;
import org.apache.shiro.web.session.mgt.WebSessionKey;
import javax.servlet.ServletRequest;
import java.io.Serializable;
/**
* @author cheng
* @className: RedisShiroSessionManager
* @description: 自定义SessionManager
* @dateTime 2018/4/25 14:41
*/
@Slf4j
public class RedisShiroSessionManager extends DefaultWebSessionManager {
/**
* @description: 获取session, 优化单次请求需要多次访问redis的问题
* @author cheng
* @dateTime 2018/4/25 14:43
*/
@Override
protected Session retrieveSession(SessionKey sessionKey) throws UnknownSessionException {
// 获取sessionId
Serializable sessionId = getSessionId(sessionKey);
ServletRequest request = null;
// 在 Web 下使用 shiro 时这个 sessionKey 是 WebSessionKey 类型的
// 若是在web下使用,则获取request
if (sessionKey instanceof WebSessionKey) {
request = ((WebSessionKey) sessionKey).getServletRequest();
}
// 尝试从request中获取session
if (request != null && sessionId != null) {
Object sessionObj = request.getAttribute(sessionId.toString());
if (sessionObj != null) {
log.info("从request获取到session:{}", sessionId);
return (Session) sessionObj;
}
}
// 若从request中获取session失败,则从redis中获取session,并把获取到的session存储到request中方便下次获取
Session session = super.retrieveSession(sessionKey);
if (request != null && sessionId != null) {
log.info("存储session到request中:{}", sessionId);
request.setAttribute(sessionId.toString(), session);
}
return session;
}
}
在ShiroConfig.java中配置自定义的SessionManager
/**
* @description: 自定义sessionManager
* @author cheng
* @dateTime 2018/4/24 10:37
*/
public SessionManager createMySessionManager() {
RedisShiroSessionManager sessionManager = new RedisShiroSessionManager();
// 自定义sessionDao
sessionManager.setSessionDAO(createRedisShiroSessionDao());
// session的失效时长,单位是毫秒
sessionManager.setGlobalSessionTimeout(ShiroUtil.GLOBAL_SESSION_TIMEOUT);
// 删除失效的session
sessionManager.setDeleteInvalidSessions(true);
// 所有的session一定要将id设置到Cookie之中,需要提供有Cookie的操作模版
sessionManager.setSessionIdCookie(createSessionIdCookie());
// 定义sessionIdCookie模版可以进行操作的启用
sessionManager.setSessionIdCookieEnabled(true);
log.info("配置sessionManager");
return sessionManager;
}
/**
* @description: 注意方法返回值SecurityManager为org.apache.shiro.mgt.SecurityManager, 不要导错包
* @author cheng
* @dateTime 2018/4/18 15:48
*/
public SecurityManager createSecurityManager() {
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
// 自定义realm
securityManager.setRealm(createMyRealm());
// 自定义sessionManager
securityManager.setSessionManager(createMySessionManager());
// 自定义rememberMeManager
securityManager.setRememberMeManager(createRememberMeManager());
// 自定义cacheManager
securityManager.setCacheManager(createCacheManager());
log.info("配置rsecurityManager");
return securityManager;
}
优化后的日志访问记录
2018-04-25 14:56:16.843 ahut [http-nio-8080-exec-1] INFO com.ahut.shiro.RedisShiroSessionDao - 创建session:8475e279-8733-45d5-8fb7-08a371c4cd91
2018-04-25 14:56:16.843 ahut [http-nio-8080-exec-1] INFO com.ahut.shiro.RedisShiroCache - 保存信息到缓存中:8475e279-8733-45d5-8fb7-08a371c4cd91
2018-04-25 14:56:16.843 ahut [http-nio-8080-exec-1] INFO com.ahut.shiro.RedisShiroSessionDao - 更新session:8475e279-8733-45d5-8fb7-08a371c4cd91
2018-04-25 14:56:16.844 ahut [http-nio-8080-exec-1] INFO com.ahut.shiro.RedisShiroCache - 保存信息到缓存中:8475e279-8733-45d5-8fb7-08a371c4cd91
2018-04-25 14:56:16.845 ahut [http-nio-8080-exec-1] INFO com.ahut.shiro.RedisShiroCache - 从缓存中获取数据:8475e279-8733-45d5-8fb7-08a371c4cd91
2018-04-25 14:56:16.846 ahut [http-nio-8080-exec-1] INFO c.a.shiro.RedisShiroSessionManager - 存储session到request中:8475e279-8733-45d5-8fb7-08a371c4cd91
2018-04-25 14:56:16.846 ahut [http-nio-8080-exec-1] INFO com.ahut.shiro.RedisShiroSessionDao - 更新session:8475e279-8733-45d5-8fb7-08a371c4cd91
2018-04-25 14:56:16.847 ahut [http-nio-8080-exec-1] INFO com.ahut.shiro.RedisShiroCache - 保存信息到缓存中:8475e279-8733-45d5-8fb7-08a371c4cd91
2018-04-25 14:56:16.852 ahut [http-nio-8080-exec-1] INFO c.a.shiro.RedisShiroSessionManager - 从request获取到session:8475e279-8733-45d5-8fb7-08a371c4cd91
2018-04-25 14:56:16.852 ahut [http-nio-8080-exec-1] INFO c.a.shiro.RedisShiroSessionManager - 从request获取到session:8475e279-8733-45d5-8fb7-08a371c4cd91
2018-04-25 14:56:16.852 ahut [http-nio-8080-exec-1] INFO c.a.shiro.RedisShiroSessionManager - 从request获取到session:8475e279-8733-45d5-8fb7-08a371c4cd91
2018-04-25 14:56:16.852 ahut [http-nio-8080-exec-1] INFO c.a.shiro.RedisShiroSessionManager - 从request获取到session:8475e279-8733-45d5-8fb7-08a371c4cd91
2018-04-25 14:56:16.852 ahut [http-nio-8080-exec-1] INFO c.a.shiro.RedisShiroSessionManager - 从request获取到session:8475e279-8733-45d5-8fb7-08a371c4cd91
2018-04-25 14:56:16.853 ahut [http-nio-8080-exec-1] INFO com.ahut.shiro.RedisShiroSessionDao - 更新session:8475e279-8733-45d5-8fb7-08a371c4cd91
2018-04-25 14:56:16.853 ahut [http-nio-8080-exec-1] INFO com.ahut.shiro.RedisShiroCache - 保存信息到缓存中:8475e279-8733-45d5-8fb7-08a371c4cd91
2018-04-25 14:56:16.854 ahut [http-nio-8080-exec-1] INFO c.a.shiro.RedisShiroSessionManager - 从request获取到session:8475e279-8733-45d5-8fb7-08a371c4cd91
2018-04-25 14:56:16.854 ahut [http-nio-8080-exec-1] INFO c.a.shiro.RedisShiroSessionManager - 从request获取到session:8475e279-8733-45d5-8fb7-08a371c4cd91
2018-04-25 14:56:16.854 ahut [http-nio-8080-exec-1] INFO com.ahut.shiro.RedisShiroSessionDao - 更新session:8475e279-8733-45d5-8fb7-08a371c4cd91
2018-04-25 14:56:16.855 ahut [http-nio-8080-exec-1] INFO com.ahut.shiro.RedisShiroCache - 保存信息到缓存中:8475e279-8733-45d5-8fb7-08a371c4cd91
2018-04-25 14:56:16.856 ahut [http-nio-8080-exec-1] INFO c.a.shiro.RedisShiroSessionManager - 从request获取到session:8475e279-8733-45d5-8fb7-08a371c4cd91
2018-04-25 14:56:16.857 ahut [http-nio-8080-exec-1] INFO c.a.shiro.RedisShiroSessionManager - 从request获取到session:8475e279-8733-45d5-8fb7-08a371c4cd91