万字解析 Cookie&Session&Token(让你彻底理解 Cookie、Session、Token 到底是什么?能用来做什么?)

Cookie&Session详解

  • 1. Why We Need Cookie&Session ?
  • 2. Cookie
    • 2.1 Cookie简介
    • 2.2 Cookie机制
    • 2.3 Cookie的有效时间
    • 2.4 Cookie源码分析及案例
  • 3. Session
    • 3.1 Session简介
    • 3.2 Session机制
    • 3.3 Session的有效时间
    • 3.4 共享数据方式和作用域对象
    • 3.5 HttpSession源码分析及案例
    • 3.6 总结:Cookie和Session的区别
  • 4. 登录验证 Token
    • 4.1 为什么需要Token
    • 4.2 Token简介
    • 4.3 Token验证登录的流程
    • 4.4 Token的存储
    • 4.5 Token的优缺点:
  • 5. 登录验证解决方案 JWT
    • 5.1 JWT简介
    • 5.2 JWT格式组成
    • 5.3 JWT的验证流程

写的不好,仍需努力。仅供参考,欢迎交流~

1. Why We Need Cookie&Session ?

HTTP是一种无状态协议,每一个HTTP请求都是相互独立的,当前请求不会记录上一次请求,即每次服务端接收到客户端的请求都是一个全新的请求,因此服务器单从网络连接上无法知道客户端的身份。

在程序中,一个用户的所有请求操作都应该属于同一个会话,另一个用户的所有请求应该属于另一个会话,会话追踪很重要!!!例如: 用户A在网上购物时购买的所有商品都应该放在用户A的购物车中,不论是什么时候购买的,这都属于同一个会话,不能将商品放入用户B的购物车内。

而Web应用程序是使用HTTP协议传输数据的,HTTP协议是无状态的协议。一旦数据交换完毕,客户端与服务器端的连接就会关闭,再次交换数据需要建立新的连接, 这就意味着服务器无法从连接上跟踪会话。例如: 用户A将一件商品放入购物车后,用户再次打开购物网站进行购物,此时服务器无法判断该购买行为属于哪一个用户的会话,因此我们需要跟踪会话。

SessionCookie 能够 弥补HTTP无状态的不足并且跟踪会话。


2. Cookie

2.1 Cookie简介

Cookie技术是客户端的解决方案, Cookie就是服务器发送给客户端的特殊信息,这些特殊信息以文本文件的形式存放在客户端,客户端向服务器发送请求时会带上这些特殊信息。相当于:服务器给客户端颁发了一个 “通行证”,每个客户端一个,无论哪一个客户端访问服务器都必须携带上自己的 “通行证”;这样服务器就能够通过这个 “通行证” 来确认客户端的身份了。

  1. 用户从客户端向服务器发送请求,用户会提供"个人信息"并且提交至服务器;
  2. 服务器在向客户端响应数据的同时也会将这个"个人信息"保存到Cookie中回传给客户端,Cookie存放于响应头(Response Header)
  3. 当客户端浏览器接收到服务器响应之后,客户端会将这个Cookie保存起来;
  4. 此后,客户端再次向服务器发送请求时,都会将相应的Cookie一并发送给服务器,Cookie存放于请求头(Request Header)
  5. 服务器检查到Cookie后,通过Cookie来辨认用户,服务器还可以根据需要来修改Cookie的内容。

有了Cookie技术后,服务器接收到来自客户端的请求后,能够通过分析请求头中的Cookie得到客户端特有的信息,从而动态的生成与该客户端相对应的内容。

  1. Cookie的不可跨域名性:Cookie可以实现同域名下的跨页面,但不能跨域名;同一个网站中的所有页面共享一套Cookie,可以设置有效期限。

  2. Cookie的存储数据量有限:Cookie存储空间为4KB左右,因此Cookie只能存储一些小数据量的数据。

  3. Cookie的生存时间

    • 整个会话期间:浏览器会将Cookie保存在内存中,浏览器关闭时就自动清除这个Cookie。
    • 持久化到客户端硬盘:浏览器关闭Cookie也不会被清除,下次打开浏览器访问该网站时,Cookie会再次自动发送到服务器。

2.2 Cookie机制

HTTP的两个头部负责 设置及发送Cookie,分别是 Set-CookieCookie

