一、session共享问题介绍
session主要用于服务端存储用户会话信息,cookie用于浏览器存储用户会话信息。单系统服务session都存在同一个web容器中,例如tomcat中,用户请求都只访问这个容器中的session信息,除非容器挂了,否者不存在session取不到的情况。随着业务的扩展,应用用户的增加,当个容器存放系统应用消耗服务的cup和内存会不断增加,导致应用性能下降。此时考虑用nginx集群做应用的负载均衡请求分发,假设用ngnix集群三个服务,分别用A、B、C表示。按照未做session共享,仍然使用Servlet中HttpSession情景,假设此时访问的是A服务,那么session将存储在A服务中,此处如果A服务宕机,ngnix会将用户的请求分发到B或者C服务,但是B和C服务中没有存A存放的Session信息,那么用户访问的数据将会丢失。为了解决session数据丢失,需要将session共享,主流做法是将session存储在nosql数据库中,例如memcache、redis等。也有很多人通过spring session 实现共享,原理大致一样,下面主要实现了memcache缓存session共享。
二、核心代码:
1、ShareSession.java
package com.gccode.sso.session;
import java.util.HashMap;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.context.support.WebApplicationContextUtils;
import com.gccode.sso.cache.BaseCache;
import com.gccode.sso.common.CookieUtils;
/**
* Title: 缓存共享session实现类
*
*/
public class ShareSession {
/**
* 存到缓存中的共享session容器
*/
private Map<String,Object> sessionWrapper;
/**
* session对象
*/
private static ShareSession session;
/**
* 浏览器请求回话id
*/
private String sessionId;
/**
* 缓存
*/
private BaseCache cache;
/**
* 构造函数初始化参数
* @param request
*/
public ShareSession(HttpServletRequest request){
WebApplicationContext wac = WebApplicationContextUtils.getRequiredWebApplicationContext(request.getServletContext());
this.cache = (BaseCache) wac.getBean("cache");
this.sessionId = CookieUtils.getCookieId(request);
this.sessionWrapper = new HashMap<String, Object>();
}
/**
* 获取session对象
* @param request
* @return
*/
public static synchronized ShareSession get(HttpServletRequest request){
if(session == null){
session = new ShareSession(request);
}
return session;
}
/**
* 设置session,带超时时间
* @param key
* @param value
* @param outTime
* @return sessionId
*/
public String setSession(String key,Object value,int outTime) {
sessionWrapper.put(key, value);
cache.put(sessionId,sessionWrapper,outTime);
return sessionId;
}
/**
* 设置session
* @param key
* @param value
* @return
*/
public String setSession(String key,Object value) {
sessionWrapper.put(key, value);
cache.put(sessionId, sessionWrapper);
return sessionId;
}
/**
* 设置session,不带超时时间
* @return
*/
@SuppressWarnings("unchecked")
public Map<String, Object> getSession() {
return (Map<String, Object>)cache.get(sessionId);
}
/**
* 删除session中的值
* @param key
*/
public void removeSession(String key) {
Map<String,Object> session = getSession();
session.remove(key);
cache.put(sessionId,session);
}
}
2、ShareSessionIntercept.java
package com.gccode.sso.session;
import java.util.HashMap;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.context.support.WebApplicationContextUtils;
import com.gccode.sso.cache.BaseCache;
import com.gccode.sso.common.CookieUtils;
/**
* Title: 缓存共享session实现类
*
*/
public class ShareSession {
/**
* 存到缓存中的共享session容器
*/
private Map<String,Object> sessionWrapper;
/**
* session对象
*/
private static ShareSession session;
/**
* 浏览器请求回话id
*/
private String sessionId;
/**
* 缓存
*/
private BaseCache cache;
/**
* 构造函数初始计划参数
* @param request
*/
public ShareSession(HttpServletRequest request){
WebApplicationContext wac = WebApplicationContextUtils.getRequiredWebApplicationContext(request.getServletContext());
this.cache = (BaseCache) wac.getBean("cache");
this.sessionId = CookieUtils.getCookieId(request);
this.sessionWrapper = new HashMap<String, Object>();
}
/**
* 获取session对象
* @param request
* @return
*/
public static synchronized ShareSession get(HttpServletRequest request){
if(session == null){
session = new ShareSession(request);
}
return session;
}
/**
* 设置session,带超时时间
* @param key
* @param value
* @param outTime
* @return sessionId
*/
public String setSession(String key,Object value,int outTime) {
sessionWrapper.put(key, value);
cache.put(sessionId,sessionWrapper,outTime);
return sessionId;
}
/**
* 设置session
* @param key
* @param value
* @return
*/
public String setSession(String key,Object value) {
sessionWrapper.put(key, value);
cache.put(sessionId, sessionWrapper);
return sessionId;
}
/**
* 设置session,不带超时时间
* @return
*/
@SuppressWarnings("unchecked")
public Map<String, Object> getSession() {
return (Map<String, Object>)cache.get(sessionId);
}
/**
* 删除session中的值
* @param key
*/
public void removeSession(String key) {
Map<String,Object> session = getSession();
session.remove(key);
cache.put(sessionId,session);
}
}
3、SSOUserFilter.java
package com.gccode.sso.session;
import java.io.IOException;
import java.util.Map;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.lang3.StringUtils;
import org.springframework.cache.Cache;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.context.support.WebApplicationContextUtils;
import com.gccode.sso.common.Constant;
import com.gccode.sso.common.CookieUtils;
/**
* Title: 用户请求过滤
*
*/
public class SSOUserFilter implements Filter{
@Override
public void init(FilterConfig filterConfig) throws ServletException {
// TODO Auto-generated method stub
}
@Override
public void doFilter(ServletRequest req, ServletResponse res,
FilterChain chain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) res;
String sessionId = CookieUtils.getCookieId(request);
WebApplicationContext wac = WebApplicationContextUtils.getRequiredWebApplicationContext(req.getServletContext());
Cache cache = (Cache) wac.getBean("cache");
if (StringUtils.isEmpty(sessionId) || null == cache.get(sessionId)) {
request.getRequestDispatcher("/logout.html").forward(request,
response);
} else {
@SuppressWarnings("unchecked")
Map<String, Object> session = (Map<String, Object>) cache.get(
sessionId).get();
if (session.get(Constant.SESSION_USER_KEY) == null) {
request.getRequestDispatcher("/logout.html").forward(request,
response);
} else {
chain.doFilter(req, res);
}
}
}
@Override
public void destroy() {
// TODO Auto-generated method stub
}
}