• JWT详解


    1、什么是token,解决了什么问题?

    token 就是常说的 “令牌”,本质上是全局唯一的字符串,用来唯一识别一个客户端,解决了session依赖单个web服务器的问题。单体应用时,用户的会话信息保存在session中,session存在于服务器端的内存中,但是对于使用服务器集群的情况,比如用nginx做负载均衡,采用轮询的方式访问服务器集群时,就会出现一种情况:用户A登录了服务器1,此时用户的session保存在了服务器1中,但是第二次请求被分配到了服务器2,由于服务器2没有用户的session信息,所以用户A就还要再次登录,这样用户的体验肯定不好。
    对于 cookie和session的机制,在请求中根据cookie中的jesessionid来自动找到服务器端的session,从而从session中取出当前用户的会话信息。

    Session session = request.getSession();// 获取session
    session.getAttribute("user") // 获取session中的用户信息
    
    • 1
    • 2

    我们可以模拟cookie和session的这种机制:
    1)cookie中是根据jesessionid来找到服务器端的session的,jesessionid就是一个全局唯一的随机字符串,我们也可以生成一个全局唯一的字符串比如使用UUID或时间戳加随机字符串的形式;
    2)web服务器在内存中存储所有的session,每个session都有一个唯一的id标识,value就是session本身,session里面又有好多键值对。数据在内存中、key-value形式的、value里面又有好多键值对,这简直就是对redis的哈希表十分准确的描述啊,所以我们可以使用redis的哈希类型来模型服务器端session。
    3) 有了客户端的随机字符串,有了服务器端的会话信息存储, 接下来就是匹配:
    cookie和session机制中是根据cookie中的jesessionid来自动找到服务器端的session,说是自动查找,其实是我们调用了他封装好的方法request.getSession()获取的,这个request中就包含了本次请求的所有信息,而调用的getSession()方法中,肯定做了这件事——获取请求头中cookie的jesessionid的值,根据这个值到服务器的内存中找到对应的session并返回。
    我们可以在用户第一次请求该web服务器时或是用户登录该web服务器时,生成一个全局唯一的token返回给前端存储,同时将该用户信息存到redis中并设置有效期,之后每次请求中都在请求头中带着这个token,服务器端根据这个token到redis中查找对应的用户信息,即得到了我们所说的 “session”。
    流程:
    1)用户使用用户名密码请求服务器(这个过程的一般是HTTP的POST请求,也可用SSL加密的https协议);
    2)服务器进行验证用户的信息;
    3)服务器通过验证返回发给用户一个token(并可以将用户的id等其他信息作为 JWT Payload(负载))将其与头部分别进行 Base64编码拼接后签名形成一个JWT(Token),形成的JWT字符串(token)就是header.payload.signature组成的编码后的字符串的形式;
    4)客户端存储token值,并在每次请求的时候附带上这个token值(前端会将JWT(token)字符串放入Header中的 Authorization位);
    5)服务器验证token,并返回数据(比如验证JWT的有效性:签名是否正确、token是否过期、检查token的接收方是否是自己(用户客户端))
    在这里插入图片描述

    2、JWT的组成(令牌组成)

    JWT(token)是由三段信息构成,Header.PayLoad.Signature;

    2.1 令牌组成

    1. 标头 (Header)
    2. 有效载荷 (PayLoad)
    3. 签名 (Signature)

    2.2 Header

    {
      "alg":"HS256",
      "typ":"JWT"
    }
    
    • 1
    • 2
    • 3
    • 4

    2.3 PayLoad

    令牌的第二部分是有效负载,其中包含声明(即有关实体通常是用户的一些不太敏感的信息【如密码等】,也是可以通过Base64编码,正因为这种不太安全,所以负载中尽量不会存放敏感的数据)

    {
      “uid” : "123456",
      "uname":"mary"
    }
    
    • 1
    • 2
    • 3
    • 4

    2.4 Signature

    标头和负载都可以通过Base64编码的,前端也可以解码获取真实信息,而签名是需要编码后的Header和PayLoad 以及我们提供的一个密钥,然后 使用标头中的签名算法如“HS256”进行签名,签名的作用是保证JWT没有被篡改过。

    1.var encodedString = base64UrlEncode(header) + '.' + base64UrlEncode(payload);
    
    2.var signature = HMACSHA256(encodedString, 'secret');
    
    • 1
    • 2
    • 3

    注意: secret是保存在服务器端的,jwt的签发生成也是在服务器端的,secret就是用来进行jwt的签发和jwt的验证,所以,它就是你服务端的私钥,在任何场景都不应该流露出去。一旦客户端得知这个secret, 那就意味着客户端是可以自我签发jwt了。
    实际上就是对头部以及负载内容进行签名,防止内容被篡改(因为Base64可以被解码)。如果有人对头部和负载内容解码修改后,再进行编码,签名形成新的 JWT那么服务器端就会判断出新的头部和负载形成的签名和 JWT(Header.PayLoad.Signature) 本身带的签名是不一致的。

  • 相关阅读:
    矩阵相乘详解
    Redis-Key的操作
    本地搭建CFimagehost私人图床——“cpolar内网穿透”
    界面组件DevExpress Reporting v23.1亮点 - 全新升级报表查看器
    音视频学习 - Qt6.3.1版本下实现屏幕截图功能
    图解LeetCode——1408. 数组中的字符串匹配(难度:简单)
    【Linux系列】离线安装openjdk17的rpm包
    基于springboot的校园店铺系统
    golang查看CPU使用率与内存及源码中的//go:指令
    【DS基础】栈与队列
  • 原文地址:https://blog.csdn.net/jiangyyyeff/article/details/128097871