当服务器返回给客户端一个HTTP响应时,如果包含 Set-Cookie头部,就会指示客户端建立一个Cookie并且在后续的HTTP请求中自动发送这个Cookie头部到服务器端,直到这个Cookie过期。

万字解析 Cookie&Session&Token(让你彻底理解 Cookie、Session、Token 到底是什么?能用来做什么?)_第1张图片(数字与图中对应)

客户端发送一个HTTP请求到服务器(没有Cookie信息);
服务器发送一个HTTP响应到客户端,包含 Set-Cookie: header(服务端生成Cookie信息);
客户端发送一个HTTP请求到服务器,包含 Cookie: header(自动发送客户端保存的Cookie信息);
服务器根据Cookie信息动态的发送一个HTTP响应到客户端。


2.3 Cookie的有效时间

在默认情况下,浏览器将Cookie对象保存在内存中,只要浏览器关闭,Cookie对象就会被销毁。

在手动设置的情况下,可以要求浏览器将接收到的Cookie保存在客户端计算机的硬盘上,同时需要指定Cookie在硬盘上的存活时间。

  • 在Cookie的存活时间范围内,关闭浏览器、关闭客户端计算机或者关闭服务器,都不会导致Cookie销毁。
  • 在Cookie的存活时间截至后,Cookie会被自动从硬盘上删除。
  • 实现方法:
    // 以秒为单位设置 cookie 的最长期限
    // 正 - cookie经过多少秒后过期;负 - cookie不会被永久存储,退出浏览器时被删除;0 - 删除cookie
    public void setMaxAge(int expiry) {
        maxAge = expiry;
    }
    

2.4 Cookie源码分析及案例

一个Cookie对象只能存放一个键值对,这个键值对中存储中的数据都是String类型,并且name属性不能为中文。Cookie类中定义了很多属性和很多操作属性的方法,我们通过一个案例来熟悉。

private String name;  // 键
private String value;  // 值
private String comment;  // 注释,用于描述Cookie的用途
private String domain;  // 可访问Cookie的域名
private int maxAge = -1;  // Cookie的有效时间
private String path;  // 可访问Cookie的路径;将path设为/,Cookie可以被网站下所有页面访问。
private boolean secure;  // true - https;false - http;默认为false。
private int version = 0;  // Cookie的版本

案例:使用Cookie实现 记录上次的访问时间

public class CookieUtil {
    public static Cookie getCookieByName(String name, Cookie[] cookies){
        if (cookies != null){
            for(Cookie cookie : cookies){
                if (name.equals(cookie.getName())){
                    return cookie;
                }
            }
        }
        return null;
    }
}
@WebServlet("/cookieTest")
public class CookieController extends HttpServlet{
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        response.setContentType("text/html; charset=UTF-8");
        PrintWriter out = response.getWriter();

        // 获取Cookie
        Cookie[] cookies = request.getCookies();
        // 通过CookieUtil获取指定名称的Cookie
        Cookie cookie = CookieUtil.getCookieByName("record", cookies);
        // 判断cookie是否为null
        if (cookie == null){
            out.print("第一次访问!");
        }else {
            long lastView = Long.parseLong(cookie.getValue());
            out.print("上次访问时间为:" + new Date(lastView).toLocaleString());
        }

        // 记录当前访问时间并存入Cookie,注意:Cookie的键值必须为String类型
        Cookie c = new Cookie("record", System.currentTimeMillis() + "");
        // 设置Cookie的有效时间
        c.setMaxAge(60);
        // 将Cookie写入响应头,推回到客户端
        response.addCookie(c);
    }
}

3. Session

3.1 Session简介

Session技术是服务端的解决方案, Session又称会话,是指:客户端浏览器访问服务器的时候,服务器将客户端的信息以某种形式记录在服务器上;客户端浏览器再次访问时,服务器只需要从这些记录中就能查找到该客户端的状态了。

如果说 Cookie机制是通过检查客户身上的“通行证” 来确定客户身份的话,那么 Session机制就是通过检查服务器上的“客户明细表” 来确认客户身份。Session相当于程序在服务器上建立的一份客户档案,客户来访的时候只需要查询客户档案表就可以了。在Web容器中有一个session列表,其中维护了大量的session对象。

