分布式應用session會話管理-基於redis

來源:互聯網
上載者:User

標籤: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

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.