1. Cookie

1.1 Cookie 引入

所谓会话简单来说就是在客户端和服务端之间建立关联,并且交换数据的一种机制。比如说当我打开浏览器并且访问了一个网站,这个过程实际上就是客户端进程与遥远的服务器某个web应用之间建立起了一个 TCP 连接,通过这个连接,浏览器与 web 应用之间可以发送数据包实现“交流”,当关闭浏览器, TCP 连接断开,那么本次对话自然也就结束了。

由于 HTTP 这个协议本身是无状态的,通俗来说就是服务器不知道来访者的身份是什么。为了解决这个问题就引入了 cookie 和 session 两大技术,前者在客户端,后者在服务端。

1.2 Cookie 实现原理

关于 cookie 为什么要起名叫 cookie 可以参考这个博文,这个问题无需深究。试想如何让服务端能识别用户的身份呢,当你去公司时,是凭借工卡进入的,而工卡是你第一次去公司报道时公司发给你的,cookie 也是同理。当你首次访问某个服务器的应用时,它会判断你有没有相关的 cookie 信息,如果没有就会给你下发一个,并且将该 cookie 保存在客户端本地,之后再访问这个服务器时,就会带着这个 cookie 信息去访问。顺便说一句,cookie 信息是放在请求头中的。这样的话,服务端就能通过解析 cookie 信息来识别访问者的身份了。

1.3 Cookie 相关方法

  • Cookie[] cookies = req.getCookies() - 获得 Cookie
  • cookie.getName() - 获得 cookie 中的 key
  • cookie.getValue() - 获得 cookie 中的 value
  • new Cookie(“lastLoginTime”, System.currentTimeMillis()+””) - 新建一个 cookie
  • cookie.setMaxAge(246060) - 设置 cookie 的有效期,当设置为0时表示删除 cookie
  • resp.addCookie(cookie) - 响应给客户端一个 cookie

一个 cookie 对象类似于一组键值对,我们通常说的 cookie 实际上是由多个 cookie 对象组成的 cookie[]。

1.4 Cookie 特点

  1. 保存在客户端,存在安全性问题
  2. 大小有限制,并且键和值只能是字符串类型
  3. 如果不设置 cookie 的过期时间,则默认会话结束时 cookie 被删除

2. Session

2.1 Session实现原理

Session 是服务端用来维持会话状态的一种技术,简单描述这样的:当客户端首次访问一个服务器时,服务器会创建一个 session 对象,并且声称一个和该对象绑定的 sessionid回传给客户端,客户端拿到这个 sessionid 后将其保存在本地 cookie 中,之后再去访问相同的服务器就会带着这个 sessionid 去访问。

服务端拿到这个 sessionid 后就知道了客户端的身份,并且能够找到与这个 sessionid 对应的 session 对象。这个 session 对象本质上也是一个键值对,和 cookie 的区别在于,session 的值是 Object 类型,而 cookie 的值为 String 类型。这意味着可以把任何对象作为值保存在服务器的 session 对象中。
未命名文件.png

2.2 一些细节问题

2.2.1 Session 对象的创建时机

session 对象的创建时机完全取决于服务端。当服务端代码中如果存在 getSession() 方法时,就会创建一个 Session 对象,并且生成一个与其绑定的 sessionid。

2.2.2 如果一个服务器部署了多个应用…

一个服务器上部署多个应用,会有几个 sessionid 呢?我个人的理解应该是只有一个。分析如下:首先所谓会话应该是本地浏览器进程与远端服务器某端口建立的一个 TCP 连接,当访问远端同一个服务器的不同应用时,可能都会存在 getSession() 方法,如果是首次访问这个服务器,那么不论访问哪个应用都会生成一个 sessionid,此时在服务器上已经存在了与这个 id 对应的 session 对象,此外在客户端也会存在一个保存在本地 cookie 中的 sessionid。将来访问同一个服务器时,就会带着这个 sessionid 去访问。