一方面,可以将 客户端浏览器与服务器之间一系列交互的动作 称为一个 Session(会话);另一方面,Session指的是 服务器端为客户端所开辟的存储空间,该空间用于保存客户端状态信息。

Session保存在服务端,为获得更高的存取速度,服务器一般会将Session存放在内存中,且每个用户都有一个独立的Session;如果Session内容过于复杂,当大量客户访问服务器的时候可能会导致内存溢出。因此,Session中保存的信息应该尽量精简。


3.2 Session机制

  1. 第一次请求,请求头中没有 JSESSIONID的Cookie;访问到对应资源后,服务器会调用HttpServletRequestgetSession() 方法创建 HttpSession 对象,同时会为该Session生成一个唯一的session id,这个id在随后的请求中用于重新获得已创建的Session;
  2. 服务器响应数据到浏览器,同时 Set-Cookie: jsessionid=jsessionid值(32位长的字符串)
  3. 再次请求时,HTTP请求头中就有一个 Cookie: jsessionid=32位长的字符串;服务器通过 getSession(true) 方法获取到在服务器中 jsessionid对应的Session对象;有就使用,没有就创建。
  • 注意:getSession(boolean create) 方法中create值的分析

    • getSession()getSession(true):获取session对象,如果没有session对象,就新建一个session对象。

    • getSession(false):获取session对象,如果没有session对象,则返回null。

万字解析 Cookie&Session&Token(让你彻底理解 Cookie、Session、Token 到底是什么?能用来做什么?)_第2张图片

URL地址重写(顺带一提)

当客户端不支持Cookie时,我们还想使用Session机制的话,解决方案就是 URL地址重写

URL地址重写的原理是:在访问资源路径后、请求参数前,添加用户的 Session id,即 “;jsessionid=XXX”,其中XXX为Session id的值;服务器能够解析重写后的URL获取 Session id,这样即使客户端不支持Cookie,也可以使用Session来记录用户状态。

假设访问:/web/urlServlet?name=mike&age=22

URL地址重写后的访问路径:/web/urlServlet;jsessionid=0CCD096E7F8D97B0BE608AFDC3E1931E?name=mike&age=22


3.3 Session的有效时间

为什么要有这个Session有效时间/失效时间/超时时间?

  • 浏览器关闭后,Session不会消失;除非程序通知服务器删除Session,否则服务器会一直保留。浏览器不会主动在其关闭时通知服务器它即将关闭,因此服务器根本不会知道服务器已经关闭了。而浏览器关闭后,保存Session id的Cookie会消失,再次连接服务器后也无法找到原来的Session;这时我们需要通过某些手段将原来的Session id发送给服务器,才能找到原来的Session。(将Cookie持久化到硬盘上,或者改写HTTP请求头,将原来的Session id发送给服务器)
  • Session生成后,用户访问一次,服务器就会更新一次Session的最后访问时间,并维护该Session。用户每访问一次Session,服务器都认为该用户的Session"活跃(active)"了一次。越来越多的用户访问服务器,随之Session也会越来越多。
  • 正是因为浏览器关闭不会导致Session被删除 和 为防止内存溢出,服务器会把长时间内没有活跃的Session从内存删除。这个时间就是Session的有效时间。如果超过了有效时间没访问过服务器,Session就自动失效了。

Session对象默认30分钟销毁。

Session的有效时间为 maxInactiveInterval 属性,可以通过 getMaxInactiveInterval() 方法获取,通过 setMaxInactiveInterval(longinterval) 修改。

HttpSession session = request.getSession();
int maxInactiveInterval = session.getMaxInactiveInterval();
session.setMaxInactiveInterval(60*60);
// 单位是秒

Session的超时时间也可以在web.xml中修改。

<session-config>
	<session-timeout>60session-timeout>
    
session-config>

另外,通过调用Session的invalidate()方法可以使Session失效。

session.invalidate();

