# session、cookie

  • 存储在浏览器的一段字符串(最大5kb)
  • 跨域不共享
  • 格式k1=v1;k2=v2;因此可以存储结构化数据
  • 每次发送 http 请求,会将请求域的 cookie 一起发送给 server
  • server 可以修改 cookie 并返回给浏览器
  • 浏览器也可以通过 JavaScript 修改 cookie(有限制)

客户端修改 cookie:

document.cookie = 'k1 = 100'
// 只会是追加到原本的 cookie 的后面

server 端 nodejs 操作 cookie

// 获取
const cookiestr = req.headers.cookie
// 修改 cookie
res.setHeader('Set-Cookie', `username=${username}; path=/; httpOnly`)

一般设置 cookie 是用户在登录的时候,如果登录验证成功,就通过 setHeader 方法进行设置 cookie;也会设置 cookie 的 expires 过期时间。

# session

如果单纯使用 cookie,进行验证登录,会暴露一些用户信息,比如用户名或者邮箱等等。 对于登录,我们可以在 cookie 中存储 userID,server 端对应 username。 存储会话信息。session,也就是 server 端存储用户信息。

结合 session 以及 cookie,我们在做登录的时候,首先在进入处理后台,首先判断客户端是否携带 cookie:

  // 解析 cookie
  req.cookie = {}
  const cookieStr = req.headers.cookie || ''
  cookieStr.split(';').forEach(item => {
    if(!item) {
      return
    }
    const arr = item.split('=')
    const key = arr[0].trim()
    const val = arr[1].trim()
    req.cookie[key] = val
  })

然后根据前面的判断从 cookie 中获取 userId:如果没有 userId ,就生成 userId,为当前时间戳加随机数,如果从 cookie 中能解析到 userId ,设置 sessionId 为 userId,然后从 redis 中根据 userId 读取 session,如果没有 session 信息,就设置 req.sessionId 为空对象,表示没有用户登录,以及设置 session 为空对象。如果能读取到用户的 session 信息,就将请求体的 session 字段设置为用户的 session 信息;

  // 解析session
  let needSetCookie = false
  let userId = req.cookie.userid
  if(!userId) {
    userId = `${Date.now()}_${Math.random()}`
    needSetCookie = true
  }
  req.sessionId = userId
  get(req.sessionId).then(val => {
    if(val === null) {
      set(req.sessionId, {})
      // 设置session
      req.session = {}
    } else {
      req.session = val
    }
  })

如果没有 cookie 就进行设置 cookie;然后在处理路由的时候,需要验证是否有 session 信息,如果没有就提示尚未登录:

  // 处理 post data
  getPostData(req).then(postData => {
    req.body = postData
    // handleBlogRouter 这里处理路由信息-其中包含下面 ,处理路由验证是否有 session 信息,如果没有返回 Promise 对象;
    const blogReault = handleBlogRouter(req, res)
    if(blogReault) {
      blogReault.then(blogData => {
        if(needSetCookie) {
          res.setHeader('Set-Cookie', `userid=${userId}; path=/; httpOnly; expires=${getCookieExpires()}`)
        }
        res.end(
          JSON.stringify(blogData)
        )
      })
      return
    }

    res.writeHead(404, {'Content-type': 'text/plain'})
    res.write('404 Not Found\n')
    res.end()
  })
}

处理路由验证是否有 session 信息:

  // 统一的登录验证函数
  const loginCheck = (req) => {
    if(!req.session.username) {
      return Promise.resolve(
        new ErrorModel('尚未登录')
      )
    }
  }
  if(method === 'POST' && req.path === '/api/blog/new') {
    // 检测是否登录
    const loginCheckResult = loginCheck(req)
    if(loginCheckResult) {
      // 未登录
      return loginCheckResult
    }
    req.body.author = req.session.username
    const result = newBlog(req.body)
    return result.then(data => {
      return new SuccessModel(data)
    })
  }

评 论:

更新: 11/21/2020, 7:00:56 PM