Python学习:Django开发_02

Python学习:Django开发_02

来源:白月黑羽教Python http://www.python3.vip/tut/webdev/django/01/

常用命令/配置

运行

python manage.py runserver 0.0.0.0:80

创建一个APP

进入项目根目录,执行下面的命令

python manage.py startapp sales 

创建数据库脚本

python manage.py makemigrations common

执行数据库脚本

python manage.py migrate

对资源的增查改删处理

前面已经说过,如果采用前后端分离的架构开发, 后端几乎不负责任何展现界面的工作,只负责对数据进行管理 。

数据的管理,主要就是:响应前端的请求, 对数据资源的 增加、修改、删除、列出

下面我们就以 BYSMS 系统中 customer 数据为例,看看如何进行 数据的增查改删 操作。

现在我们的系统,API接口 已经由架构师定义好了, 点击这里查看

其中包括了客户数据的 增查改删 接口。

大家先仔细阅读一下这个接口文档,将来你们可能就需要根据这样的文档来实现后端的功能哦。

现在我们就根据这个接口文档,来实现后端。

创建 mgr应用目录

接口文档明确说明了,这是针对 管理员用户 的 请求。

前面我们已经为 销售员用户 专门创建了一个应用 sales 来处理相关的 请求。

所以,我们可以 再为 管理员用户 专门创建一个应用 mgr 来处理相关的 请求。 怎么创建还记得吗?

对了,执行

python manage.py startapp mgr 

添加处理请求模块 和 url 路由

前面,我们都是在views.py 里面定义函数,处理 http请求的。

但是可以想象, 以后,这个mgr应用要处理很多类型的http请求。

都用这个views.py 就会让这个文件非常的庞大, 不好维护。所以,我们可以用不同的 py 文件处理不同类型的http请求。

比如,这里我们可以新增一个文件 customer.py, 专门处理 客户端对 customer 数据的操作。

将来如果客户端有对其他类型数据的操作, 比如 order 数据, 我们就可以添加 orders.py 来处理。


接下来,从接口文档,我们可以发现对资源的增删改查 操作, 都是同一个URL,都是 /api/mgr/medicine

而且我们发现,不同的操作请求,使用不同的 HTTP 请求方法 ,比如 添加是POST, 查询是 GET, 修改是 PUT, 删除是 DELETE。

而且请求的参数中都有 action 参数表明这次请求的操作具体是什么。

注意:Django 的 url路由功能 不支持 根据 HTTP 请求的方法 和请求体里面的参数 进行路由。

就是不能像下面这样,来根据请求 是 post 还是 get 来 路由

path('customers/', 'app.views.list_customer', method='get'),
path('customers/', 'app.views.add_customer',  method='post'),

那么大家想想该怎么办?

一种方式是:自己编写一个函数, 来 根据 http请求的类型 和请求体里面的参数 分发(或者说路由)给 不同的函数进行处理。

我们可以 在 customer.py 中定义如下 dispatcher 函数

def dispatcher(request):
    # 将请求参数统一放入request 的 params 属性中,方便后续处理

    # GET请求 参数在url中,同过request 对象的 GET属性获取
    if request.method == 'GET':
        request.params = request.GET

    # POST/PUT/DELETE 请求 参数 从 request 对象的 body 属性中获取
    elif request.method in ['POST','PUT','DELETE']:
        # 根据接口,POST/PUT/DELETE 请求的消息体都是 json格式
        request.params = json.loads(request.body)


    # 根据不同的action分派给不同的函数进行处理
    action = request.params['action']
    if action == 'list_customer':
        return listcustomers(request)
    elif action == 'add_customer':
        return addcustomer(request)
    elif action == 'modify_customer':
        return modifycustomer(request)
    elif action == 'del_customer':
        return deletecustomer(request)

    else:
        return JsonResponse({'ret': 1, 'msg': '不支持该类型http请求'})

该函数 把 请求消息中的参数统一放入到 request请求对象的params 属性中。

params 属性 被 做成一个 dict 类型 , 方便后面的处理函数来获取消息中的参数。