3.4 共享数据方式和作用域对象

  1. 四大共享数据方式:

    • ServletContext: 生命周期为服务器启动到关闭;数据由所有用户共享。
    • HttpServletReuqest: 生命周期为一次请求;无法重定向后再次获取内容。
    • Cookie: 存储在客户端浏览器,客户端可以对Cookie进行查看和修改,不安全。
    • HttpSession: 存储在服务器,默认30分钟销毁,适合用户登录后的信息共享。
  2. 三大域对象: ServletContext(应用作用域)、HttpSession(会话作用域)、HttpServletRequest(请求作用域)

    • 数量: 一个用户可拥有多个request对象;一个用户只有1个session对象;所有用户共用一个 application对象。

    • 范围排序:

      • application > session > request
      • application 应用域对象,可以完成跨会话的数据关系,但是这些会话必须在同一个应用中。
      • session 会话域对象,可以完成跨请求的数据共享,但是这些请求必须在同一个会话中。
      • request 请求域对象,可以完成跨 Servlet 的数据共享,但是这些 Servlet 必须在同一个请求中。
    • 使用原则: 由小到大尝试,优先使用范围小的。


3.5 HttpSession源码分析及案例

public interface HttpSession {
    
    public String getId();  // 获取SessionID

    public void setMaxInactiveInterval(int interval);  // 设置Session的有效时间,单位为秒
    public int getMaxInactiveInterval();  // 获取Sessison的有效时间
	public void invalidate();  // 设置Session立即失效
    
    public void setAttribute(String name, Object value);  // 以键值对的形式用session保存数据
    public Object getAttribute(String name);  // 通过name获取数据
    public void removeAttribute(String name);  // 通过name删除数据
}

案例:利用Session实现免登录

LoginServlet

@WebServlet("/login")
public class LoginServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        // 获取请求参数
        String username = request.getParameter("username");
        String password = request.getParameter("password");
        if ("Sun".equals(username) && "123".equals(password)){
            // 如果账号密码正确,跳转到 success.jsp,并将提示消息存入session会话域
            // 即使关闭浏览器再打开,直接访问 /web/success.jsp 也能访问到,实现了免登录
            HttpSession session = request.getSession();
            session.setAttribute("msg1", "登录成功!");
            response.sendRedirect("/web/success.jsp");
        }else {
            request.setAttribute("msg2", "密码或账号不正确~");
            request.getRequestDispatcher("index.jsp").forward(request, response);
        }
    }
}

login.jsp

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head><title>logintitle>head>
<body>
    ${msg2}<br>
    <form action="/web/login">
        username: <input type="text" name="username"><br>
        password: <input type="password" name="password"><br>
        <input type="submit" value="log in">
    form>
body>
html>

success.jsp

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head><title>logged intitle>head>
<body>
	${msg2}
body>
html>

测试结果:
万字解析 Cookie&Session&Token(让你彻底理解 Cookie、Session、Token 到底是什么?能用来做什么?)_第3张图片


3.6 总结:Cookie和Session的区别

HttpSession Cookie
存储位置 存放在服务器的 Session列表(服务器的内存中) 存放在客户端浏览器缓存中 或 客户端计算机的硬盘中
数据类型 Session 对象可以存储任意类型的数据Object Cookie对象存储的共享数据类型只能是String
数据数量 Session 对象使用Map集合存储共享数据,可存储任意数量的共享数据 一个Cookie对象只能存储一个共享数据
存储容量 Session没有大小限制,和服务器的内存大小相关 单个Cookie保存的数据不能超过4K,
很多浏览器限制一个站点最多保存20个Cookie
安全性 Session更加安全(保存用户重要的信息) Cookie的信息是可见的且易于本地编辑的,容易引起安全问题
(保存用户不重要的信息)
销毁时机 浏览器关闭后Session不会被销毁;除非程序通知服务器删除Session 或者 Session的有效时间结束 存放在客户端浏览器缓存中:关闭浏览器时销毁
存放在客户端计算机硬盘中:不会被销毁直到Cookie的有效时间结束

4. 登录验证 Token

4.1 为什么需要Token

还是一样的问题,我们为什么需要Token?不是已经有了 Cookie 和 Session吗?因为在真实的开发环境中有这样的两种情景:单机Tomcat应用分布式应用;在单机Tomcat应用中Session自然是够用了,但是在分布式应用中存在一个 Session共享问题

