如何用cookie,session,token去做身份验证

解决几个疑惑

  • HTTP 是一个无连接且无状态协议,客户端每次发出请求时,下一次请求无法得知上一次请求所包含的状态数据,如何能把一个用户的状态数据关联起来呢?
  • 如何对用户进行身份验证?

关键字 token、cookie和session 的理解

token 令牌

是验证身份的一种方式。最简单的token组成:uid(用户唯一的身份标识)、time(当前时间的时间戳)、sign(签名,由token的前几位+盐以哈希算法压缩成一定长的十六进制字符串,可以防止恶意第三方拼接token请求服务器)。

引申问题

  • 攻击者会不会伪造token?

浏览器里一种能永久保存的数据。由服务器产生,发送给浏览器。

cookie的组成有:名称(key)、值(value)、有效域(domain)、路径(域的路径,一般设置为全局:”\”)、失效时间(expires)、安全标志(指定后,cookie只有在使用SSL连接时才发送到服务器(https))。

expires 和 maxAge:告诉浏览器这个 cookie 什么时候过期,expires 是 UTC 格式时间,maxAge 是 cookie 多久后过期的相对时间。当不设置这两个选项时,会产生 session cookie,session cookie 是 transient 的,当用户关闭浏览器时,就被清除。

  • 读取cookie document.cookie
  • 写入cookie document.cookie = 'name=abigale'

以下是我从某个网站截取下的请求信息,可以看到 cookie 里的信息

例子: expires设置有效时间

1
2
3
let date = new Date();
date.setDate(date.getDate+30);
document.cookie = 'name=abc;path=/;expires='+date;

session 会话

当用户去访问某个服务器时,服务器会产生session然后存下用户的信息。session是通过 session_id 去识别用户的。

  • session存放位置可以是
    • 内存
    • cookie本身
    • redis 或 memcached 等缓存中
    • 数据库中。(线上来说,缓存的方案比较常见,存数据库的话,查询效率相比前三者都太低,不推荐)

传统的身份验证:cookie和session之间的合作

说在cookie和session之前:

  • 为什么会有cookie又有session:

    • 原因一:因为如果我们把所有信息全都存在cookie,cookie有很容易被用户在浏览器看到或者在js脚本修改,cookie存储数据大小也受限,太大传输效率就低了,所以我们把一些敏感或者量大的数据存在session里。
    • 原因二:将两者信息进行匹配验证,即浏览器(cookie)和服务器端(session)之间的验证。
  • 为什么需要这两者的合作?

    • HTTP是一种无状态无连接的协议,即请求是不知道是谁请求,需要在这两者之间做一层身份的识别。就如原本是去商场用现金交易,付款后就不知道用户是谁了,但后来变成线上支付,加了一层身份识别,我们就可以对用户进行追踪了。

合作过程

  • 客户端访问时,通过了验证。
  • 服务端将客户端信息进行处理加密(签名,专业术语叫信息摘要算法)。
  • 把记录这些信息的ID发送给客户端
  • 客户端收到ID后存储在cookie中
  • 下次客户端重新访问服务端时,带上cookie信息
  • 服务端验证cookie里面的信息,如果能找到对应的记录,则用户通过了验证

引申出的问题:

为什么要加密?

比如我们放在浏览器的信息是可以通过js拿到的或者其他攻击拿到的,很不安全。如果我们通过某种信息加密手段,就可以防止攻击者冒充用户去干坏事。

可以怎么加密呢?下面举个例子

  • 秘密字符串:如 "thisIsMySecret"
  • 本来发送给服务端的cookie: "name:abigale",这时abigale用户可以伪装成另一位同学yuhaha,如cookie: "name:yuhaha"
  • 这是服务器做个sha1加密:sha1("thisIsMySecret","abigale"),然后把某个加密后的签名(如:4850a42e3bc0ac94323d3923e3d1d)发给用户
  • 后来用户发给服务器端的cookie就变成了

    1
    2
    3
    4
    {
    name: 'abigale',
    'user.sig': '4850a42e3bc0ac94323d3923e3d1d'
    }
  • 到这时,如果攻击者将cookie改为

    1
    2
    3
    4
    {
    name: 'yuhaha',
    'user.sig': '4850a42e3bc0ac94323d3923e3d1d'
    }
签名就匹配不上了,要伪造对应的签名几率很小很小
  • 这就是完成一个加密过程了。这个过程是用来识别用户,针对了HTTP面向无连接(请求完就断开)的方法。

新的验证机制 – token认证

以上我们讲到我们采用 session_id 去匹配用户验证用户完成一次API请求,但每次请求都要去做匹配。一种新的请求机制是token认证

快速理解:我们将一些需要验证的信息在服务端做一次加密,包括一些失效期等,然后返回给客户端,客户端将token存起来,下次访问再讲token传给服务端验证。

参考