然后 dispatch函数再根据 请求的 类型 和 action 参数的值 决定由那个函数具体处理该请求消息。

比如 action 参数 为 ‘add_customer’ 的 请求 就由 addcustomer 函数 进行处理。

当然在文件的开头,我们需要 先导入 JsonResponse 和 json 的定义,像下面这样

from django.http import JsonResponse
import json

接下来,根据 API 接口 ,我们发现 凡是 API 请求url为 /api/mgr/customers 的,都属于 客户 相关的API, 都应该交由 我们上面定义的dispatch函数进行分派处理。

那么我们需要在Django的url路由文件中加入对应的路由。

所以,

第一步:我们应该在 总路由文件 bysms/urls.py 中定义了如下部分

    # 凡是 url 以 api/mgr  开头的,
    # 都根据 mgr.urls 里面的 子路由表进行路由
    path('api/mgr/', include('mgr.urls')),

第二步: 在 mgr 目录下面添加 urls.py 路由文件, 并 加入如下声明即可, 如下所示

from django.urls import path

from mgr import customer

urlpatterns = [

    path('customers', customer.dispatcher),
]

这样,就表示 凡是 API 请求url为 /api/mgr/customers 的,都交由 我们上面定义的dispatch函数进行分派处理

列出客户

通常数据资源的 增查改删 里面的 查 就是 查看,对应的就是列出数据资源。

根据接口文档,列出客户数据接口,后端返回的数据格式如下

{
    "ret": 0,
    "retlist": [
        {
            "address": "江苏省常州武进市白云街44号",
            "id": 1,
            "name": "武进市 袁腾飞",
            "phonenumber": "13886666666"
        },
        
        {
            "address": "北京海淀区",
            "id": 4,
            "name": "北京海淀区代理 蔡国庆",
            "phonenumber": "13990123456"
        }
    ]              
}

看到没有,这里我们无需 将数据库中获取的数据 转化为 供浏览器展示的HTML。

在前后端分离的开发架构中,如何展示数据,那是前端的事情。

我们后端只需要根据接口文档, 返回原始数据就行。

我们可以使用如下的函数来返回数据库的所有的 客户数据信息

def listcustomers(request):
    # 返回一个 QuerySet 对象 ,包含所有的表记录
    qs = Customer.objects.values()

    # 将 QuerySet 对象 转化为 list 类型
    # 否则不能 被 转化为 JSON 字符串
    retlist = list(qs)

    return JsonResponse({'ret': 0, 'retlist': retlist})

当然在文件的开头,我们需要 先导入 Customer 定义,像下面这样

# 导入 Customer 
from common.models import Customer

可以发现,无需转化数据为HTML, 后端的代码任务也大大减轻。

添加客户

通常数据资源的 增查改删 里面的 增 就是 添加,对应的就是添加数据资源。

根据接口文档,添加客户数据接口,前端提供的客户数据格式如下

{
    "action":"add_customer",
    "data":{
        "name":"武汉市桥西医院",
        "phonenumber":"13345679934",
        "address":"武汉市桥西医院北路"
    }
}

我们可以使用如下的函数来处理

def addcustomer(request):

    info    = request.params['data']

    # 从请求消息中 获取要添加客户的信息
    # 并且插入到数据库中
    # 返回值 就是对应插入记录的对象 
    record = Customer.objects.create(name=info['name'] ,
                            phonenumber=info['phonenumber'] ,
                            address=info['address'])


    return JsonResponse({'ret': 0, 'id':record.id})

Customer.objects.create 方法就可以添加一条Customer表里面的记录。

临时取消 CSRF 校验

根据接口文档,添加客户 请求是个Post请求

POST /网站名/api/mgr/signin  HTTP/1.1
Content-Type:   application/x-www-form-urlencoded

注意,缺省创建的项目, Django 会启用一个 CSRF (跨站请求伪造) 安全防护机制。

在这种情况下, 所有的Post、PUT 类型的 请求都必须在HTTP请求头中携带用于校验的数据。

为了简单起见,我们先临时取消掉CSRF的 校验机制,等以后有需要再打开。

