1. 程式人生 > >重寫request的getSession方法實現叢集session共享

重寫request的getSession方法實現叢集session共享

我們在用redis實現session共享的時候,會遇到這樣的問題:當一個使用者登陸後,可能叢集環境下給你分配到另一臺伺服器,這時候你用request.getSession()獲取的session是現在伺服器上的session,裡面沒有你想要的使用者資訊,然後你又要重新登入,這樣是你會同意嗎,肯定不行的,所以我們不需要系統自動建立session了,我們來自己寫一個實現共享;

首先實現重寫的第一步就是使用過濾器,那麼我們來配置一個過濾器:他能在目標方法之前對請求進行處理,這樣我們就可以用過濾器代替原有的建立session的方法;

<!-- 自定義過濾器:testFilter1 -->   
<filter>  
    <filter-name>sessionFilter</filter-name>  
    <filter-class>util.SessionFilter</filter-class>  
</filter>  
<filter-mapping>  
    <filter-name>sessionFilter</filter-name>  
    <url-pattern>/*</url-pattern>  
</filter-mapping> 

然後我們去建立這個過濾器:

public class SessionFilter implements Filter {
	public FilterConfig config;
	@Override
	public void doFilter(ServletRequest request, ServletResponse response,
			FilterChain chain) throws IOException, ServletException {
		// TODO Auto-generated method stub
		 HttpServletRequest req = (HttpServletRequest) request;
		 HttpServletResponse res=(HttpServletResponse)response;
         req = new SecurityServletRequestWrapper(req, res);
         chain.doFilter(req, res);
	}
	@Override
	public void destroy() {
		// TODO Auto-generated method stub
		this.config=null;
	}
	@Override
	public void init(FilterConfig filterConfig) throws ServletException {
		// TODO Auto-generated method stub
		this.config=filterConfig;
	}
}

上面的SecurityServletRequestWrapper類就是我們要去重寫的類,這裡我們要重寫HttpServletRequestWrapper中的getSession方法:

public class SecurityServletRequestWrapper extends HttpServletRequestWrapper{
	private HttpSession session;
	private HttpServletRequest request;
	private HttpServletResponse response;
	public SecurityServletRequestWrapper(HttpServletRequest request,HttpServletResponse response) {
		super(request);
		this.request=request;
		this.response=response;
	}
	//重寫獲取session的方法
	public HttpSession getSession() {
		return this.getSession(true);
	} 
	public HttpSession getSession(boolean create) {
		if(create){
			String id = CookieUtil.getCookieValue(request, "pcxSessionId");
			if(StringUtils.isEmpty(id)){
				id=UUID.randomUUID().toString();
				CookieUtil.setCookie(request, response, "pcxSessionId", id, 60*60);
			}
			this.session=new DispacherSessionImmpl(this.request,this.response,id);
			return this.session;
		}else{
			return null;
		}
    }
}

我們從cookie裡面獲取session的ID,如果獲取不到,那就建立一個id然後儲存到session中,這個ID就是新的session的ID;這裡有個CookieUtil類它裡面兩個方法就是從cookie中獲取資料和儲存資料到cookie;當然其他方法沒有實現;

public class CookieUtil {
    public static void setCookie(HttpServletRequest request,
            HttpServletResponse response, String name, String value, int seconds) {
        if (StringUtils.isEmpty(name) || StringUtils.isEmpty(value))
            return;
        Cookie cookie = new Cookie(name, value);
        //cookie.setDomain(domain);
        cookie.setMaxAge(seconds); 
        cookie.setPath("/");
        response.setHeader("P3P",
                "CP='IDC DSP COR ADM DEVi TAIi PSA PSD IVAi IVDi CONi HIS OUR IND CNT'");
        response.addCookie(cookie);
    }
    public static String getCookieValue(HttpServletRequest request,String name){
        Cookie cookies[] = request.getCookies();
        if (cookies != null) {
            for (int i = 0; i < cookies.length; i++) {
                if (name.equalsIgnoreCase(cookies[i].getName())) {
                    return cookies[i].getValue();
                }
            }
        }
        return "";
    }
}

然後我們重點看的就是DispacherSessionImmpl類;它實現了HttpSession介面,方法有些多,只說一下修改了的方法,沒有說的都是實現原方法就可以;

public class DispacherSessionImmpl implements HttpSession{
	
	private HttpSession session;
	private HttpServletRequest request;
	private HttpServletResponse response;
	private Map<String,Object> sessionMap=null;
	private SessionStore sessionStore=new SessionStore();
	private String sid;
	public DispacherSessionImmpl(){
	}
	public DispacherSessionImmpl(HttpSession session){
		this.session=session;
	}
	public DispacherSessionImmpl(HttpServletRequest request,HttpServletResponse response,String id){
		this.sid=id;
		this.request=request;
		this.response=response;
	}

sessionMap儲存放進session中的資料的;

SessionStore是將session資料與redis進行關聯實現共享;

	@Override
	public Object getAttribute(String name) {
		// TODO Auto-generated method stub
		if(sessionMap==null){
			sessionMap=sessionStore.getSession(this.getId());
		}
		return sessionMap.get(name);
	}

這個是獲取你儲存的資訊的;

@Override
	public void setAttribute(String name, Object value) {
		// TODO Auto-generated method stub
		if(sessionMap==null){
			sessionMap=sessionStore.getSession(this.getId());
		}
		this.sessionMap.put(name, value);
		sessionStore.saveSession(this.getId(), sessionMap);
	}

往session中儲存資料的;這裡就是redis中儲存了;

@Override
	public void removeAttribute(String name) {
		// TODO Auto-generated method stub
		if(sessionMap==null){
			sessionMap=sessionStore.getSession(this.getId());
		}
		sessionMap.remove(name);
		sessionStore.removeSession(this.getId());
	}

移除資料的方法;

@Override
	public void invalidate() {
		// TODO Auto-generated method stub
		this.sessionMap.clear();
		sessionStore.removeSession(this.getId());
		
	}

刪除用的;

接下來我們就看一下儲存那個類,這裡要用到序列化,我們先寫一個序列化得到類出來,直接呼叫就行

public class Hessian2Serialization {
	public byte[] serialize(Object obj) throws IOException{  
	    if(obj==null) throw new NullPointerException();  
	    ByteArrayOutputStream os = new ByteArrayOutputStream();  
	    HessianOutput ho = new HessianOutput(os);  
	    ho.writeObject(obj);  
	    return os.toByteArray();  
	}  
	public Object deserialize(byte[] by) throws IOException{  
	    if(by==null) throw new NullPointerException();  
	    ByteArrayInputStream is = new ByteArrayInputStream(by);  
	    HessianInput hi = new HessianInput(is);  
	    return hi.readObject();  
	}  
}

在這裡,我們需要hession的jar包,

我們看一下儲存:

public class SessionStore {
	private Hessian2Serialization h2Serialization=new Hessian2Serialization();
	//private RedisUtil redisUtil;
	private Jedis jedis=new Jedis("127.0.0.1", 6379);
	public Map<String,Object> getSession(String sid){
		 Map<String,Object> session=new HashMap<String, Object>();
		 try {
			 byte[] bs = jedis.get(sid.getBytes());
			 if(bs!=null){
				 Object deserialize = h2Serialization.deserialize(bs);
				 session=(Map<String, Object>) deserialize;
			 }
		} catch (Exception e) {
			// TODO: handle exception
		}
		return session;
	}
	public void saveSession(String sid,Map<String, Object> session){
		try {
			byte[] bs = h2Serialization.serialize(session);
			jedis.set(sid.getBytes(), bs);
			jedis.expire(sid.getBytes(), 3600);//設定過期時間
		} catch (Exception e) {
			// TODO: handle exception
		}
	}
	public void removeSession(String sid){
		try {
			jedis.del(sid.getBytes());
		} catch (Exception e) {
			// TODO: handle exception
		}
	}
}
到這裡,我們就可以去測試了。