分布式应用中的Session共享

  1. 真实的应用不大可能单节点部署,所以就需要解决多节点登录Session共享的问题;假设有三台服务器,第一次请求的Session保存在 服务器01,第二次请求被 服务器02 处理;尽管是同一个客户发送的请求,但是在 服务器02 中无法获取到 服务器01 中的Session
  2. 尽管 Tomcat也支持Session共享,但是存在广播风暴(当一个用户发送一次请求,服务器1将Session同步到服务器2和服务器3);当用户数量很大的时候,占用资源严重(不推荐);
  3. 使用Redis存储Token:服务端使用UUID随机生成64位或128位的Token,放入Redis中,并且返回给客户端并存储;用户每次访问都携带此Token,服务器去Redis中校验是否有此用户即可。

4.2 Token简介

Token,即令牌,也可以理解为暗号;即在数据传输前需要对暗号,不同的暗号被授予不同的数据操作;使用Token的身份验证方法,服务器不再需要存储用户的登录记录。

4.3 Token验证登录的流程

 1. 用户使用账号和密码请求登录;
 2. 服务器收到请求,通过对比数据库中的信息验证用户名和密码;
 3. 验证成功后签发一个Token,并将这个Token返回给客户端;
 4. 客户端收到这个Token后将其存储起来,比如存到Cookie、LocalStorage或SessionStorage中;
 5. 客户端每次向服务器请求资源时都带上这个Token(请求头中);
 6. 服务器接收到请求,获取请求头中的Token,验证其有效性;
 7. 验证通过后,解析 Token 获取用户信息,根据用户信息进行其他逻辑操作,例如:根据用户权限允许其访问相关资源;
 8. 将客户端访问的目标资源响应给客户端。

4.4 Token的存储

 - Token可以存在数据库中,但是可能查询Token的时间过长导致Token丢失;
 - 为了避免查询实践过程,可以将Token放在内存中(用户量即使是百万级的也占不了多少内存)。

4.5 Token的优缺点:

  1. 支持跨域访问:Token是无法跨域的,而Token由于没有用到Cookie(前提是将Token放到请求头中),所以跨域后不会存在信息丢失问题;
  2. 无状态:Token机制在服务端不需要存储Session信息,因为Token自身包含了所有登录用户的信息(避免再次查库),所以可以减轻服务端压力;
  3. 更适用于移动端:当客户端是非浏览器平台时,Cookie是不被支持的,此时采用token认证方式会简单很多;
  4. Token的缺点:Token是经过Base64编码,所以可以解码,因此Token加密前的对象不应该包含敏感信息,如用户权限,密码等;

万字解析 Cookie&Session&Token(让你彻底理解 Cookie、Session、Token 到底是什么?能用来做什么?)_第4张图片

5. 登录验证解决方案 JWT

5.1 JWT简介

JWT 就是上述流程当中Token的一种具体实现方式,其全称是 Json Web Token;就是通过一定规范生成Token,然后通过解密算法逆向解密Token,获取用户信息。

通俗地说,JWT的本质就是一个字符串,它将用户信息保存到一个Json字符串中,然后进行编码后得到一个JWT Token,这个JWT Token带有签名信息,接收后可以校验是否被篡改,可以用于在各方之间安全地将信息作为Json对象传输。

5.2 JWT格式组成

  1. 头部 Header:描述签名使用的算法;
  2. 负载 Payload:描述加密对象的信息;包含一些规范,比如:签发者 iss、过期时间 exp、主题 sub等;
  3. 签名 Signature:对 Header 和 Payload两部分进行加密,防止别人拿到Token进行解密,篡改Token。

5.3 JWT的验证流程

  1. 首先,前端通过表单将用户名和密码发送到后端的接口;

  2. 后端核对用户名和密码成功后,将包含用户信息的数据作为JWT的Payload,将其与JWT Header分别进行Base64编码拼接后签名,形成一个JWT Token;

  3. 后端将JWT Token字符串作为登录成功的结果返回给前端;

  4. 前端在每次请求时将JWT Token放入HTTP请求头中的 Authorization属性中;

  5. 后端检查前端传过来的JWT Token,验证其有效性,比如检查签名是否正确、是否过期、Token的接收方是否是自己等;

  6. 验证通过后,后端解析出JWT Token中包含的用户信息,进行其他逻辑操作(一般是根据用户信息得到权限等),返回结果;

万字解析 Cookie&Session&Token(让你彻底理解 Cookie、Session、Token 到底是什么?能用来做什么?)_第5张图片

你可能感兴趣的:(JavaWeb,http,网络,java,javaweb,网络协议)