要临时取消掉CSRF的 校验机制,非常简单,只需要在 项目的配置文件 bysms/settings.pyMIDDLEWARE 配置项 里 注释掉 ‘django.middleware.csrf.CsrfViewMiddleware’ 即可。

如下所示

MIDDLEWARE = [
    'django.middleware.security.SecurityMiddleware',
    'django.contrib.sessions.middleware.SessionMiddleware',
    'django.middleware.common.CommonMiddleware',
    # 'django.middleware.csrf.CsrfViewMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'django.contrib.messages.middleware.MessageMiddleware',
    'django.middleware.clickjacking.XFrameOptionsMiddleware',
]

修改客户信息

数据资源的 增查改删 里面的 就是 改动,对应的就是修改数据资源。

根据接口文档,修改客户数据接口,前端提供的数据格式如下

{
    "action":"modify_customer",
    "id": 6,
    "newdata":{
        "name":"武汉市桥北医院",
        "phonenumber":"13345678888",
        "address":"武汉市桥北医院北路"
    }
}

我们可以使用如下的函数来处理

def modifycustomer(request):

    # 从请求消息中 获取修改客户的信息
    # 找到该客户,并且进行修改操作
    
    customerid = request.params['id']
    newdata    = request.params['newdata']

    try:
        # 根据 id 从数据库中找到相应的客户记录
        customer = Customer.objects.get(id=customerid)
    except Customer.DoesNotExist:
        return  {
                'ret': 1,
                'msg': f'id 为`{customerid}`的客户不存在'
        }


    if 'name' in  newdata:
        customer.name = newdata['name']
    if 'phonenumber' in  newdata:
        customer.phonenumber = newdata['phonenumber']
    if 'address' in  newdata:
        customer.address = newdata['address']

    # 注意,一定要执行save才能将修改信息保存到数据库
    customer.save()

    return JsonResponse({'ret': 0})

删除客户

数据资源的 增查改删 里面的 就是 删除,对应的就是删除数据资源。

根据接口文档,删除客户数据接口,前端只需要提供要删除的客户的ID。

数据格式如下

{
    "action":"del_customer",
    "id": 6
}

我们可以使用如下的函数来处理

def deletecustomer(request):

    customerid = request.params['id']

    try:
        # 根据 id 从数据库中找到相应的客户记录
        customer = Customer.objects.get(id=customerid)
    except Customer.DoesNotExist:
        return  {
                'ret': 1,
                'msg': f'id 为`{customerid}`的客户不存在'
        }

    # delete 方法就将该记录从数据库中删除了
    customer.delete()

    return JsonResponse({'ret': 0})

测试后端代码

后端对 客户数据的增查改删已经实现了。

那么,怎么测试我们的代码是否正确呢?

有的同学说,可以等前端开发好了对接。

但是, 我们后端的开发和前端开发是并行的,要等前端开发好了,你们现在干什么?

喝茶看报纸吗?老板肯定不答应。

测试我们的代码,可以自己开发测试程序,模拟前端,发出http请求 对 后端进行测试。

这就是 Web API 接口测试了, Python做接口测试, 最合适的就是使用 requests 这个库。

点击这里参考一下白月黑羽教程requests库的使用

这里,我们只需要 使用 requests库构建 各种 接口规定的 http 请求消息, 并且检查响应。

比如,要检查 列出客户 的接口

import  requests,pprint

response = requests.get('http://localhost/api/mgr/customers?action=list_customer')

pprint.pprint(response.json())

运行一下,大家看看,是不是可以返回这样的结果呢?

{'ret': 0,
 'retlist': [{'address': '梁山行者武松大寨',
              'id': 1,
              'name': '武松',
              'phonenumber': '14456789012',
              'qq': '23434344'},
             {'address': '梁山黑旋风大寨',
              'id': 2,
              'name': '李逵',
              'phonenumber': '13000000001',
              'qq': '234234234324'}]}

根据接口文档,ret 值为0,表示 接口调用成功。

如果retlist里面的格式符合接口规定,并且其中的数据和数据库内容一致,则测试通过

要检查添加客户的接口,如下

import  requests,pprint

