我们在用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
}
}
}
到这里,我们就可以去测试了。