重寫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
}
}
}
到這裡,我們就可以去測試了。