# 构建添加 客户信息的 消息体,是json格式
payload = {
    "action":"add_customer",
    "data":{
        "name":"武汉市桥西医院",
        "phonenumber":"13345679934",
        "address":"武汉市桥西医院北路"
    }
}

# 发送请求给web服务
response = requests.post('http://localhost/api/mgr/customers',
              json=payload)

pprint.pprint(response.json())

# 构建查看 客户信息的消息体
response = requests.get('http://localhost/api/mgr/customers?action=list_customer')

# 发送请求给web服务
pprint.pprint(response.json())

和前端集成

最终我们的产品 前端和后端系统会集成在一起成为一个完整的系统。

部署到生产环境(生产环境就是正式的线上运营环境)运行的架构往往比较复杂。我们在后面有专门的章节讲述 一个比较完整的线上环境 如何搭建。

这里我们讲述开发环境下, 前后端分离的架构如何简单集成。


前端环境其实就是 一些前端的代码和资源文件,包括 js文件、html文件、css文件 还有 图片视频文件等。

我们模拟前端团队开发的 前端 系统 打包在这里 ,点击这里下载

下载好以后,可以解压该 z_dist.zip 文件到项目根目录下面,形成一个目录 z_dist。

该目录下面就是前端的 代码资源文件。

Django的开发环境也可以从浏览器访问这些前端的资源文件。

但是前端文件都是静态文件,需要我们配置一下Django的配置文件, 指定http请求如果访问静态文件,Django在哪个目录下查找。

注意,接下来我们配置 Django 静态文件服务, 是 开发时 使用的 一种 临时方案 ,性能很低,这是方便我们调试程序用的。

前面讲过,正式部署web服务的时候,不应该这样干,应该采用其它方法,比如Nginx等。后面的教程会有详细的讲解如何使用Nginx 和 Django 组合使用。


现在,请打开 bysms/urls.py 文件,在末尾 添加一个

+  static("/", document_root="./z_dist")

并添加如下声明

# 静态文件服务
from django.conf.urls.static import static

最终,内容如下

from django.contrib import admin

# 导入一个include函数
from django.urls import path, include

# 静态文件服务
from django.conf.urls.static import static

urlpatterns = [
    path('admin/', admin.site.urls),

    # 凡是 url 以 sales/  开头的,
    # 都根据 sales.urls 里面的 子路由表进行路由
    path('sales/', include('sales.urls')),

    # 凡是 url 以 api/mgr  开头的,
    # 都根据 mgr.urls 里面的 子路由表进行路由
    path('api/mgr/', include('mgr.urls')),

]  +  static("/", document_root="./z_dist")

最后的

+  static("/", document_root="./z_dist")

就是在url 路由中加入 前端静态文件的查找路径。

这样如果 http请求的url 不是以 admin/ sales/ api/mgr/ 开头, Django 就会认为是要访问 z_dist目录下面的静态文件。

好了,现在我们 运行如下命令,启动Django 开发服务器

python manage.py runserver 80

然后我们打开浏览器,输入如下网址:

http://localhost/mgr/index.html

就会出现 管理员操作界面,如下

白月黑羽Python3教程

这是前端开发的 客户管理界面,可以在界面上进行客户的 增查改删操作, 这些操作会触发API 请求发送给我们的后端服务。

大家可以操作一下看看, 后端是否能够正确的响应。

学员常见问题

不少朋友反应,下载示例代码,运行后,发现页面是一片空白, 按F12发现如下错误

Refused to execute script from 'http://localhost/mgr/index.js?...' 

because its MIME type ('text/plain') is not executable, 

and strick MIME type checking is enabled.

那是因为你们的 Windows系统上不知道什么时候破坏了注册表,把扩展名为js的文件类型注册为 ‘text/plain’, 应该为’application/javascript’。

要解决这个问题,请在 Django项目 setting.py 中 末尾添加如下代码,强制Django使用’application/javascript’ 作为 js文件类型

import mimetypes
mimetypes.add_type('text/css', '.css')
mimetypes.add_type('application/javascript', '.js')

然后,刷新网页(按ctrl键刷新,确保不用缓存)即可。

实现登录

