- 1. Session存储数据
- 2. 获取Session数据
- 3. 关闭浏览器后Session ID失效问题
- 4. 在分布式环境下Session存在的问题
- 5. Cookie+MySQL实现登录功能
@RequestMapping(path = "/set/session", method = RequestMethod.GET)
@ResponseBody
public String setSession(HttpSession session) {
session.setAttribute("name","zhagnsan");
session.setAttribute("age",17);
return "set sesion";
}
Session是依赖于Cookie实现的,除了把Session对象保存在服务器的内存中以外,还会创建一个Cookie对象,Cookie的key=JSESSIONID,Cookie的vaue=sessionId,这个sessionId唯一的标识一个Session对象。
@RequestMapping("/get/session")
@ResponseBody
public String getSession(HttpSession session){
System.out.println(session.getAttribute("name"));
System.out.println(session.getAttribute("age"));
return "get session";
}
如下,当浏览器再次访问服务器时,请求头中会带有Cookie,这个Cookie就是之前存储的SessionId。
我发现当我关闭浏览器后,Session ID便失效了。
只要浏览器不关闭,以后每次访问服务器,浏览器都会带着这个Session ID,但是浏览器关闭后,这个Session ID就失效了,此时之前浏览器保存的Session ID便没了,那么怎么进行验证呢?
Session是一种服务器端的对象,保存在服务器中。 每个Session 有一个唯一的Session id。 Session的超时也是由服务器来控制,大部分的Session机制都使用进程中Cookie来保存Session id的,关闭浏览器后这个进程也就自动消失了,进程中的Cookie自然就消失了,那么Session id也跟着消失了,再次连接到服务器时也就无法找到原来的Session了。
其实服务器是不会知道浏览器关闭了没有,所以关闭浏览器时服务器是不会删除Session的,也正是这个原因服务器才会设置一个Session失效时间的(默认20分钟),不然服务器早晚会被撑爆。
客户端用cookie保存了sessionID:
客户端用cookie保存了sessionID,在没有把浏览器关掉的时候,这个sessionID会一直保存在浏览器中,每次请求的时候都会把这个sessionID提交到服务器,所以服务器认为我们是登录的;
如果太长时间没有请求服务器,服务器会认为我们已经所以把浏览器关掉了,这个时候服务器会把该sessionID从内存中清除掉,这个时候如果我们再去请求服务器,sessionID已经不存在了,所以服务器并没有在内存中找到对应的 sessionID,这是需要重新登录。
客户端没有用cookie保存sessionID :
如果我们请求服务器,因为没有提交sessionID上来,服务器会认为你是一个全新的请求,服务器会给你分配一个新的sessionID,这就是 为什么我们每次打开一个新的浏览器的时候都会产生一个新的sessionID。
当我们一旦把浏览器关掉后,再打开浏览器再请求该页面,它会让我们登录,这是为什么?
我们明明已经登录了,而且还没有超时,sessionID肯定还在服务器上的,为什么现在我们又要再一次登录呢?这是因为我们关掉浏览再请求的时候,我们提交的信息没有把刚才的sessionID一起提交到服务器,所以服务器不知道我们是同一个人,所以这时服务器又为我们分配一个新的sessionID。
4. 在分布式环境下Session存在的问题在分布式环境中,小明通过服务器1登录了系统, 那 session 会保存在服务器1上, 假设小明的下一次请求被转发到服务器3怎么办? 服务器3可没有小明的 session 啊。
于是出现了session 的复制, 服务器1创建Session并存储数据后,将Session复制到其他服务器上,这样小明即使访问服务器3怎也能找到Session,因为每个服务器上都存储了Session 数据。但是这样也会存在一定的问题,比如服务器内存压力大,且服务器之间是耦合的。
于是又出现了共享Session,将Session都存储在一台服务器上,Session的创建和获取都在这个服务器,但是如果服务器挂了,那么Session数据就全丢失了。
于是又演变成了目前比较主流的方法:
-
客户端的信息不存在Session中了,能存在Cookie中就存在Cookie中;
-
有些敏感数据无法存储在Cookie中,就存储在数据库中或redis中,数据库可以做集群;
这种方式虽然没有使用到Session,但是原理和Session原理类似,也需要依赖于Cookie:
- 浏览器请求登录,服务器在MySQL中保存登录信息,登陆成功后将 ticket(ticket唯一标识这条用户信息,类似于session id)通过Cookie返回给浏览器并保存,而不是直接将敏感信息保存在Cookie中;
- 当浏览器再次访问时,请求 Cookie 中会带有 ticket,通过 ticket 访问数据库可以得到用户信息并验证;
核心代码:
@PostMapping("/login")
public String login(String username,String password,boolean rememberme,
Model model,HttpSession session,HttpServletResponse response){
// Cookie的过期时间
int expiredSeconds = rememberme ? REMEMBERME_EXPIRED_SECONDS : DEFAULT_EXPIRED_SECONDS;
//登录
Map map = userService.login(username, password, expiredSeconds);
// 登录成功,将ticket通过cookie响应给浏览器
if(map.containsKey("ticket")){
Cookie cookie = new Cookie("ticket",map.get("ticket").toString());
cookie.setPath(contextPath);
cookie.setMaxAge(expiredSeconds);
response.addCookie(cookie);
// 重定向到首页
return "redirect:/index";
}else{
// 登录失败
model.addAttribute("usernameMsg",map.get("usernameMsg"));
model.addAttribute("passwordMsg",map.get("passwordMsg"));
return "/site/login";
}
}
public Maplogin(String username,String password,int expiredSeconds){ //生成登录凭证 LoginTicket loginTicket = new LoginTicket(); loginTicket.setUserId(user.getId()); loginTicket.setStatus(0); loginTicket.setExpired(new Date(System.currentTimeMillis()+expiredSeconds*1000)); // 随机字符串 loginTicket.setTicket(CommunityUtil.generateUUID()); //存入数据库中 loginTicketMapper.insertLoginTicket(loginTicket); //将登录凭证的ticket放入map中,以便在controller层将其通过cookie响应给浏览器 map.put("ticket",loginTicket.getTicket()); return map; }