即使是倒推,可以去查看浏览器的 cookie 长什么样,一个域名会对应一个 cookie,而一个 cookie 内只能有一个 sessionid,所以不要错误的理解为客户端与一个 web 应用间就会有一个 sessionid。

2.2.3 当会话结束时…

当关闭浏览器时,自然会话也就结束了,之前的 session 对象自然就无法获取了,所以还是有局限性的。此时如果重新打开浏览器去访问服务器,理论上就会拿到一个新的 sessionid 并保存在本地。

3. Cookie 和 Session 的区别

  1. 首先 cookie 把用户信息保存在本地,而 session 是保存在服务端
  2. cookie 保存在本地自然有数据不安全的问题,但是 session 保存在服务端也增加了服务端的压力

4. Cookie 和 Session 的应用实例

4.1 Cookie

实现一个请求会获取到上一次请求的时间。

  1. public class CookieDemo01 extends HttpServlet {
  2. @Override
  3. protected void doGet(HttpServletRequest req, HttpServletResponse resp)
  4. throws ServletException, IOException {
  5. //0. 设置编码
  6. resp.setHeader("Content-type", "text/html;charset=UTF-8");
  7. req.setCharacterEncoding("utf-8");
  8. resp.setCharacterEncoding("utf-8");
  9. //1. 得到请求的cookie
  10. Cookie[] cookies = req.getCookies();
  11. //2. 判断客户端cookie是否存在lastAccessTime
  12. for (Cookie cookie : cookies) {
  13. if (cookie.getName().equals("lastAccessTime")) {
  14. Long lastAccessTime = Long.parseLong(cookie.getValue());
  15. Date date = new Date(lastAccessTime);
  16. resp.getWriter().write("上次访问时间为: " + date + "");
  17. }
  18. }
  19. //3. 如果没有就回传一个lastAccessTime给客户端
  20. Cookie cookie = new Cookie("lastAccessTime", String.valueOf(System.currentTimeMillis()));
  21. cookie.setMaxAge(24*60*60);
  22. resp.addCookie(cookie);
  23. }
  24. }

4.2 Session

从客户端请求中获取 sessionid, 如果不存在则创建一个。并获取 session 对象中指定属性的值。

  1. public class SessionDemo01 extends HttpServlet {
  2. @Override
  3. protected void doGet(HttpServletRequest req, HttpServletResponse resp)
  4. throws ServletException, IOException {
  5. //0. 解决编码问题
  6. req.setCharacterEncoding("utf-8");
  7. resp.setCharacterEncoding("utf-8");
  8. resp.setHeader("Content-type", "text/html;charset=UTF-8");
  9. //1. 获得session对象(如果有则直接拿到,如果没有就创建一个新的返回给客户端)
  10. HttpSession session = req.getSession();
  11. //2. 设置session对象的属性
  12. session.setAttribute("manager", new Person("tianyichen", 26));
  13. //3. 获取session的id
  14. String id = session.getId();
  15. //4. 判断session是不是新建的
  16. if (session.isNew()) {
  17. resp.getWriter().write("session创建成功,id=" + session.getId());
  18. } else {
  19. resp.getWriter().write("session对象已经存在了,id=" + session.getId());
  20. }
  21. Person manager = (Person) session.getAttribute("manager");
  22. resp.getWriter().write(manager.toString());
  23. }
  24. }

删除旧 session,创建新 session。

  1. public class SessionDemo02 extends HttpServlet {
  2. @Override
  3. protected void doGet(HttpServletRequest req, HttpServletResponse resp)
  4. throws ServletException, IOException {
  5. req.setCharacterEncoding("utf-8");
  6. resp.setCharacterEncoding("utf-8");
  7. resp.setHeader("Content-type", "text/html;charset=UTF-8");
  8. HttpSession session = req.getSession();
  9. if (session.isNew()) {
  10. resp.getWriter().write("session对象创建成功,id=" + session.getId());
  11. } else {
  12. session.invalidate();
  13. resp.getWriter().write("旧session已删除,id=" + session.getId());
  14. }
  15. }
  16. }