利用缓存实现session共享
Posted 十一路客
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了利用缓存实现session共享相关的知识,希望对你有一定的参考价值。
一.使用场景
应用部署在A,B两台服务器上时,此时若一用户在A服务器上登录后,登录信息会存放在A服务器上的session中,之后若该用户的请求被分配到B服务器上,会出现请求错误,因为B服务器上没有该用户的登录信息,因此考虑将session放在缓存中,实现session在多个服务器间的共享。(其他方案:将session存放在cookie[不安全],或者数据库[速度慢]中)
二. 解决方案
将session存到缓存中,封装HttpSessionWrapper类,对session的使用还和之前一样
1.HttpServletRequestWrapper类
//处理session共享 将session存到mdb中 封装HttpServletRequestWrapper
public class HttpServletRequestWrapper extends javax.servlet.http.HttpServletRequestWrapper
//sessionId
private String sid;
//session实例
private SessionService sessionService;
public HttpServletRequestWrapper(String sid,HttpServletRequest request
, SessionService sessionService)
super(request);
this.sid = sid;
this.sessionService = sessionService;
@Override
public HttpSession getSession(boolean create)
return new HttpSessionWrapper(this.sid,super.getSession(create),this.sessionService);
@Override
public HttpSession getSession()
return new HttpSessionWrapper(this.sid,super.getSession(),this.sessionService);
2.HttpSessionWrapper类
public class HttpSessionWrapper implements HttpSession
private final Logger logger = LoggerFactory.getLogger(HttpSessionWrapper.class);
//session过期时间 单位是秒
private static final Integer EXPIRE_TIME = 3600;
//sessionID 自定义sessionId 否则会出现两个服务器生成的sessionid不一样的情况
private String sid;
//HashMap 对应session里的属性值
private Map map = new HashMap();
private SessionService sessionService;
private HttpSession session;
public HttpSessionWrapper(String sid, HttpSession session, SessionService sessionService)
this.sid = sid;
this.sessionService = sessionService;
this.session = session;
//根据session对map进行初始化
Map memSession = null;
String sessionJsonStr = this.sessionService.getSessionBySID(this.sid);
if(sessionJsonStr == null || sessionJsonStr.equals("") ||sessionJsonStr.equals(""))
memSession = null;
else
memSession = JSON.parseObject(sessionJsonStr, Map.class);
//sid没有加入到session中,需要初始化session,替换为自定义session
if(memSession == null)
memSession = new HashMap();
if(session != null)
Enumeration<String> names = session.getAttributeNames();
while(names.hasMoreElements())
String key = names.nextElement();
memSession.put(key,session.getAttribute(key));
//if
this.map = memSession;
//logger.info("initial HttpSessionWrapper");
//不能在这里更新 会出错 session内容会被置为null
//attributeChange();
//tair value需要是可序列化的 因此这里将map转化为了json 每访问一次session 需更新session有效期
private void attributeChange()
String sessionId = this.sid;
String content = JSON.toJSONString(this.map);
HashMap<String, Object> param = new HashMap<String, Object>();
param.put("sessionId", sessionId);
param.put("content", content);
param.put("expireTime", HttpSessionWrapper.EXPIRE_TIME);
Integer count = this.sessionService.updateSession(param);
if(count <= 0)
logger.error("attributeChange put sid: " + sessionId + " failure");
@Override
public Object getAttribute(String key)
logger.info("HttpSessionWrapper getAttribute name: " + key);
//这里不能加更新语句 因为构造函数循环中调用getAttribute时 this.map还没赋值完
//attributeChange();
if(this.map != null && this.map.containsKey(key))
Object value = this.map.get(key);
logger.info("value: " + value);
if(value != null)
logger.info(value.getClass().toString()); //class com.alibaba.fastjson.JSONObject
if(key.equals(Constants.SESSION_LOGIN_USER))//登录用户
TUser userObj = JSON.parseObject(value.toString(),TUser.class);
return userObj;
else if(key.equals(Constants.SESSION_MENU_LIST))//菜单列表
List<TMenu> menuList = JSON.parseArray(value.toString(),TMenu.class);
return menuList;
else if(key.equals("javax.security.auth.subject"))
//20180327 add
Subject object = JSON.parseObject(value.toString(), Subject.class);
logger.info(object.getClass().toString());
return object;
else
return value;
else
return null;
@Override
public Enumeration<String> getAttributeNames()
logger.info("HttpSessionWrapper getAttributeNames");
Set temp = this.map.keySet();
//attributeChange();
return new Vector(temp).elements();
@Override
public void removeAttribute(String name)
logger.info("HttpSessionWrapper removeAttribute name: " + name);
this.map.remove(name);
attributeChange();
@Override
public void setAttribute(String name, Object value)
logger.info("HttpSessionWrapper setAttribute name: " + name);
this.map.put(name,value);
attributeChange();
@Override
public void invalidate()
logger.info("HttpSessionWrapper invalidate");
this.map.clear();
long s1= System.currentTimeMillis();
try
Integer count = this.sessionService.deleteSessionBySID(this.sid);
logger.info("removeSession sid is:" + this.sid + "; count: " + count);
finally
logger.info("used time: " + (System.currentTimeMillis() - s1));
//以下没有用缓存mdb实现
@Override
public long getCreationTime()
return this.session.getCreationTime();
@Override
public String getId()
return this.sid;
@Override
public long getLastAccessedTime()
return this.session.getLastAccessedTime();
@Override
public ServletContext getServletContext()
return this.session.getServletContext();
@Override
public void setMaxInactiveInterval(int i)
this.session.setMaxInactiveInterval(i);
@Override
public int getMaxInactiveInterval()
return this.session.getMaxInactiveInterval();
@Override
public HttpSessionContext getSessionContext()
return this.session.getSessionContext();
@Override
public boolean isNew()
return this.session.isNew();
@Override
public Object getValue(String s)
return this.session.getValue(s);
@Override
public String[] getValueNames()
return this.session.getValueNames();
@Override
public void removeValue(String s)
this.session.removeValue(s);
@Override
public void putValue(String s, Object o)
this.session.putValue(s,o);
3.filter配置
@Component
public class ApplicationFilterConfig
@Bean
public FilterRegistrationBean filterRegistrationBean()
FilterRegistrationBean registrationBean = new FilterRegistrationBean();
MdbSessionFilter sessionFilter = new MdbSessionFilter();
registrationBean.setFilter(sessionFilter);
List<String> urlPatterns = new ArrayList<String>();
urlPatterns.add("/*");
registrationBean.setUrlPatterns(urlPatterns);
return registrationBean;
4.filter定义
//为实现session共享 创建Filter 重新封装request
public class MdbSessionFilter implements Filter
private static final Logger logger = LoggerFactory.getLogger(MdbSessionFilter.class);
//这里不可使用autowired注解注入
private SessionService sessionService;
private static final Set<String> noValidRoutes = new HashSet();
static
//必须加/
// noValidRoutes.add("/");
// noValidRoutes.add("/index");
// noValidRoutes.add("/login");
// noValidRoutes.add("/verifyCode");
noValidRoutes.add("/checkpreload.htm");
@Override
public void init(FilterConfig filterConfig) throws ServletException
logger.info("init MdbSessionFilter");
ServletContext context = filterConfig.getServletContext();
ApplicationContext ac = WebApplicationContextUtils.getWebApplicationContext(context);
sessionService = (SessionService) ac.getBean("sessionService");
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain)
throws IOException, ServletException
String sessionId = Constants.SESSION_COOKIE_NAME;
//logger.info("sessionId: " + sessionId);
HttpServletRequest request = (HttpServletRequest) servletRequest;
HttpServletResponse response = (HttpServletResponse) servletResponse;
String uri = request.getRequestURI();
if(noValidRoutes.contains(uri))
filterChain.doFilter(request,response);
return;
else
logger.info("MdbSessionFilter uri: " + uri);
Cookie cookies[] = request.getCookies();
Cookie sCookie = null;
String sid = "";
//sessionId已经存放在cookie中 直接取出 放入sid中
if (cookies != null && cookies.length > 0)
for (int i = 0; i < cookies.length; i++)
sCookie = cookies[i];
if (sCookie.getName().equals(sessionId))
sid = sCookie.getValue();
if (sid == null || sid.length() == 0)
//生成sessionID
sid = java.util.UUID.randomUUID().toString();
logger.info("sid: " + sid);
Cookie mycookie = new Cookie(sessionId, sid);
//设置生命周期为1天,秒为单位
mycookie.setMaxAge(-1);
mycookie.setPath("/");
mycookie.setHttpOnly(true);
response.addCookie(mycookie);
//logger.info("sessionId: " + sessionId + " -- sid: " + sid);
filterChain.doFilter(new HttpServletRequestWrapper(sid, request, sessionService), response);
@Override
public void destroy()
5.SessionService
public interface SessionService
/**
* 根据sid获取session内容
* @param sid sessionId
* @return session内容 json字符串
* */
String getSessionBySID(String sid);
/**
* 根据sessionId更新session
* @param param -- sessionId content expireTime
* @return 影响行数
* */
Integer updateSession(HashMap<String, Object> param);
/**
* 根据sessionId删除session
* @param sid sessionId
* @return 影响行数
* */
Integer deleteSessionBySID(String sid);
以上是关于利用缓存实现session共享的主要内容,如果未能解决你的问题,请参考以下文章
Redis 分布式缓存,是如何实现多台服务器SESSION 实时共享的