1、session共享问题介绍html
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共享。java
2、核心代码:nginx
一、ShareSession.java web
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); } }
二、ShareSessionIntercept.javaredis
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); } }
三、SSOUserFilter.javaspring
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 } }