session 和 token

前面我们已经根据接口文档,对客户数据操作API请求进行处理了。

不知道大家有没有发现一个问题?

这些请求过来我们就直接处理了,并没有验证这个请求是不是已经登录的管理员发出的。

对于请求消息的合法性验证, 通常有两种机制 session 和 token

session机制

session 机制的原理如下:

  • 服务端在数据库中保存一张session表。 这张表记录了一次用户登录的会话信息。

    会话 的英文就叫session

    这张表,可以根据 session号(通常叫session ID) 查到 session 的信息数据。

    Django 中 该表 名字就叫 django_session, 如下所示。

    default

    大家可以发现sessionid 通常就是 一串字符串 用来标记一个session的。 而session对应的数据在这里是是加密的。


  • 在用户登录成功后, 服务端就在数据库session表中 中创建一条记录,记录这次会话。

    也就是创建一个新的 sessionid 插入到 该表中。

    服务端也可以 放入一些 该session对应的数据到 记录的数据字段中,比如登录用户 的 信息。

    然后在该登录请求的HTTP响应消息中, 的头字段 Set-Cookie 里填入 sessionid 数据。

    类似这样

    Set-Cookie: sessionid=6qu1cuk8cxvtf4w9rjxeppexh2izy0hh
    

    根据http协议, 这个Set-Cookie字段的意思就是 要求前端将其中的数据存入 cookie中。 并且随后访问该服务端的时候, 在HTTP请求消息中必须带上 这些 cookie数据。

    cookie 通常就是 服务端要求存储在客户端的一些数据。

    以后每次访问 同一个服务(简单理解为访问同一个网站), 必须在HTTP请求中再带上 这些cookie里面的数据。

    cookie数据由多个 键值对组成, 比如:

    sessionid=6qu1cuk8cxvtf4w9rjxeppexh2izy0hh
    username=byhy
    favorite=phone_laptop_watch
    

    这样服务端可以获取该用户的一些额外信息。而不需要每次都要数据库中查找。


  • 该用户的后续操作,触发的HTTP请求, 都会在请求头的Cookie字段带上前面说的sessionid 。

    如下所示

     Cookie: sessionid=6qu1cuk8cxvtf4w9rjxeppexh2izy0hh
    

    服务端接受到该请求后,只需要到session表中查看是否有该 sessionid对应的记录,即可判断这个请求是否是前面已经登录的用户发出的。

    如果不是,就可以拒绝服务,重定向http请求到登录页面让用户登录。

token机制

使用session机制验证用户请求的合法性 的主要缺点有两个

  • 性能问题

    因为,验证请求是根据sessionid 到数据库中查找session表的,而数据库操作是服务端常见的性能瓶颈,尤其是当用户量比较大的时候。

  • 扩展性问题

    当系统用户特别多的时候,后端处理请求的服务端通常由多个,部署在多个节点上。 但是多个节点都要访问session表,这样就要求数据库服务能够被多个节点访问,不方便切分数据库以提高性能。

最近比较流行的一种token机制可以比较好的解决这些问题。

token 简单来说,就是包含了 数据信息 和 校验信息的 数据包。

Session 机制是把 数据信息(比如session表)放到 服务端,服务端是客户无法篡改的,从而保证验证的 可靠性。

而 token机制 数据信息 直接传给 客户端,客户每次请求再携带过来给服务端。服务端无需查找数据库,直接根据token里面的数据信息进行校验。

那么问题来了:客户数据直接发送给客户端,如果 客户端篡改了数据, 比如把自己改为 vip用户怎么办? 服务端怎么验证数据有没有被客户端篡改(术语叫完整性验证)呢?



token 机制的原理如下:


  • 服务端配置一个密钥(secret key),该密钥是服务端私密保存的,不能外泄

  • 在用户登录成功后, 服务端将 用户的信息数据 + 密钥 一起进行一个哈希计算, 得到一个哈希值。

    注意:哈希算法保证了, 哈希值只能根据 同样的 源数据得到。

    如果谁修改了用户信息, 除非他知道密钥,再次使用哈希算法才能得到 正确的新的 哈希值。

    所以这个哈希值,就是用来校验数据是否被修改的.

    然后将 用户数据信息 和 哈希值 一起 做成一个字节串 ,这个字节串就称之为 token

    大家可以发现 token 里面包含了用户数据信息 和 用于校验完整性的哈希值

    然后,服务端返回给客户的HTTP响应中 返回了这个token。 通常token是放在HTTP响应的头部中的。 具体哪个头部字段没有规定,开发者可以自行定义。

  • 服务端接收到请求后,会根据 数据信息 和 密钥 使用哈希算法再次 生成 哈希值。

    如果客户修改了数据信息, 因为他不知道密钥,没法得到正确的新的哈希值,那么 服务端根据 篡改后的数据+密钥 得到的新 哈希值一定和 保存在token中的老哈希值 不同。就知道数据被修改了。

    如果客户没有修改数据,服务端 根据 原来的数据+密钥 得到的哈希值 和保存在token中原来的哈希值一致,就校验通过。

    校验通过后,就确信了数据没有被修改,可以放心的使用token里面的数据 进行后续的业务逻辑处理了。

    上述处理中,由于服务端访问查找数据库,从而大大了提高了处理性能。



使用session验证客户端请求

由于 Django 对session 的支持是 缺省就有的,下面我们来看看前面我们实行的系统中,如何加入对API请求的验证。

大家是否注意到,前面我们处理登录的函数中 有如下箭头所指的 一行代码

image

这行代码的作用 就是在登录认证后,将 用户类型保存到session数据中, 也就是存入前面数据库的那张图的 会话数据记录中。

我们只需在后续的 url 以 /api/mgr 开头的 API请求处理 之前,加上一个验证逻辑。

验证请求的cookie里面是否有sessionid,并且检查session表,看看是否存在session_key为该sessionid 的一条记录,该记录的数据字典里面是否 包含了 usertype为 mgr 的 数据。

前面实现的代码中, 这些请求都是在dispatcher入口函数处理的,我们就只需在该dispatch中进行验证。

修改 mgr/customer.py 的dispatcher 函数,在前面加上如下代码

    # 根据session判断用户是否是登录的管理员用户
    if 'usertype' not in request.session:
        return JsonResponse({
            'ret': 302,
            'msg': '未登录',
            'redirect': '/mgr/sign.html'})

    if request.session['usertype'] != 'mgr' :
        return JsonResponse({
            'ret': 302,
            'msg': '用户非mgr类型',
            'redirect': '/mgr/sign.html'})

注意request对象里面的session属性对应的就是 session记录里面的 数据。

该数据对象类似字典,所以检查是否有usertype类型为mgr的信息,就是这样写

if request.session['usertype'] != 'mgr' 



目前为止,我们系统的完整代码,点击这里下载



上一页 下一页