发现只有在写博客的时候,才会认真地去学习。总结一下我在学习、写前后端项目中的一些有关身份验证的心得体会。

因为HTTP是无状态的,如何保证用户和用户之间的操作隔离开来,如何保证有些接口、页面只有某些用户才能访问。一般通过下面的方式实现:

1.cookie方案

原理

这种方式依赖没用禁用cookie浏览器环境。

step1:客户端请求登陆接口,服务端返回200状态码同时把cookie给客户端。

step2:客户端在需要身份验证的接口上带上cookie。

step3:服务端把cookie取出、解析后就可以知道身份了。

那session(会话)又是什么?session不是一个实体,而是基于cookie来维持会话状态的一种技术。有些敏感信息存储在客户端导致不安全,所以服务端在创建session的时候生成一个session_id通过cookie传给客户端。而服务端维持一个 session_id -> session 类似的哈希结构。

一般来说session是在内存中的,但是通常会把这个哈希结构用KV(键值对)数据库来存储,这样做的好处是方便管理,而且在分布式的环境下也可以。

服务端大致实现思路:


const http = require('http')
const server = http.createServer((req, res) => {
  if (req.url === '/login'){
    // 从数据库中验证账号密码正确(略)
    // 保存下面这个SESSION_ID(略)
    // 设置响应头
    res.writeHead(200, {
      'Content-Type': 'application/json',
      'Set-Cookie': 'SESSION_ID=abcdefg; HttpOnly;' 
    })
    // 返回响应
    res.write(JSON.stringify({
      code: 200,
      msg: 'OK'
    }))
    res.end()
  }

  // 匹配其他路由
  else {
    if (req.headers.cookie && isValid(req.headers.cookie)){
      // 解析cookie并验证,验证通过做一些事情
      // 验证失败则返回code 401
      res.write('OK')
      res.end()
    }
  }
})

server.listen(3000)

前端需要注意:使用fetch时默认不携带cookie,需要手动设置。在XMLHttpRequest中也是。

// Fetch
fetch({
  //cookie既可以同域发送,也可以跨域发送
  credentials: 'include'  
})

// XMLHttpRequest
xhr.withCredentials = true

安全

首先,没用绝对的安全。只能通过一些手段来提高安全性。

1.在set-cookie的时候使用HttpOnly。这样可以防止客户端通过JS来获取cookie,在一些XSS攻击中可能会导致cookie泄漏。

2.HTTPS。在传输过程中cookie不会以明文的方式传递,就算被第三方截取,也是加密过的。

3.服务端的session过期机制。

Token方案

在一些非浏览器环境下(当然浏览器环境也可以使用该方案),如微信小程序、APP中等前端如何和后端通信。其实原理和上面说的是一样的。

原理

step1:客户端请求登陆接口,服务端返回Token,且客户端手动将Token保存。

step2:客户端发起请求的时候将Token带上。

step3:服务端解析Token。

不同就在于需要手动去存储这个凭证。

安全

1.Token设置过期时间,增加刷新机制。

2.HTTPS。

JWT

JWT(JSON Web Token),我过去对JWT的理解是错误的。

参考:停止使用 JWT 认证。JWT适合用于一次性登陆的场景。

优点是服务端消耗更少的资源(不用去保存状态)。

一般jwt保存在localStorage里面,泄漏了jwt同样会导致身份被盗用。可以通过在payload里面设置过期时间来避免泄漏后造成的风险。所以“更安全”不成立。