前面我们 实现了 服务端 管理员 的一些增删改查操作,但是,我们还没有实现管理员 登录的功能呢。

现在我们来根据,API接口文档, 点击这里查看 实现 管理员登录 功能。

处理登录、登出请求

我们可以在mgr目录里面创建一个代码文件 sign_in_out.py

这个代码文件就是用来处理 管理员登录和登出 的API 请求的


大家可以思考一下, 我们的代码 该如何处理登录请求呢?

无非就把请求参数里面的用户名、密码取出来, 和数据库中记录的用户名密码进行比对。

如果和数据库中 记录的一致就认为是认证通过,否则就是不通过。

Django中创建一个项目,自动为我们创建了一张 用户表,名为 auth_user,如下所示

白月黑羽Python3教程

并且已经为我们做好了登录名、密码验证的库

我们只需要使用 库里面的方法就可以了。

Django的文档就给出了登录和登出代码范例,我们稍微修改一下。

我们在 mgr 目录下面, 创建一个 sign_in_out.py 文件。

在该文件中,输入如下内容

from django.http import JsonResponse

from django.contrib.auth import authenticate, login, logout

# 登录处理
def signin( request):
    # 从 HTTP POST 请求中获取用户名、密码参数
    userName = request.POST.get('username')
    passWord = request.POST.get('password')

    # 使用 Django auth 库里面的 方法校验用户名、密码
    user = authenticate(username=userName, password=passWord)
    
    # 如果能找到用户,并且密码正确
    if user is not None:
        if user.is_active:
            if user.is_superuser:
                login(request, user)
                # 在session中存入用户类型
                request.session['usertype'] = 'mgr'

                return JsonResponse({'ret': 0})
            else:
                return JsonResponse({'ret': 1, 'msg': '请使用管理员账户登录'})
        else:
            return JsonResponse({'ret': 0, 'msg': '用户已经被禁用'})
        
    # 否则就是用户名、密码有误
    else:
        return JsonResponse({'ret': 1, 'msg': '用户名或者密码错误'})


# 登出处理
def signout( request):
    # 使用登出方法
    logout(request)
    return JsonResponse({'ret': 0})

创建 url路由

Bysms系统,浏览器登陆登录页面的url是 http://127.0.0.1/mgr/sign.html

但是这不是 登录 API接口的url路径。

根据接口,管理员登录的API 路径是 /api/mgr/signin

前面的课程, 我们已经在总路由文件 bysms/urls.py 中 添加了如下路由记录

    # 凡是 url 以 api/mgr  开头的,
    # 都根据 mgr.urls 里面的 子路由表进行路由
    path('api/mgr/', include('mgr.urls')),

现在,我们只需要 在mgr 目录下面 的子路由文件 urls.py 里添加如下内容

from django.urls import path
from mgr import sign_in_out

urlpatterns = [

    path('signin', sign_in_out.signin),
    path('signout', sign_in_out.signout),

]

这样就表示:

如果有HTTP请求 url是 /api/mgr/signin 就由 sign_in_out.py 里面的signin 函数处理,

如果有HTTP请求 url是 /api/mgr/signout 就由 sign_in_out.py 里面的signout 函数处理。

测试我们的代码

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

这样我们后端的登录请求处理的代码已经完成了。

那么,怎么测试是否正确呢?

还是可以 使用 requests库构建 登录请求http消息, 并且检查响应,看看是否能登录成功。

非常简单,如下代码即可

import  requests,pprint

payload = {
    'username': 'byhy',
    'password': '88888888'
}

response = requests.post('http://localhost/api/mgr/signin',
              data=payload)

pprint.pprint(response.json())

运行一下,大家看看,是不是可以返回这样的结果呢?

{'ret': 0}

根据接口文档,ret

值为0,表示登录接口调用成功。

session 和 token

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

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

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

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

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

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

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

session 方案

session 就是 会话 的意思。

session 方案 的原理如下:

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

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

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

    白月黑羽Python3教程

    大家可以发现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请求的合法性验证。

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

白月黑羽Python3教程

这行代码的作用 就是在登录认证后,将 用户类型保存到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' 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值