万字详细解释cookie,session,JWT(token)

  • 鉴权

    • 鉴权: 判断某用户是否有权访问服务器上的资源
    • 例如;
      • 用户需要登录之后才可以访问一些特定的界面
      • 某些页面需要token才可以访问
    • HTTP是无状态的
      • 早期的web只需要提供信息给访问者就行了,不需要记录状态,浏览器和服务器不能凭借HTTP协议来辨别请求的上下文
      • 服务器是不能识别浏览器发出的请求是否之前发过
    • 鉴权思路
      • 一种: 将身份信息保存在服务器.
        • 例如: 取快递的时候,报出快递号就可以查看是否有这个快递
        • 快递点就是服务器,自己是客户端
      • 二:种 将身份信息保存在客户端.
        • 例如: 游乐园门票,使用门票后,在门票上面打孔,但门票在自己手上
        • 自己是客户端,服务器是游乐园
    • 鉴权实现步骤
      • 怎么让服务器认识你,明白你是谁
      • 用户登录之后,得到能表明身份的凭证,下次请求或请求其他页面,带上这个凭证我就可以访问,不带凭证就不能访问,因为服务器不认识你
  • 鉴权的方式有三种(cookie,session,token)

    • cookie(甜饼)

      • 是存在于浏览器上的一种浏览器本地存储的方式, 存储一些不重要的信息,大小为4K
      • 同域名下的 cookie 不同标签页可以共享, 默认过期时间是浏览器关闭时
      • 进行 HTTP 请求时, 会自动带上浏览器全部的 cookie 发给后台, 后台也可以获取 cookie, 设置可以在响应时, 向浏览器中设置 cookie。
      • 可以通过浏览器查看某个网站的cookie
      • 因为cookie可以人为的设置和修改查看,所以一些重要信息无法在cookie上储存,因为是不安全的
    • cookie的修改和查看
      • 万字详细解释cookie,session,JWT(token)

      • 万字详细解释cookie,session,JWT(token)
        通过控制台也可以查看cookie
      • cookie在检查元素中可以进行查看,双击中间的name和value可以进行修改和增加,一般cookie保存一些无关紧要的信息和请求
    • cookie鉴权方案

      • 分析思路
        • 用户登录成功,服务器设置cookie
        • 用户下次请求会自动加上cookie,服务器解析cookie,判断是否登录
        • 用户退出,服务器删除cookie
        • 万字详细解释cookie,session,JWT(token)
          思路图
      • 案例准备(结构,代码,安装包)

        • 用到的npm包

          • npm install cookie-parser

            npm install express

            下面cookie代码演示默认:已安装这两个包        

          • 万字详细解释cookie,session,JWT(token)
            案例目录
          • index.html代码
          • <!DOCTYPE html>
            <html lang="en">
                <head>
                    <meta charset="UTF-8" />
                    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
                    <meta http-equiv="X-UA-Compatible" content="ie=edge" />
                    <title>Document</title>
                </head>
                <body>
                    <input type="text" id="username" name="name" value="admin" />
                    <input type="text" id="password" name="password" value="123456" />
                    <button id="btn_keyvalue">登录</button>
            
                    <a href="./testcookie" target="_blank">页面跳转携带cookie</a>
                    <button id="btn_testCookie">ajax页面跳转携带cookie</button>
                    <button id="btn_delCookie">退出,删除cookie</button>
                    <script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
                    <script>
                        // 用户退出,验证删除cookie的功能
                        $('#btn_delCookie').click(function () {
                            $.ajax({
                                type: 'get',
                                url: 'http://localhost:3000/quit',
                                success(res) {
                                    console.log(res)
                                },
                            })
                        })
            
                        // 测试cookie的效果
                        $('#btn_testCookie').click(function () {
                            $.ajax({
                                type: 'get',
                                url: 'http://localhost:3000/testcookie',
                                success(res) {
                                    console.log(res)
                                },
                            })
                        })
            
                        // 用户登录
                        $('#btn_keyvalue').click(function () {
                            $.ajax({
                                type: 'post',
                                url: 'http://localhost:3000/login',
                                data: {
                                    name: $('#username').val(),
                                    password: $('#password').val(),
                                },
                                success(res) {
                                    console.log(res)
                                },
                            })
                        })
                    </script>
                </body>
            </html>
            
          • server.js代码
          • // 引入模块 express cookie-parser
            const express = require('express')
            const cookieParser = require('cookie-parser')
            const app = express()
            // 中间件 静态托管
            app.use(express.static('public'))
            // 中间件 接收简单键值对 接受的数据在req.body中
            app.use(express.urlencoded())
            // const cors = require('cors')
            // app.use(cors())
            // 中间件 引入cookieparser语法
            app.use(cookieParser())
            // 发送POST请求 点击登录
            app.post('/login', (req, res) => {
                // 解构赋值 ES6语法
                console.log('接收到的简单键值对', req.body)
                const { name, password } = req.body
                // console.log('获取的值', name, password)
                // 1.判断,只能是这个账号密码,正常是链接的数据库
                // 2.判断输入的值是否在数据中存在
                // 3.简单测试cookie,默认这俩值
                if (name === 'admin' && password === '123456') {
                    // 设置响应头来设置cookie
                    // res.setHeader('set-cookie', 'name=' + name)
                    // express框架给我们提供了一个res.cookie方法,用来设置cookie
                    res.cookie('islogin', true)
                    res.send('登陆成功')
                } else {
                    res.send('登陆失败')
                }
            })
            // 跨页面传cookie
            app.get('/testcookie', (req, res) => {
                // 能获取到cookie是引入的cookie-parser
                console.log('本次请求带的cookie是', req.headers.cookie)
                console.log('请求携带的cookie是', req.cookies)
                if (req.cookies.islogin === 'true') {
                    res.send('cookie跨页面成功')
                } else {
                    res.send('cookie已被移除')
                }
            })
            // 退出登录
            app.get('/quit', (req, res) => {
                // 移除islogin的cookie
                res.clearCookie('islogin')
                res.send('删除cookie')
            })
            
            app.listen(3000, () => {
                console.log(3000)
            })
            

      • 效果展示

        • 万字详细解释cookie,session,JWT(token)
          未登录
        • 万字详细解释cookie,session,JWT(token)
          登陆后
        • 万字详细解释cookie,session,JWT(token)
          cookie是登陆后进行设置的

        • 万字详细解释cookie,session,JWT(token)

           我们设置的cookie在浏览器页面也可以被访问的到的

        • 同理,操作各种跨页面,携带cookie进行请求,移除都是可以完成的

    • session

      • session从字面上就是会画

      • session 是区别于数据库存在的一种服务器临时存储技术, 它主要存储一些无需持久化的数据, 比如临时的登录状态信息等.

      • session原理

        • 用户登录成功时,服务器端会生成一个sessionid,并通过set-cookie将生成的sessionid返回给客户端

        • 客户端收到sessionid会将它保存在cookie中,当客户端再次访问服务端时会带上这个sessionid

        • 当服务端再次接收到来自客户端的请求时,会先去检查是否存在sessionid,不存在就新建一个sessionid重复1,2的流程,如果存在就去遍历服务端的session文件,找到与这个sessionid相对应的文件,文件中的键值便是sessionid,值为当前用户的一些信息

        • 万字详细解释cookie,session,JWT(token)

           通过cookie回传给浏览器的是session编号;真正的数据在服务器端

      • 有案例(不想写,写了三次,自动删除三次)

    • cookie和session的比较

      • cookie原理:

        • 从服务器端向客户端浏览器留下信息设置响应头:set-cookie

        • 浏览器每次访问服务器时都带上这些信息(自动携带cookie是浏览器的特点);

      • session原理

        • 服务器端会为每个用户(浏览器)各自保存一个session(文件)。当服务器保存session之后,会以cookie的形式告诉浏览器,你的session编号是哪一个。它把session号返回给了浏览器,而把真实的数据保存在服务器。

        • 下次再来访问服务器的时候,浏览器就会带着它自己的session号去访问,服务器根据session号就可以找到对应的session了。

      • cookie:优点是节省服务器空间,缺点不安全。不要保存敏感信息。

      • session:优点是安全,缺点需要服务器空间(服务器重启,则数据丢失), 是一种最常见的解决方案。

    • JWT

      • JWT(json web token),token的意思是"令牌",是服务端生成的一串字符串,作为客服端进行请求的一个标识

      • 原理:

         

        • 服务器认证之后,生成一个JSON对象,返回给用户,就像下面这样

                  {
                          "姓名": "张三",
                          "角色": "管理员",
                          "到期时间": "2022年7月11日0点0分"
                  }

        • 以后,用户与服务端通信的时候,都要返回这个JSON对象.服务器完全只靠这个对象认定用户的身份
        • 并且为了防止用户篡改数据,服务器在生成这个对象的时候,会给他加密一下,就是我们看到的长长的字符串
        • 万字详细解释cookie,session,JWT(token)
        • 当因为跳转页面携带token,会出现跨域问题(本文中解决跨越问题引用cors包,具体解决跨域问题的方法,后面会对跨域解释(//此处省略地址))
        • 万字详细解释cookie,session,JWT(token)
      • 案例准备

        • 安装npm包

          • npm i jsonwebtoken

            npm i express

            npm i cors

            npm i express-jwt    1

        • 案例目录
          • 万字详细解释cookie,session,JWT(token)

        • idnex.html代码
    • <!DOCTYPE html>
      <html lang="en">
          <head>
              <meta charset="UTF-8" />
              <meta name="viewport" content="width=device-width, initial-scale=1.0" />
              <meta http-equiv="X-UA-Compatible" content="ie=edge" />
              <title>Document</title>
          </head>
          <body>
              <h1>token</h1>
              <input type="text" id="username" name="name" value="admin" />
              <input type="text" id="password" name="password" value="123456" />
              <button id="btn_login">登录,获取token</button>
      
              <button id="btn_testToken">没有token,不能访问</button>
      
              <button id="btn_delToken">删除token</button>
      
              <script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
              <script>
                  $('#btn_delToken').click(function () {
                      // 删除localstorage
                      localStorage.removeItem('token')
                  })
      
                  // 在请求头中携带token
                  $('#btn_testToken').click(function () {
                      $.ajax({
                          type: 'get',
                          url: 'http://localhost:3000/test',
                          headers: {
                              Authorization: localStorage.getItem('token'),
                          },
                          success(res) {
                              console.log(res)
                          },
                      })
                  })
      
                  $('#btn_login').click(function () {
                      $.ajax({
                          type: 'post',
                          url: 'http://localhost:3000/login',
                          data: {
                              name: $('#username').val(),
                              password: $('#password').val(),
                          },
                          success(res) {
                              console.log('登录成功,token', res.token)
                              // 保存token
                              localStorage.setItem('token', res.token)
                          },
                      })
                  })
              </script>
          </body>
      </html>
      
  • server.js代码
  • // 引用模块 express cors jsonwebtoken express-jwt
    const express = require('express')
    const cors = require('cors')
    const app = express()
    const jwt = require('jsonwebtoken')
    const expressJwt = require('express-jwt')
    // 中间件 跨域
    app.use(cors({ origin: true, credentials: true }))
    // 中间件 静态托管
    app.use(express.static('public'))
    // 中间件 接受简单键值对
    app.use(express.urlencoded())
    
    // app.use(jwt().unless())
    // jwt() 用于解析token,并将 token 中保存的数据 赋值给 req.user
    // unless() 约定某个接口不需要身份认证
    app.use(
        expressJwt({
            secret: 'node1',
            // 生成token时的 钥匙,必须和jwt.sign({ name: name },
            //'node1', { expiresIn: 15 })第二个参数名字一致统一
            algorithms: ['HS256'], // 必填,加密算法
        }).unless({
            path: ['/login'], // 除了个接口,其他都需要认证
        })
    )
    app.post('/login', (req, res) => {
        console.log('接收到的数据是', req.body)
        const { name, password } = req.body
        if (password === '123456') {
            // 创建token
            // expiresIn:过期时间,单位是秒
            // - 参数1:必填,对象形式;希望在token中保存的数据
            // - 参数2:必填,字符串形式;加密的钥匙;后续验证token的时候,还需要使用
            // - 参数3:可选,对象形式;配置项,比如可以配置token的有效期
            // - 参数4:可选,函数形式;生成token之后的回调
            // - 生成的token前面,必须拼接 `Bearer ` 这个字符串。
            const tokenStr = jwt.sign({ name: name }, 'node1', { expiresIn: 15 })
            const token = 'Bearer ' + tokenStr
            res.json({ msg: '登录成功', token: token })
        } else {
            res.json({ msg: '登录失败' })
        }
    })
    
    app.get('/test', (req, res) => {
        res.json({ msg: '测试tokenOk' })
    })
    // 在所有的路由最后,加入错误处理中间件,来提示token方面的错误。
    app.use((err, req, res, next) => {
        if (err.name === 'UnauthorizedError') {
            // res.status(401).send('invalid token...');
            res.status(401).send({ status: 1, message: '身份认证失败!' })
        }
    })
    app.listen(3000, () => {
        console.log(3000)
    })
    
  • cookie不能存储重要的信息
  • session储存的信息在服务器上
    • 当访问用户过多的时候,一个服务器储存有限,特点时间大批用户涌入,服务器可能会瘫痪
    • 当在*登陆的信息后,去到其他地区之后,服务器没有你的信息,就还需要登录验证
    • 只能用于特定的服务器,服务器关闭的开启都会影响用户的体验
    • 对于一个域名多个服务器来说不太试用
  • 目前现在的主流公司,一般都是以token来储存用户的信息的
上一篇:jwt学习笔记


下一篇:SpringBoot学习项目-博客项目-part4