Express之用户身份的认证机制 | 青训营笔记

130 阅读5分钟

这是我参与「第五届青训营 」伴学笔记创作活动的第 10 天

用户身份的认证机制

Cookie

  • 作用

    HTTP 协议是无状态的,服务器无法区分请求是否发送自同一个客户端。Cookie 是 HTTP 协议中用来解决无状态问题的技术。

    服务器以响应头的形式将 cookie 发送给客户端,客户端收到后将其存储,并在下一次向该服务器发送请求时将 cookie 传回服务器,这样服务器就可以根据 cookie 来识别客户端了。

    image.png
  • 使用方法

    1. 服务器给客户端发送 cookie(响应cookie)
      res.cookie(name, value)
      
    2. 服务器接收客户端发回来的 cookie(请求cookie)
      # 1. 安装可以解析 cookie 的包:cookie-parser
      npm i cookie-parser
      
      const express = require("express")
      // 2. 引入cookie-parser
      const cookieParser = require("cookie-parser")
      // 3. 设置为中间件,解析 cookie
      app.use(cookieParser())
      app.METHOD("路由路径", (req, res, next) => {
          // req.cookies 用来读取客户端发回的cookie
          console.log(req.cookies)
      })
      
  • cookie 的有效期

    cookie 是有有效期的,默认情况下 cookie 的有效期就是一次会话(session)。

    会话就是一次从打开到关闭浏览器的过程。

    可以通过 expiresmaxAge 属性来设置 cookie 的有效时间。

    res.cookie(name, value, {
        expires: new Date(y, m, d) //具体到失效的时间点,一般不使用该方法
        maxAge: number// 单位毫秒
    })
    
  • 删除 cookie

    cookie 一旦发送给浏览器 服务器就不能再修改了,但是可以通过发送新的同名 cookie 来替换旧 cookie,从而达到删除或修改的目的。

    // 删除 cookie
    res.cookie("name", "", {
        maxAge: 0
    })
    
  • cookie 的缺点

cookie 由服务器创建,浏览器保存,每次浏览器访问服务器时都需要将 cookie 返回。这导致不能在 cookie 中存放过多数据,并且 cookie 直接存储在客户端,容易被篡改盗用。

Session技术

  • 实现原理

    1. 会话(session)技术:将用户的数据统一存储在服务器中,每个用户的数据都有一个统一的 id 。只需通过 cookie 将 id 发送给浏览器,浏览器每次访问时将 id 返回,即可读取到服务器中存储的数据。

    2. session 是服务器中的一个对象,这个对象用来存储用户的数据。每个 session 对象都有一个唯一的 id ,id 会通过 cookie 的形式发送给客户端。客户端每次访问时,只需将存有 id 的 cookie 发回,即可获取它在服务器中存储的数据。

    NLtIC.png
  • 实现方法

    # 1.安装 expresss-session
    npm i expresss-session
    
    const express = require("express")
    const app = express()
    // 2.引入 expresss-session
    const session = require("expresss-session")
    // 3.设置 session 中间件
    app.use(
        session({
        	secret: "加密算法"
        })
    )
    app.METHOD("路由路径", (req, res, next) => {
        // req.session 用来存储用户信(仅存在内存中,重启服务器会失效)
        req.session.name = value //设置用户数据
        console.log(req.session.name) //读取用户数据
    })
    
  • session 失效的原因

    1. 浏览器的 cookie 被删除
    2. 服务器中的 session 对象被删除
    res.session.destroy(() => {
        // session失效后的操作
        // session失效一般用于退出登录,因此此处可重定向到登陆界面
    })
    
  • 持久化 session

    express-session 默认是将 session 存储到内存中的,一旦服务器重启就会自动重置 session,所以需要对 session 进行持久化操作(写到文件或数据库中)。

    如果将 session 存储到文件中,则需要引入中间件 session-file-store

    # 1.安装
    npm i session-file-store
    
    const express = require("express")
    const app = express()
    const session = require("expresss-session")
    // 2.引入
    const FileStore = require("session-file-store")(session)
    // 3.设置中间件
    app.use(
        session({
            store: new FileStore({
                path: "session文件存储路径"
                secret: "加密算法"
                ttl: 3600 //session有效时间(秒),默认值为3600s(1 hour)
                reapInterval: 3600 //定期清理过期session的间隔(秒),默认为1h
            }),
        	secret: "加密算法"
        })
    )
    app.METHOD("路由路径", (req, res, next) => {
        // req.session 用来存储用户信息
        req.session.name = value //设置用户数据
        req.session.save(() => {
            // 用户数据存储成功后的回调
        })
    })
    
  • 缺点

    session 保存了会话的链接信息与我们需要传输的内容,容量大,还需要保持会话信息,每次同一用户不同客户端访问服务器都需要重新建立session,造成冗余信息大。

Token 令牌

  • token 是访问资源接口(API)时所需要的资源凭证

  • 简单 token 的组成

    1. uid(用户唯一的身份标识)
    2. time(当前时间的时间戳)
    3. sign(签名,token 的前几位以哈希算法压缩成的一定长度的十六进制字符串)
  • token 的身份验证流程

    1. 客户端使用用户名跟密码请求登录
    2. 服务端收到请求,去验证用户名与密码
    3. 验证成功后,服务端会签发一个 token ,并把这个 token 发送给客户端
    4. 客户端收到 token 以后,会把它存储起来,比如放在 cookie 里或者 localStorage 里
    5. 客户端每次向服务端请求资源的时候需要带着服务端签发的 token
    6. 服务端收到请求,然后去验证客户端请求里面带着的 token ,如果验证成功,就向客户端返回请求的数据

    什么是cookie,token和session?它们之间有什么关系? - 知乎

csrf 攻击

  • 定义

    跨站请求伪造 CSRF (也称 XSRF)是一种挟制用户在当前已登录的 Web 应用程序上执行非本意的操作的攻击方法。

    攻击者通过一些技术手段欺骗用户的浏览器去访问一个自己曾经认证过的网站,并以用户的名义发送恶意请求(如财产操作)。

  • 防御方法

    1. 现代大部分浏览器都不允许在跨域的情况下自动发送 cookie,有效避免了 csrf 攻击,但例如 IE 等浏览器仍然存在该问题。
    2. 使用 referer 来检查请求的源URL
      // 获取请求的源URL
      const referer = req.get("referer")
      // 判断URL是否合法
      if(!referer || !referer.startWith("合法的URL前缀")) {
          // 请求不合法
          res.status(403).send("暂无权限")
          return
      }
      
    3. 使用验证码进行人机验证
    4. 尽量使用 post 请求,并结合 token