Cookie 与 Session

一、Cookie 详解

1.Cookie 是什么?

Cookie,是某些网站为了辨别用户身份,进行session跟踪而储存在用户本地终端上的数据,由用户客户端计算机暂时或永久保存的信息;

2.为什么使用Cookie?

由于 web 程序是使用HTTP传输协议的,而HTTP协议属于无状态的协议,对于事务处理没有记忆能力;因此当后续处理需要前面的信息时就需要重新上传,这样就会导致每次连接传送的数据量增大了;
Cookie的出现就可以解决这一问题:
原理:
第一次登录后服务器返回一些数据(cookie)给浏览器,然后浏览器将其保存在本地,当该用户发送第二次请求时 就会把上次请求存储的cookie数据自动的携带给服务器,服务器通过浏览器携带的数据就能判断当前用户是那个了;
简单来讲cookie的工作原理就是:
给每个用户发放一个通行证,这样服务器就能通过通行证确认用户身份了;

3.Cookie 产生时间

  • Cookie 的使用首先取决于用户(原因:浏览器可以禁用Cookie,同时服务端也可以不set-cookie;
  • Cookie的两种保存方式:其1为浏览器将Cookie保存在内存中,其2为保存在客户端的硬盘上,之后每次HTTP请求浏览器都会将Cookie 发送给服务端;

4.Cookie 生存周期

Cookie 在生成时就会被指定一个Expire值,这就是Cookie的生存周期,在这个周期内Cookie有效,超出周期就会被清除;

5.Cookie 缺陷

  • 数量受限制

表现在:

一个浏览器能创建的 Cookie 数量最多不超过300个,且每个不能超过4KB,每个web站点能设置的Cookie 总数不能超过20个;

  • 安全性得不到保障:

表现在:存在跨站点脚本攻击

  • 浏览器可以设置禁用Cookie:

6.Cookie 使用场景

基于Cookie 的缺陷,因此一般使用在:

  • 对安全性要求不高;
  • 不需要存储大量的数据;
  • 主要用于客户端与服务器之间的状态保持;

二、Session 详解

1.什么是 Session

Session 也称为会话机制,那什么是会话呢?
会话的本质:就是一个"哈希表", 存储了一些键值对结构,其中 key 就是令牌的 ID(token/sessionId), value 就是用户信息(用户信息可以根据需求灵活设计);
sessionId 和 token 就可以理解成是同一个东西的不同叫法;

流程为:

  • 当用户登陆的时候, 服务器在 Session 中新增一个新记录, 并把 sessionId / token 返回给客户端(例如通过 HTTP 响应中的 Set-Cookie 字段返回);
  • 客户端后续再给服务器发送请求的时候, 需要在请求中带上 sessionId/ token (例如通过 HTTP 请求中的 Cookie 字段带上);
  • 服务器收到请求之后, 根据请求中的 sessionId / token 在 Session 信息中获取到对应的用户信息, 再进行后续操作;

2.Session 生命周期

根据需求设定,一般为半个小时(当你登陆一个服务器时,服务器返回给你一个sessionID ,登陆成功后的半个小时之内没有对该服务器进行任何的HTTP请求,半个小时之后进行一次HTTP请求,就会提示你重新登陆);


三、涉及核心方法

HttpServletRequest 类中的相关方法:

方法描述
HttpSession getSession()在服务器中获取会话,参数为true,当会话不存在时,新建会话;参数为false时,会话不存在返回null
Cookie[] getCookies()返回一个数组,包含客户端发送请求的所有Cookie对象,会自动把Cookie中的格式解析成键值对

HttpServletResponse 类中的相关方法:

方法描述
void addCookie(Cookie cookie)把指定的Cookie 添加到响应中

HttpSession 类中的相关方法:

方法描述
Object getAttribute(String name)该方法返回在该 session 会话中具有指定名称的对象,没有返回null
void setAttribute(String name, Object value)该方法使用指定的名称绑定一个对象到该 session 会话
boolean isNew()判定当前是否是新创建出的会话

Cookie 类中的相关方法:

方法描述
String getName()返回 cookie 的名称 ,名称在创建后不能改变,(这个值是 Set-Cooke 字段设置给浏览器的)
String getValue()获取与 cookie 关联的值
void setValue(String newValue)设置与 cookie 关联的值

四、代码示例(用户登陆)

简单实现用户登陆,登陆成功后允许访问敏感资源:

前端代码:请求方法为 post

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>

<body>

    <h3>用户登陆</h3>
    <form action="login" method="post">
    <input type="text" name="username" placeholder="请输入用户名">
    <br>
    <input type="text" name="password" placeholder="请输入密码">
    <br>
    <input type="submit" value="提交">
</form>
</body>

</html>

后端代码:

  • 创建一个 LoginServlet
package session;


import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;


@WebServlet("/login")
public class LoginServlet extends HttpServlet {
    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //body:username=xxx&password=xxx
        //登陆成功:跳转到request.html,此时允许访问敏感资源(/sensitive)
        //登陆失败:返回错误信息,此时不允许访问敏感资源
        String username = req.getParameter("username");
        String password = req.getParameter("password");


        //模拟数据库进行账号密码登陆
        if("abc".equals(username) && "123".equals(password)){
            //登陆成功
            //为登陆成功的用户创建一个session

            //tomcat启动时维护了一个数据结Map<String,Session>
            //用来保存多个用户的会话信息,键为sessionID的值(随机字符串)
            //获取当前请求的session,如果没有获取到就重新创建一个放在Map结构中
            HttpSession session =req.getSession(true);
            //session对象本身也是一个Map结构,可以保存多组键值对的信息
            session.setAttribute("u","username");
            //以上代码会设置一个set-cookie,jsessionid=随机字符串
            //跳转到登录后的页面
            resp.sendRedirect("request.html");
        }else{
            //登陆失败,返回错误信息
            resp.setContentType("text/html;charset=utf-8");
            resp.getWriter().println("登陆失败");
        }

    }
}

  • 创建一个 SensitiveServlet
package session;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;

@WebServlet("/sensitive")
public class SensitiveServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        resp.setContentType("text/html;charset=utf-8");
        //获取当次请求的session(此时可能登陆,也可能没有登陆)
        //如果没有获取到,就返回null
        HttpSession session = req.getSession(false);
        if(session != null){
            //session 不为空
            String username = (String) session.getAttribute("u");
            if(username != null){
                //session设置的数据也不为空
                resp.getWriter().println("用户已登陆,允许访问");
                return;
            }
        }

            resp.getWriter().println("用户未登录,不允许访问");
    }
}

运行 tomcat,三种情况如下所示:

  • (1)当没有登陆时就访问sensitive,此时出现如下界面所示内容:

在这里插入图片描述

  • (2)当访问登陆页面时,输入错误的账号密码时,如下所示:
    在这里插入图片描述
    当点击提交时,也面会发生跳转,但会显示如下登陆失败页面:
    在这里插入图片描述
  • (3)只有输入正确的账号密码时,页面才会跳转到request.html 中,显示:
    在这里插入图片描述

服务器会给当前用户设置一个set-cookie,jsessionid=随机字符串:
在这里插入图片描述

五、小结

  • Cookie 是客户端的机制,它可以存储在浏览器,也可以存储在本地;Session 是服务器端的机制,只能存储在服务器;
  • session能够存储任意的Java对象,但cookie只能存储string类型的对象;
  • session占用服务器性能,session越多就会增加服务器压力;
  • cookie没有session安全性高;
  • 单个cookie保存的数据不能超过4KB,而session的大小限制与服务器本身的内存大小相关;