跳转至

session 和 token

您需要高效学习,找工作? 点击咨询 报名实战班

点击查看学员就业情况

点击这里,边看视频讲解,边学习以下内容

前面我们已经根据接口文档,编写代码, 对前端发来的 Customer API请求进行处理了。

并且我们也编写了 用户登录处理的代码。

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

前端发来的 Customer API 请求, 我们后端代码就直接处理了, 并没有验证 这个请求是不是已经登录的管理员发出的。

如果是这样,客户端可以不需要登录,直接访问 登录后的主页, 我们编写的登录处理代码又 有什么用呢?

这就 需要我们在 处理 Customer API 请求前, 判断发出该请求的用户是否登录了。

对于请求消息的合法性验证, 通常有两种方案: sessiontoken

session 方案


点击这里,边看视频讲解,边学习以下内容

session 就是 会话 的意思。

session 方案 的原理如下:

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

    具体记录什么信息, 不同的系统各有差异, 通常 会记录 该用户的 ID 、姓名 、登录名 之类的。

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

    image

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

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


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

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

同时也 放入一些 该session对应的数据到 记录的数据字段中,比如登录用户 的 信息。

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

类似这样

Set-Cookie: sessionid=6qu1cuk8cxvtf4w9rjxeppexh2izy0hh

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

cookie 通常就是存储在客户端浏览器的一些数据。 服务端可以通过http响应消息 要求 浏览器存储 一些数据。

以后每次访问 同一个网站服务, 必须在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响应的头部中的。 具体哪个头部字段没有规定,开发者可以自行定义。

  • 该用户的后续操作,触发的HTTP API请求, 会在请求消息里面 带上 token 。

    具体在请求消息的什么地方 存放 token, 由开发者自己定义,通常也存放在http 请求 的头部中。

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

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

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

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

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



使用session验证客户端请求


点击这里,边看视频讲解,边学习以下内容

Django 对session 的支持是 缺省就有的,下面我们来看看如何加入对API请求的合法性验证。

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

image

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

Django 框架 会自动在 HTTP 响应消息头中 加入 类似下面的 sessionid cookie

   Set-Cookie: sessionid=????????

后续的HTTP请求就会携带这个sessionid,

我们处理 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'}, 
            status=302)

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

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

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

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



目前为止,我们项目代码,在如下百度网盘中的 bysms_07.zip

百度网盘链接:https://pan.baidu.com/s/1nUyxvq6IYykBNtPUf4Ho6w

提取码:9w2u

这里面的前端代码包含了用户登录的界面,登录地址为 http://localhost/mgr/sign.html