Cookie & Session


HTTP 协议是一种无状态协议,即每次服务端接收到客户端的请求时,都是一个全新的请求,服务器并不知道客户端的历史请求记录。Cookie 和 Session 的主要目的就是为了弥补 HTTP 的无状态特性。

什么是Cookie?

Cookie 是一种服务端产生存储在客户端用于记录客户端状态的机制。

Cookie工作原理

  • 客户端首次请求服务器:如果服务器需要记录该用户状态,就使用 response 向客户端颁发一个 Cookie,客户端会把 Cookie 保存起来;
  • 客户端再次请求服务器:客户端会把请求连同该 Cookie 一同提交给服务器,服务器检查该 Cookie,以此来辨认用户状态。
首次请求 再次请求

首次请求

  • ①request
GET /reader/ HTTP/1.1
Host: jwt1399.top
  • ②response
HTTP/1.1 200 OK
Date: Thu, 12 Jul 2021 07:12:20 GMT
Server: Apache
<Set-Cookie: sid=1342077140226724; path=/; expires=Wed,10-Oct-12 07:12:20 GMT>
Content-Type: text/plain; charset=UTF-8

再次请求

  • ③request
GET /image/ HTTP/1.1
Host: jwt1399.top
Cookie: sid=1342077140226724

Cookie的首部字段

首部字段名 首部类型 说明
Set-Cookie 响应首部字段 开始状态管理所使用的Cookie信息
Cookie 请求首部字段 服务器接收到的Cookie信息

Set-Cookie属性

属性 说明
name Cookie的名称。Cookie一旦创建,名称便不可更改
value Cookie的值。如果值为Unicode字符,需要为字符编码。如果为二进制数据,则需要使用base64编码
expires Cookie的有效期(Cookie被删除时的时间戳)。格式为GMT,若设置为以前的时间,则该Cookie立刻被删除,并且该时间戳是服务器时间,不是本地时间!若不明确指定则默认为浏览器关闭前为止。
path Cookie的使用路径。若不指定则默认为文档所在的文件目录
domain 可以访问该Cookie的域名。如果设置为“.google.com”,则所有以“google.com”结尾的域名都可以访问该Cookie。注意第一个字符必须为“.”
secure 仅在HTTPS 安全通信时才会发送Cookie,默认为false
HttpOnly 加以限制,使Cookie不能被JavaScript 脚本访问
maxAge Cookie失效的时间,单位秒。如果为正数,则该Cookie在maxAge秒后失效。如果为负数,该Cookie为临时Cookie,关闭浏览器即失效,浏览器也不会以任何形式保存该Cookie。如果为0,表示删除该Cookie。默认为-1
Size Cookie的大小。在所有浏览器中,任何Cookie大小超过限制都被忽略,且永远不会被设置。各个浏览器对Cookie的最大值和最大数目有不同的限制
SameSite 用来限制第三方 Cookie,从而减少安全风险。它有3个属性,分别是:Strict,Lax,None
Priority 优先级,chrome的提案,定义了三种优先级,Low/Medium/High,当cookie数量超出时,低优先级的cookie会被优先清除。
comment Cookie的用处说明,浏览器显示Cookie信息的时候显示该说明
version Cookie使用的版本号。0表示遵循Netscape的Cookie规范,1表示遵循W3C的RFC 2109规范

实例测试

我们可以编写一个测试用例来看看:

Cookie cookie = new Cookie("test", "yyds");
resp.addCookie(cookie);
resp.sendRedirect("time");
for (Cookie cookie : req.getCookies()) {
    System.out.println(cookie.getName() + ": " + cookie.getValue());
}

我们可以观察一下,在HttpServletResponse中添加Cookie之后,浏览器的响应头中会包含一个Set-Cookie属性,同时,在重定向之后,我们的请求头中,会携带此Cookie作为一个属性,同时,我们可以直接通过HttpServletRequest来快速获取有哪些Cookie信息。

最关键的其实是namevaluemaxAgedomain属性。

那么我们来尝试修改一下maxAge来看看失效时间:

cookie.setMaxAge(20);

设定为20秒,我们可以直接看到,响应头为我们设定了20秒的过期时间。20秒内访问都会携带此Cookie,而超过20秒,Cookie消失。

既然了解了Cookie的作用,我们就可以通过使用Cookie来实现记住我功能,我们可以将用户名和密码全部保存在Cookie中,如果访问我们的首页时携带了这些Cookie,那么我们就可以直接为用户进行登陆,如果登陆成功则直接跳转到首页,如果登陆失败,则清理浏览器中的Cookie。

那么首先,我们先在前端页面的表单中添加一个勾选框:

<div>
    <label>
        <input type="checkbox" placeholder="记住我" name="remember-me">
        记住我
    </label>
</div>

接着,我们在登陆成功时进行判断,如果用户勾选了记住我,那么就讲Cookie存储到本地:

if(map.containsKey("remember-me")){   //若勾选了勾选框,那么会此表单信息
    Cookie cookie_username = new Cookie("username", username);
    cookie_username.setMaxAge(30);
    Cookie cookie_password = new Cookie("password", password);
    cookie_password.setMaxAge(30);
    resp.addCookie(cookie_username);
    resp.addCookie(cookie_password);
}

