• cookie,session,Token 这些你都知道吗?


    前言

    无状态的HTTP协议

    一天,你有个需求,你要去超市买一瓶可乐。 到了超市买了可乐,你告诉售货员,下次给我准备下雷碧,我下次来拿。 第二次,你去超市拿雷碧,售货员说他不记得你什么时候说要准备雷碧。 这次你学聪明了,售货员给你写了个纸条,上面有超市的章印,下次你带着纸条来,买上了超市 给你准备的雷碧

    1. http超文本传输协议:Hyper Text Transfer Protocol
    2. http不会为了下次连接所需要的信息而维护这次连接
    3. 就像你去超市买可乐,买完就结束了,它不会记录你告诉他的信息,不记录你下次还要买雷碧,下次他也不知道你曾经来过
    4. 顾名思义无状态是指,当浏览器发送请求给server的时候,server响应,可是同一个浏览器再发送请求给server的时候,他会响应,可是他不知道你就是刚才那个浏览器,简单地说,就是server不会去记得你,所以是无状态协议。而DNS是有状态协议。
    5. 再举个例子,像购物车,你买东西加入购物车,如果http协议的话,刷新页面,购物车就为空了。

    一、cookie


    cookie 是一个非常具体的东西,指的就是浏览器里面能永久存储的一种数据。跟服务器没啥关系,仅仅是浏览器实现的一种数据存储功能。

    cookie由服务器生成,发送给浏览器,浏览器把cookie以KV形式存储到某个目录下的文本文件中,下一次请求同一网站时会把该cookie发送给服务器。由于cookie是存在客户端上的,所以浏览器加入了一些限制确保cookie不会被恶意使用,同时不会占据太多磁盘空间。所以每个域的cookie数量是有限制的。

    如何设置

    客户端设置

    document.cookie = "name=xiaoming; age=12 "
    
    • 1
    • 客户端可以设置cookie的一下选项: expires, domain, path, secure(只有在https协议的网页中, 客户端设置secure类型cookie才能生效), 但无法设置httpOnly选项

    设置cookie => cookie被自动添加到request header中 => 服务端接收到cookie

    服务端设置

    不管你是请求一个资源文件(如html/js/css/图片), 还是发送一个ajax请求, 服务端都会返回response.而response header中有一项叫set-cookie, 是服务端专门用来设置cookie的;

    • 一个set-cookie只能设置一个cookie, 当你想设置多个, 需要添加同样多的set-cookie
    • 服务端可以设置cookie的所有选项: expires, domain, path, secure, HttpOnly

    Cookie,SessionStorage,LocalStorage

    HTML5提供了两种本地存储的方式 sessionStorage 和 localStorage

    二、Session

    Cookie是存储在客户端方,Session是存储在服务端方,客户端只存储SessionId

    在上面我们了解了什么是Cookie,既然浏览器已经通过Cookie实现了有状态这一需求,那么为什么又来了一个Session呢?这里我们想象一下,如果将账户的一些信息都存入Cookie中的话,一旦信息被拦截,那么我们所有的账户信息都会丢失掉。所以就出现了Session,在一次会话中将重要信息保存在Session中,浏览器只记录SessionId一个SessionId对应一次会话请求。

     1@RequestMapping("/testSession")
     2@ResponseBody
     3public String testSession(HttpSession session){
     4    session.setAttribute("testSession","this is my session");
     5    return "testSession";
     6}
     7
     8
     9@RequestMapping("/testGetSession")
    10@ResponseBody
    11public String testGetSession(HttpSession session){
    12    Object testSession = session.getAttribute("testSession");
    13    return String.valueOf(testSession);
    14}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    这里我们写一个新的方法来测试Session是如何产生的,我们在请求参数中加上HttpSession session,然后再浏览器中输入http://localhost:8005/testSession进行访问可以看到在服务器的返回头中在Cookie中生成了一个SessionId。然后浏览器记住此SessionId下次访问时可以带着此Id,然后就能根据此Id找到存储在服务端的信息了


    此时我们访问路径
    http://localhost:8005/testGetSession,发现得到了我们上面存储在Session中的信息。那么Session什么时候过期呢?

    • 客户端:和Cookie过期一致,如果没设置,默认是关了浏览器就没了,即再打开浏览器的时候初次请求头中是没有SessionId了。
    • 服务端:服务端的过期是真的过期,即服务器端的Session存储的数据结构多久不可用了,默认是30分钟。

    在这里插入图片描述
    既然我们知道了Session是在服务端进行管理的,那么或许你们看到这有几个疑问,Session是在在哪创建的?Session是存储在什么数据结构中?接下来带领大家一起看一下Session是如何被管理的。

    Session的管理是在容器中被管理的,什么是容器呢?Tomcat、Jetty等都是容器。接下来我们拿最常用的Tomcat为例来看下Tomcat是如何管理Session的。在ManageBase的createSession是用来创建Session的。

    1@Override
     2public Session createSession(String sessionId) {
     3    //首先判断Session数量是不是到了最大值,最大Session数可以通过参数设置
     4    if ((maxActiveSessions >= 0) &&
     5            (getActiveSessions() >= maxActiveSessions)) {
     6        rejectedSessions++;
     7        throw new TooManyActiveSessionsException(
     8                sm.getString("managerBase.createSession.ise"),
     9                maxActiveSessions);
    10    }
    11
    12    // 重用或者创建一个新的Session对象,请注意在Tomcat中就是StandardSession
    13    // 它是HttpSession的具体实现类,而HttpSession是Servlet规范中定义的接口
    14    Session session = createEmptySession();
    15
    16
    17    // 初始化新Session的值
    18    session.setNew(true);
    19    session.setValid(true);
    20    session.setCreationTime(System.currentTimeMillis());
    21    // 设置Session过期时间是30分钟
    22    session.setMaxInactiveInterval(getContext().getSessionTimeout() * 60);
    23    String id = sessionId;
    24    if (id == null) {
    25        id = generateSessionId();
    26    }
    27    session.setId(id);// 这里会将Session添加到ConcurrentHashMap中
    28    sessionCounter++;
    29
    30    //将创建时间添加到LinkedList中,并且把最先添加的时间移除
    31    //主要还是方便清理过期Session
    32    SessionTiming timing = new SessionTiming(session.getCreationTime(), 0);
    33    synchronized (sessionCreationTiming) {
    34        sessionCreationTiming.add(timing);
    35        sessionCreationTiming.poll();
    36    }
    37    return session
    38}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38

    到此我们明白了Session是如何创建出来的,创建出来后Session会被保存到一个ConcurrentHashMap中。可以看StandardSession类。

    1protected Map<String, Session> sessions = new ConcurrentHashMap<>();
    
    • 1

    到这里大家应该对Session有简单的了解了。

    Session是存储在Tomcat的容器中,所以如果后端机器是多台的话,因此多个机器间是无法共享Session的,此时可以使用Spring提供的分布式Session的解决方案,是将Session放在了Redis中。

    三、Token

    1、什么的Token

    Token是首次登陆时由服务器下发,作为客户端进行请求的一个令牌,当交互时用于身份验证的一种验证机制,当第一次登录后,服务器生成一个Token便将此Token返回给客户端,以后客户端只需带上这个Token前来请求数据即可,无需再次带上用户名和密码。

    2、Token的作用

    Token完全由应用程序进行管理,所以它可以避开同源策略
    Token可以避免CSRF(跨站请求访问)攻击
    Token可以是无状态的,可以在多个服务器之间共享
    使用Token减轻服务器的压力,减少频繁的查询数据库。

    3、Token身份认证的过程

    用户通过用户名和密码发送请求
    程序进行验证
    程序返回一个签名的token给客户端
    客户端进行存储token,并且用于每次发送请求
    服务器端进行验证token并返回数据

    4、Token的存储位置

    存储在localStorage中
    // 存储token到ls const { token } = res.data; window.localStorage.setItem(‘jwtToken’, token);
    优点:没有时间限制的存储,会一直存放在浏览器中。

    缺点:由于LocalStorage 可以被 javascript 访问,所以容易受到XSS攻击。所以可以在一个统一的地方复写请求头,让每次请求都在header中带上这个token, 当token失效的时候,后端会返回401,这个时候在你可以在前端代码中操作返回登陆页面,清除localstorage中的token。(适用于 ajax请求或者 api请求,可以方便的存入 localstorage)另外,需要应用程序来保证Token只在HTTPS下传输。

    存储在cookie中
    优点:可以防止 csrf攻击,因为 csrf只能在请求中携带 cookie,而这里必须从 cookie中拿出相应的值并放到 authorization 头中。实际上cookie不能跨站(同源策略)被取出,因此可以避免 csrf 攻击。(适用于 ajax请求或者 api请求,可以方便的设置 auth头)

    5、Token处理过期时间

    在我的vue项目中,我将Token存储在了localStorage中,有处理过Token过期,我是这样做的:

    created() {
        if (localStorage.jwtToken) {
          const decoded = jwt_decode(localStorage.jwtToken);
          //获取当前时间
          const currentTime = Date.now() / 1000;
          //检测token是否过期
          if (decoded.exp < currentTime) {
            //跳转到登录页面
            this.$router.push('/login');
            //其他处理
            ...
          } else{
            //没有过期的处理;
          }   
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    四、总结

    cookie,session,Token没有绝对的好与坏之分,只要还是要结合实际的业务场景和需求来决定采用哪种方式来管理回话,当然也可以三种都用。

    学习资源分享

    最后感谢每一个认真阅读我文章的人,看着粉丝一路的上涨和关注,礼尚往来总是要有的,虽然不是什么很值钱的东西,如果你用得到的话可以直接拿走

    这些资料,对于想进阶【自动化测试】的朋友来说应该是最全面最完整的备战仓库,这个仓库也陪伴我走过了最艰难的路程,希望也能帮助到你!凡事要趁早,特别是技术行业,一定要提升技术功底。希望对大家有所帮助…….

    加入下方我的交流群免费获取!

  • 相关阅读:
    邦芒宝典:工作中如何自我定位
    C++中的mutable关键字
    typescript变量
    vue3入门,其实吧,压力也没那么大
    解决Cannot resolve symbol ‘MybatisPlusInterceptor‘
    【DataRoom】- 基于VUE的开源的大屏可视化设计器
    Oracle EBS API创建AP发票报错:ZX_TAX_STATUS_NOT_EFFECTIVE和ZX_REGIME_NOT_EFF_IN_SUBSCR-
    forplo | 冲冲冲!这个画森林图的包好flexible哦~
    【微信小程序从入门到精通(项目实战)】——微电影小程序
    SQL进阶(五):With 函数 vs 视图函数
  • 原文地址:https://blog.csdn.net/weixin_54696666/article/details/125624239