tomcat集群中的session模拟
Cookie和Session的区别(面试必备)_chen13333336677的博客-CSDN博客_cookie和session区别面试
1 什么是session
浏览器每次访问,服务器都会为每个用户创建一个独立的HttpSession对象,第一次访问服务器时,请求中没有携带任何标识,所以服务器会创建一个新的session对象,并且生成一个SessionID;这个SessionID在响应浏览器的时候会被装进cookie中,从而被保存到浏览器中;当用户再一次访问服务器时,请求中会携带着cookie中的SessionID去访问;服务器会根据这个SessionID去查看是否有对应的Session对象;有就拿出来使用,没有就创建一个Session。
通常在Session中存储用户或菜单等信息
2 session的获取方式
2.1 从HttpServletRequest直接获取
2.2 @SessionAttribute注解获取
Spring MVC @SessionAttributes注解 - Just_Do - 博客园 (cnblogs.com)
3 Session返回流程
3.1 后端返回代码
后端使用map保存session并返回给前端
- 拓展:map在JDK1.8下的新写法
3.2 Session返回图解
用户首次访问服务器,服务器生成session,然后生成cookie保存在用户的浏览器上,直到设定的过期时间达到或者用户主动清除cookie
tomcat服务器上储存session的容器如图
在存活期内,cookie会跟随每一次request
- 拓展:请求头中的User-Agent给出了前端访问类型,如果需要电脑模拟手机访问服务器,通常就是修改这个属性值
3.3 存在的问题
这一整套流程中,session被保存在tomcat服务器上,因此,每当tomcat服务器重启时,session就会被清空,这就会造成用户需要重新输入登录密码等问题
除此之外,如果Nginx使用了ip_hash策略,并且后续tomcat服务器集群数量发生了变化,那么就会导致session与用户的cookie
这一切的源头都是因为session保存在了服务器上,我们必须想办法把session保存在一个持久化容器上,比如mysql,比如redis的rdb
4 手动创建session 解决session随tomcat服务器被清空的问题
假设要实现用户登录模块,此时可以利用hutool工具类生成一个MD5随机码充当sessionID,然后手动创建一个cookie返回给前端
代码如下
生成的MD5随机码如图
前端响应头如图
这个MD5码的目的就是做一个Jsession的效果,下次再访问时,后端能根据request携带的MD5码给出数据
由于这个MD5码是手动创建的,所以我们可以将其存到各种持久化容器中,这就解决了问题
5 储存自定义session到redis中
首先设置redis.windows.conf,把bind绑定的ip注释掉,不需要绑定本机
然后使用一个客户端工具来控制redis——another-redis-desktop manager
这个软件的GUI做的好看一点,也方便操作redis,不过更建议使用命令行,毕竟多敲才能熟练
在controller声明stringRedisTemplate,这个对象能提供操作redis的方法,利用这个对象的方法,将手动模拟的session存入redis
6 springboot项目下 配置web拦截器 实现对前端cookie的验证
以用户登录模块为例子,项目为springboot项目
当前端发送访问请求时,拦截下来,然后从请求头中取出cookie,拿到key为“sid”的cookie的value,再拿到redis去判断是否存在对应的value
存在则让用户直接登录成功,否则让用户重新输入账号密码
编写配置类
编写拦截器
@Component
public class SessionInterceptor implements HandlerInterceptor {
@Resource
private StringRedisTemplate stringRedisTemplate;
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
boolean isLogin = false;
Cookie[] cookies = request.getCookies();
if(cookies != null){
for (Cookie cookie : cookies) {
if("sid".equals(cookie.getName())){
String value = cookie.getValue();
String sessionKey = RedisConstants.USER_SESSION_KEY + value;
String UserInfoStr = stringRedisTemplate.opsForValue().get(sessionKey);
if(StringUtils.hasLength(UserInfoStr)){
isLogin = true;
}
}
}
}
if(!isLogin){
response.setCharacterEncoding("utf-8");
response.setContentType("application/json");
PrintWriter writer = response.getWriter();
HashMap<String, Object> map = new HashMap<>();
map.put("status",10003);
map.put("msg","请先登录!");
String string = JSON.toJSONString(map);
writer.write(string);
writer.flush();
}
return isLogin;
}
}
7 手动模拟的意义
上面手动模拟了session,再使用拦截器,就摆脱了tomcat的session,不再依赖tomcat服务器的状态,因此session不在担心tomcat服务器重启而被清空,同时session保存在redis中,就实现了tomcat服务器集群之间的共享,也不再担心Nginx使用ip_hash策略时,session与用户cookie对不上的问题
分布式锁的创建思维也是这样,自定义锁,只要解决锁的原子性问题,就能使用
8 spring提供的SpringSession干掉手动模拟session
spring已经把这个知识点整合了,给了一个springsession,我们不再需要手动封装这些东西
Spring Session 是Spring家族中的一个子项目,Spring Session提供了用于管理用户会话信息的API和实现。它把servlet容器实现的httpSession替换为spring-session,专注于解决 session管理问题,Session信息存储在Redis中,可简单快速且无缝的集成到我们的应用中
8.1 载入maven依赖
这个封装好的东西原理是利用SessionAttributeListener来监听session对象的操作,当Session生成时,将会自动将Session的JsessionID和保存到session中的属性保存至redis中
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<!-- 使用SessionAttributeListener 来监听Session对象的操作,当Session生成时,将会自动将session的JsessionID和保存到session中的属性保存至redis中 -->
<dependency>
<groupId>org.springframework.session</groupId>
<artifactId>spring-session-data-redis</artifactId>
</dependency>
8.2 springboot项目的yml的配置
spring:
redis:
host: 127.0.0.1
password: javasm
port: 6379
timeout: 3000
session:
store-type: redis
8.3 使用springsession
springBoot session使用_灰猫-CSDN博客_springboot使用session
SpringSession使用_LililililililMeng的博客-CSDN博客_springsession使用
spring-session之一:简介、使用及实现原理 - duanxz - 博客园 (cnblogs.com)
直接在controller的方法中使用HttpSession
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpSession;
@RestController
public class TestController {
@GetMapping("/set")
public String setSession(HttpSession session){
session.setAttribute("msg","Hello");
return "OK";
}
@GetMapping("/get")
public String getSession(HttpSession session){
return (String)session.getAttribute("msg");
}
}
8.4 springsession保存到redis的原理
redis是一个大map,结构如图,而springsession有点取巧,每一个session都设为一个key,然后设置过期时间,这样就能很方便的进行管理
虽然这会导致产生大量的key,但是对于redis来说,可以储存几十亿的key,所以spring随便用