然后,我们修改一下默认的请求地址,现在一律通过http://localhost:8080/yyds/login进行登陆,那么我们需要添加GET请求的相关处理:

@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    Cookie[] cookies = req.getCookies();
    if(cookies != null){
        String username = null;
        String password = null;
        for (Cookie cookie : cookies) {
            if(cookie.getName().equals("username")) username = cookie.getValue();
            if(cookie.getName().equals("password")) password = cookie.getValue();
        }
        if(username != null && password != null){
            //登陆校验
            try (SqlSession sqlSession = factory.openSession(true)){
                UserMapper mapper = sqlSession.getMapper(UserMapper.class);
                User user = mapper.getUser(username, password);
                if(user != null){
                    resp.sendRedirect("time");
                    return;   //直接返回
                }
            }
        }
    }
    req.getRequestDispatcher("/").forward(req, resp);   //正常情况还是转发给默认的Servlet帮我们返回静态页面
}

现在,30秒内都不需要登陆,访问登陆页面后,会直接跳转到time页面。

现在已经离我们理想的页面越来越接近了,但是仍然有一个问题,就是我们的首页,无论是否登陆,所有人都可以访问,那么,如何才可以实现只有登陆之后才能访问呢?这就需要用到Session了。

Cookie缺点

  • Cookie 在 HTTP 消息中是明文传输,没有加密
  • Cookie 存储于浏览器,可以被篡改和攻击
  • Cookie 大小受到限制(通常限制为50 个,每个不超过4KB)

Session

什么是Session?

Session 是一种存储在服务器端用于记录客户端状态的机制。

Session工作原理

  • 客户端首次请求服务器:服务器端会生成一个 Session ID 自己先保存下来,再发送给客户端,客户端收到后会将它保存在 Cookie 中
  • 客户端再次请求服务器:客户端会把请求连同该 Session ID 一同提交给服务器,服务器检查该 Session ID,以此来辨认用户状态。

session的生命周期

Session何时生效:

Sessinon 在用户访问首次访问服务器时创建,需要注意只有访问JSP、Servlet等程序时才会创建Session,只访问 HTML、IMAG 等静态资源并不会创建 Session,可调用 request.getSession(true) 强制生成 Session

Session何时失效:

服务器会把长时间没有活动的 Session 从服务器内存中清除,此时 Session 便失效。Tomcat 中Session 的默认失效时间为 30 分钟。

实例测试

那么现在,我们在用户登录成功之后,将用户对象添加到Session中,只要是此用户发起的请求,我们都可以从HttpSession中读取到存储在会话中的数据:

HttpSession session = req.getSession();
session.setAttribute("user", user);

同时,如果用户没有登录就去访问首页,那么我们将发送一个重定向请求,告诉用户,需要先进行登录才可以访问:

HttpSession session = req.getSession();
User user = (User) session.getAttribute("user");
if(user == null) {
    resp.sendRedirect("login");
    return;
}

在访问的过程中,注意观察Cookie变化。

Session并不是永远都存在的,它有着自己的过期时间,默认时间为30分钟,若超过此时间,Session将丢失,我们可以在配置文件中修改过期时间:

<session-config>
    <session-timeout>1</session-timeout>
</session-config>

我们也可以在代码中使用invalidate方法来使Session立即失效:

session.invalidate();

现在,通过Session,我们就可以更好地控制用户对于资源的访问,只有完成登陆的用户才有资格访问首页。

Session缺点

  • 基于 Cookie 的机制容易被 CSRF
  • 如果是分布式部署,需要做多机共享 Session 机制
  • 存储在服务端,占用资源

两者对比

  • Cookie 机制是通过检查客户身上的“通行证”来确认客户身份

  • Session 机制是通过检查服务器上的“客户明细表”来确认客户身份

Cookie Session
存储位置 客户端 服务端
存储大小 有限制 无限制
跨域支持 支持 不支持
存储信息 不加密 加密
生命周期 可自定义 不可自定义

面试题

一般是通过 Cookie 来保存 SessionID ,假如你使用了 Cookie 保存 SessionID 的方案的话, 如果客户端禁用了 Cookie,那么 Session 就无法正常工作。

但是,并不是没有 Cookie 之后就不能用 Session 了,比如你可以将 SessionID 放在请求的 url 里面https://javaguide.cn/?Session_id=xxx 。这种方案的话可行,但是安全性和用户体验感降低。当然,为了你也可以对 SessionID 进行一次加密之后再传入后端。

参考

Sponsor❤️

您的支持是我不断前进的动力,如果您感觉本文对您有所帮助的话,可以考虑打赏一下本文,用以维持本博客的运营费用,拒绝白嫖,从你我做起!🥰🥰🥰

支付宝 微信

文章作者: 简简
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 简简 !
评论
填上邮箱会收到评论回复提醒哦!!!
 上一篇
Ajax & Axios & Json Ajax & Axios & Json
AJAX (Asynchronous JavaScript And XML):异步的 JavaScript 和 XML。Axios 本质上是对原生的 Ajax 进行封装,简化代码
2022-04-21
下一篇 
Servlet Servlet
Servlet 是 Server Applet 的缩写,译为“服务器端小程序”,是一种使用 Java 语言来开发动态网站的技术。
2022-04-19
  目录