session会话在单台服务器的情况下不会出现共享问题,然而在集群或者分布式应用中,则会出现session共享问题。
解决方案
- 基于cookie存储,但是存在安全问题;
- 基于数据库存储,用户量大的情况下对db压力大;
- 服务器内置的session复制,如was提供复制功能,但是会损耗服务器内存;
- 采用Nginx请求分发,绑定ip,只能访问用户第一次访问的服务器,不能支持负载均衡;
- 基于noSql,如memcache,redis等;
- 使用框架的会话管理工具 spring-session(使用了spring-data-redis连接池,项目使用spring framework时才可以);
- 如果项目中使用shiro这种自带session(非httpsession)的权限框架,需要重写shiro的SessionDao将session存入redis(或其他)来实现session共享,可以自己重写(也不是很麻烦)或者找现成的(shiro-redis)。
HTTP请求是无状态的
客户端首次访问时,会创建一个session对象,并生成一个sessionId,在此次响应中将sessionId以响应报文的方式些回客户端浏览器内存或以重写url方式送回客户端,来保持整个会话。
也就是说客户端request请求时候,如果获取了session,就默认分配一个jessionid,然后通过response响应到客户端cookie,然后客户端下一次请求,默认会携带这个jessionid请求到服务端,服务端拿到这个jessionid来区分不同的客户端。
具体实现
1)原理:写一个Session过滤器拦截每一次请求,在这里检查由Cookie生成的SessionID,进行创建或获取。核心是实现使用装饰类,实现Session在Redis中的存取操作。
2)此处存取方式为 sessionID+sessionKey作为Redis的key ==== sessionValue作为Redis的value,这样保存了每次存取都从Redis中操作,效率更高。
3)注意:序列化方式推荐使用Apache下Commons组件——SerializationUtils 或 org.springframework.util.SerializationUtils
//定义请求经过的Session过滤器
public class SessionFilter extends OncePerRequestFilter implements Filter {
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
// 从cookie中获取sessionId,如果此次请求没有sessionId,重写为这次请求设置一个sessionId
String sid &