標籤:extends 功能 als 序列化 sid 請求 成功 密鑰 對象
session會話在單台伺服器下不會出現共用問題,現在應用部署方式都是分布式,或者叢集部署,這樣必然會面臨一個問題,session共用。
session共用的解決方案也有很多,
一、web伺服器的粘性請求,比如採用nginx請求分發,使用ip_hash這種負載平衡方式,用戶端請求只會被分發到相同的後台server,這樣可以避免session共用的問題。但是缺點也很明顯
二、基於資料庫儲存(網站使用者量大的情況下,頻繁dml資料,對db壓力大)
三、基於cookie儲存(安全問題、雖然可以加密儲存、但是我覺得永遠不能將敏感性資料放在用戶端,不信任啊O(∩_∩)O哈哈~)
四、伺服器內建的session複製域(比如was下提供session複製功能、但這個損耗伺服器記憶體)
五、基於nosql(memcache、redis都可以)
http請求是無狀態的
這裡要引入一個概念sessionid,session對象當用戶端首次訪問時,建立一個新的session對象.並同時產生一個sessionId,並在此次響應中將sessionId以響應報文的方式些回用戶端瀏覽器記憶體或以重寫url方式送回用戶端,來保持整個會話
也就是說用戶端request請求時候,如果擷取了session,就預設分配一個jessionid,然後通過response響應到用戶端cookie,然後用戶端下一次請求,預設會攜帶這個jessionid請求到服務端,服務端拿到這個jessionid來區分不同的用戶端。
說清楚這些,就可以從sessionid入手了,要實現將session資訊存入redis,總結了下大概是三點:
1.實現httpsession介面,重寫我們需要用到的方法,比如set get這些。。balabala一堆......
2.繼承HttpServletRequestWrapper,這個類裡面有getSession()方法,我們需要重寫,來取自訂session
3.實現filter,用來攔截用戶端請求,擷取我們自訂的request,從而獲得session
具體實現:
1.實現httpsession介面
public class HttpSessionWrapper implements HttpSession { protected final Logger logger = LoggerFactory.getLogger(getClass());private String sid = "";private HttpServletRequest request;private HttpServletResponse response;private final long creationTime = System.currentTimeMillis();private final long lastAccessedTime = System.currentTimeMillis();private SessionMeta meta;public HttpSessionWrapper() {} public HttpSessionWrapper(String sid,SessionMeta meta, HttpServletRequest request,HttpServletResponse response) { this.sid=sid; this.request=request; this.response=response; this.meta=meta;}public Object getAttribute(String name) { logger.info(getClass()+"getAttribute(),name:"+name); Jedis jedis =null; Object obj =null; String jsonStr = null; try { jedis =JedisPoolStore.getInstance().getJedis(meta.getHost(), meta.getPort()); jsonStr = jedis.get(name); if(jsonStr!=null||StringUtils.isNotEmpty(jsonStr)){ jedis.expire(name, meta.getSeconds());// 重設到期時間 obj =JSON.parseObject(jsonStr, User.class); //反序列對象 } if (jedis != null) { JedisPoolStore.getInstance().returnJedis(jedis); } return obj;} catch (JSONException je) {logger.error(je.getMessage());if (null != jedis)JedisPoolStore.getInstance().returnJedis(jedis); return jsonStr;} catch (Exception e) {logger.error(e.getMessage());if (e instanceof JedisException) {if (null != jedis)JedisPoolStore.getInstance().returnBrokenJedis(jedis);} else {if (null != jedis)JedisPoolStore.getInstance().returnJedis(jedis);}throw new HttpSessionException(" session 異常 getAttribute() name:"+name);} } public void setAttribute(String name, Object value) { logger.info(getClass()+"setAttribute(),name:"+name); Jedis jedis =null; try { jedis =JedisPoolStore.getInstance().getJedis(meta.getHost(), meta.getPort()); if(value instanceof String){ String value_ =(String) value; jedis.set(name,value_);//一般字元串對象 }else{ jedis.set(name, JSON.toJSONString(value));//序列化對象 } jedis.expire(name, meta.getSeconds());// 重設到期時間 if (jedis != null) { JedisPoolStore.getInstance().returnJedis(jedis); }} catch (Exception e) {logger.error(e.getMessage());if (e instanceof JedisException) {if (null != jedis)JedisPoolStore.getInstance().returnBrokenJedis(jedis);} else {if (null != jedis)JedisPoolStore.getInstance().returnJedis(jedis);}throw new HttpSessionException(" session 異常 setAttribute() name:"+name+",value:"+value);} } /** * 不可用 * @deprecated * */ public void invalidate() { logger.info(getClass()+"invalidate()"); } public void removeAttribute(String name) { logger.info(getClass()+"removeAttribute(),name:"+name); if(StringUtils.isNotEmpty(name)){ Jedis jedis =null; try { jedis =JedisPoolStore.getInstance().getJedis(meta.getHost(), meta.getPort()); jedis.del(name); if (jedis != null) { JedisPoolStore.getInstance().returnJedis(jedis); } } catch (Exception e) { logger.error(e.getMessage()); if (e instanceof JedisException) { if (null != jedis) JedisPoolStore.getInstance().returnBrokenJedis(jedis); } else { if (null != jedis) JedisPoolStore.getInstance().returnJedis(jedis); } throw new HttpSessionException(" session 異常 removeAttribute() name:"+name); } } } /** * 不可用 * @deprecated * */ public Object getValue(String name) { return null; } /** * 不可用 * @deprecated * */ public Enumeration getAttributeNames() { return null; } /** * 不可用 * @deprecated * */ public String[] getValueNames() { return null; } /** * 不可用 * @deprecated * */ public void putValue(String name, Object value) { } /** * 不可用 * @deprecated * */ public void removeValue(String name) { } public long getCreationTime() { return creationTime; } public String getId() { logger.info(getClass()+"getId():"+sid); return sid; } public long getLastAccessedTime() { return lastAccessedTime; } /** * 不可用 * @deprecated * */ public ServletContext getServletContext() { return null; } /** * 不可用 * @deprecated * */ public void setMaxInactiveInterval(int interval) { } /** * 不可用 * @deprecated * */ public int getMaxInactiveInterval() { return 0; } /** * 不可用 * @deprecated * */ public HttpSessionContext getSessionContext() { return null; } /** * 不可用 * @deprecated * */ public boolean isNew() { logger.info(getClass()+"isNew()"); return false; }}
2.繼承HttpServletRequestWrapper
/*** * * @author xiaoshuai * */public class DefinedHttpServletRequestWrapper extends HttpServletRequestWrapper{ protected final Logger logger = LoggerFactory.getLogger(getClass()); private HttpSessionWrapper currentSession; private HttpServletRequest request; private HttpServletResponse response; private String sid = "";private SessionMeta meta; public DefinedHttpServletRequestWrapper(HttpServletRequest request) { super(request); } public DefinedHttpServletRequestWrapper(String sid, HttpServletRequest request) { super(request); this.sid = sid; } public DefinedHttpServletRequestWrapper(String sid, SessionMeta meta,HttpServletRequest request, HttpServletResponse response) { super(request); this.request = request; this.response = response; this.sid = sid; this.meta=meta; } @Override public HttpSession getSession(boolean create) { if(currentSession != null) { return currentSession; } if(!create) { return null; } currentSession = new HttpSessionWrapper(sid,meta, request, response); return currentSession; } @Override public HttpSession getSession() { return getSession(true); } }
3.實現filter
public class SessionFilter implements Filter{protected final Logger logger = LoggerFactory.getLogger(getClass());private static SessionMeta meta = new SessionMeta();private static final String host ="host";private static final String port ="port";private static final String seconds="seconds";public void init(FilterConfig filterConfig) throws ServletException {logger.debug("init filterConfig info");meta.setHost(filterConfig.getInitParameter(host));meta.setPort(Integer.parseInt(filterConfig.getInitParameter(port)));meta.setSeconds(Integer.parseInt(filterConfig.getInitParameter(seconds)));}public void doFilter(ServletRequest request, ServletResponse response,FilterChain chain) throws IOException, ServletException { //從cookie中擷取sessionId,如果此次請求沒有sessionId,重寫為這次請求設定一個sessionIdHttpServletRequest httpRequest = (HttpServletRequest) request;HttpServletResponse httpResponse = (HttpServletResponse) response; String sid = CookieHelper.findCookieInfo(httpRequest,CookieHelper.COOKIE_DOMAIN_NAME); if(StringUtils.isEmpty(sid) ){ try { sid =CookieHelper.saveCookie(SessionId.doIds(), httpResponse);} catch (Exception e) {e.printStackTrace();} } logger.info("JESSIONID:"+sid); chain.doFilter(new DefinedHttpServletRequestWrapper(sid,meta,httpRequest, httpResponse), response);}public void destroy() {}}
3.配置web.xml
<!-- session過濾器 --><filter><filter-name>sessionFilter</filter-name><filter-class>cn.session.filter.SessionFilter</filter-class><init-param><param-name>host</param-name><param-value>10.1.193.1</param-value></init-param><init-param><param-name>port</param-name><param-value>6372</param-value></init-param><init-param><param-name>seconds</param-name><param-value>1800</param-value></init-param></filter><filter-mapping><filter-name>sessionFilter</filter-name><url-pattern>/*</url-pattern></filter-mapping>
第一次產生sessionid訪問:
系統登入後存使用者資訊至redis:
關於安全這塊,因為不管登入系統與否,sessionid都會產生,這時候就會產生一個問題,因為cookie是可以被修改的,就會產生一個問題,撞session的分享。。。換成不同的sessionid去請求系統。。。總有一天會撞上。。
SO,我這邊是這樣處理的,
當登入成功之後,產生一個token令牌,產生規則的話自己定,一堆密鑰比如系統名字+sessionid+userid+固定字元,產生一個加密的字串,放入cookie。
這樣當我們擷取當前登入使用者時,解密token,擷取sessionid,然後取redis使用者資訊。。(切記這裡不要直接通過sessionid取使用者資訊,有風險!!!)
使用者沒有登入成功,自然也沒有這個令牌。。。
這裡又有另外一個問題,使用者如果禁用cookie呢????? so what..... 下次再說這個。。。
水平有限,如果你有更好的方式,請聯絡我,交流................................tks!!!!
分布式應用session會話管理-基於redis