用户认证
1. cookie&session
- cookie:因为http请求是无状态的,第一次和服务器连接后并且登录成功后,返回一些数据(cookie)给浏览器,然后浏览器保存在本地,当该用户发送第二次请求的时候,就会自动的把上次请求存储的cookie数据自动的携带给服务器。
服务拿到cookie字符串就会进行解析为session={key:value}中的key,对应了找到这个用户是否登录is_login、用户名username等一些信息。
-
设置cookie
新建一个应用来演示cookie相关的操作
-
views.py代码
class Login(View): def get(self,request): # 设置cookie,cookie是在响应中返回的,所以要对响应对象去设置cookie response=HttpResponse() # set_cookie参数 # key:这个cookie的key,一般用JSessionID # value:这个cookie的value # max_age:最长的生命周期 # expires:过期时间 response.set_cookie("JSessionID","asdshgfkldsg999",max_age=120) response.content="请求成功" return response
-
使用Jmeter调用该接口
新建一个HTTP Cookie管理器
-
获取cookie
-
views.py
def post(self,request): # 获取cookie print(request.COOKIES) return HttpResponse("获取cookie")
-
使用Jmeter调用该接口
-
-
存储session
-
views.py
def put(self,request): # 存储cookie cookie = request.COOKIES # session的key是用户名,value是cookie的值 request.session["tim"] = cookie["JSessionID"] return HttpResponse("存储cookie成功")
-
使用Jmeter调用该接口
-
查看数据库表Django_session,新增了session信息
-
2. 修改session的存储机制
默认情况下,session数据是存储到数据库中的。当然也可以将session数据存储到其他地方。可以通过设置SESSION_ENGINE
来更改session的存储位置,这个可以配置为以下几种方案:
- django.contrib.sessions.backends.db:使用数据库。默认就是这种方案。
- django.contrib.sessions.backends.file:使用文件来存储session。
- django.contrib.sessions.backends.cache:使用缓存来存储session。想要将数据存储到缓存中,前提是你必须要在settings.py中配置好CACHES,并且是需要使用Memcached,而不能使用纯内存作为缓存。
- django.contrib.sessions.backends.cached_db:在存储数据的时候,会将数据先存到缓存中,再存到数据库中。这样就可以保证万一缓存系统出现问题,session数据也不会丢失。在获取数据的时候,会先从缓存中获取,如果缓存中没有,那么就会从数据库中获取。
- django.contrib.sessions.backends.signed_cookies:将session信息加密后存储到浏览器的cookie中。这种方式要注意安全,建议设置SESSION_COOKIE_HTTPONLY=True,那么在浏览器中不能通过js来操作session数据,并且还需要对settings.py中的SECRET_KEY进行保密,因为一旦别人知道这个SECRET_KEY,那么就可以进行解密。另外还有就是在cookie中,存储的数据不能超过4k。
3. 项目应用
使用django自带的认证、登录、退出账号的方法,记得自己配置下urls.py,不再展示
-
先创建django自带鉴权相关表
python manage.py makemigrations python manage.py migrate
数据库会生成对应的用户权限相关表和session的表
-
禁掉CSRF中间件
在django处理请求的过程中,需要经过中间件的过滤,涉及到跨站请求伪造时,django会把请求阻止过滤掉,所以我们要在setting.py中禁用跨站请求伪造的中间件,如果不禁用,好像会报一个403的错误。
-
配置settings.py
SESSION_ENGINE = 'django.contrib.sessions.backends.db' # 配置session引擎,表示你想把session存到什么地方(session可以存在数据库、缓存、文件里) SESSION_COOKIE_NAME = 'sessionid' # Session的cookie保存在浏览器上时的key,即:sessionid=随机字符串 SESSION_COOKIE_PATH = "/" # Session的cookie保存的路径(默认) SESSION_COOKIE_DOMAIN = None # Session的cookie保存的域名 SESSION_COOKIE_SECURE = False # 是否Https传输cookie SESSION_COOKIE_HTTPONLY = True # 是否Session的cookie只支持http传输 SESSION_COOKIE_AGE = 60*30 # Session的cookie失效日期(30分钟),不要写成1800,这样不易读,比如表示8小时,写为60*60*8 SESSION_EXPIRE_AT_BROWSER_CLOSE = False # 是否关闭浏览器使得Session过期 SESSION_SAVE_EVERY_REQUEST = False # 是否每次请求都保存Session,默认修改之后才保存
-
认证、登录、退出账号的代码
from django.contrib.auth import authenticate, login, logout from django.contrib.auth.decorators import login_required from django.contrib.auth.models import User from django.http import HttpResponse, JsonResponse import json # 使用django自带的认证、登录、登出方法 def signup(request): '''用户注册方法''' data = json.loads(request.body) # django的创建用户的方法,request传入username、password、email是必填的 user = User.objects.create_user(username=data['username'], password=data['password'], email=data['email']) # 使用User模块的create_user方法创建用户 user.save() # 保存 # 返回一个响应信息 info = { "code": '0000', 'msg': "用户注册成功", 'data': { "id": user.id, "username": user.username, "password": user.password } } return JsonResponse(info) # 使用JsonResponse返回一个json字符串 def user_login(request): ''' 用户登录方法 :param request: :return: ''' if request.method == 'GET': return HttpResponse("脑补一个登录页面") data = json.loads(request.body) user = authenticate(username=data["username"], password=data["password"]) # 返回一个user对象 info = { "code": None, 'msg': None } if user is not None: if user.is_active: # is_active字段表示此条用户名和信息是否有效 login(request, user) # 调用django.contrib.auth模块的login info["code"] = "0000" info["msg"] = "登录成功" else: info["code"] = "9999" info["msg"] = "该账户不可用" else: info["code"] = "9999" info["msg"] = "用户名密码不正确" return JsonResponse(info) def user_logout(request): '''退出登录''' logout(request) # 调用django.contrib.auth模块的logout方法 info = { "code": "0000", 'msg': "退出登录成功" } return JsonResponse(info) def test(request): return HttpResponse("脑补一个登录页面") @login_required(login_url='/login_demo/test') # 此装饰器会校验会在访问该视图之前,校验session,若通过则继续,不通过则重定向到login_url指定的视图中去 def test_user(request): ''' 测试登录 :param request: :return: ''' return HttpResponse('ok')
-
使用Jmeter调用注册接口
-
数据库auth_user表新增了一条用户信息
可见,用户密码是加密的。需要通过django的authenticate方法来验证用户名、密码是否正确。
-
使用Jmeter调用登录接口
接口响应头返回一个sessionid:
数据库表django_session中,新增一条记录,可以看出返回的sessionid就是这条记录的session_key
-
哪些视图需要用户认证呢?
使用装饰器
@login_required(login_url='/login_demo/test')
,此装饰器会校验会在访问该视图之前,校验session,若通过则继续,不通过则重定向到login_url指定的视图中去session过期后,调用testuser,重定向到test
登录之后,调用testuser
ps: 在每个视图上加一个装饰器其实很麻烦,此处只是了解原理,后面学习drf框架时,会使用新的方式进行用户认证。
-
- 扩展内容
- 用户组管理
- 权限管理
缓存
(暂时不深入讲,会在drf部分讲我们开发所需要的一点知识)
中间件
1. 简介
在Django中,中间件(middleware)其实就是一个类,在请求到来和结束后,Django会根据自己的规则在合适的时机执行中间件中相应的方法。
- 1.执行完所有的request方法到达执行流程;
- 2.执行中间件的其他方法;
- 3.经过所有response方法,返回客户端;
在一个项目中,如果想对全局所有视图函数或视图类起作用时,就可以在中间件中实现,比如想实现用户登录判断,基于用户的权限管理(RBAC)等都可以在Django中间件中来进行操作。
在settings.py文件中,注册该中间件(Django项目中的settings模块中,有一个MIDDLEWARE_CLASSES变量,其中每个元素都是一个中间件)
MIDDLEWARE_CLASSES = [
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.auth.middleware.SessionAuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
]
-
引子
中间件是 Django用来处理请求和响应的钩子框架。它是一个轻量级的、底层级的“插件”系统,用于全局性地控制Django 的输入或输出,可以理解为内置的app或者小框架。
在django.core.handlers.base模块中定义了如何接入中间件,这也是学习Django源码的入口之一。
每个中间件组件负责实现一些特定的功能。例如,Django 包含一个中间件组件 AuthenticationMiddleware,它使用会话机制将用户与请求request关联起来。
中间件可以放在你的工程的任何地方,并以Python路径的方式进行访问。
Django 具有一些内置的中间件,并自动开启了其中的一部分,我们可以根据自己的需要进行调整。
-
如何启用中间件
若要启用中间件组件,请将其添加到 Django 配置文件settings.py的 MIDDLEWARE 配置项列表中。
在 MIDDLEWARE 中,中间件由字符串表示。这个字符串以圆点分隔,指向中间件工厂的类或函数名的完整 Python 路径。下面是使用 django-admin startproject命令创建工程后,默认的中间件配置:
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', ]
实际上在Django中可以不使用任何中间件,如果你愿意的话,MIDDLEWARE 配置项可以为空。但是强烈建议至少使用 CommonMiddleware。建议是保持默认的配置,这有助于你提高网站的安全性。
-
中间件最关键的顺序问题
MIDDLEWARE
的顺序很重要,具有先后关系,因为有些中间件会依赖其他中间件。例如:AuthenticationMiddleware
需要在会话中间件中存储的经过身份验证的用户信息,因此它必须在SessionMiddleware
后面运行 。在请求阶段,调用视图之前,Django 按照定义的顺序执行中间件 MIDDLEWARE,自顶向下。
如果某个层的执行过程认为当前的请求应该被拒绝,或者发生了某些错误,导致短路,直接返回了一个响应,那么剩下的中间件以及核心的视图函数都不会被执行。
-
Django内置的中间件
Django内置了下面这些中间件,满足了我们一般的需求:
-
Cache
缓存中间件
如果启用了该中间件,Django会以CACHE_MIDDLEWARE_SECONDS 配置的参数进行全站级别的缓存。
-
Common
通用中间件
-
2. 中间件方法
传统方式自定义中间件其实就是在编写五大钩子函数:
process_request(self,request)
process_response(self, request, response)
process_view(self, request, view_func, view_args, view_kwargs)
process_exception(self, request, exception)
process_template_response(self,request,response)
可以实现其中的任意一个或多个
3. 自定义中间件
有时候,为了实现一些特定的需求,我们可能需要编写自己的中间件。
在编写方式上,需要注意的是,当前Django版本2.2,存在两种编写的方式。一种是Django当前官网上提供的例子,一种是老版本的方式。本质上,两种方式其实是一样的。
我们先看一下传统的,也是技术文章最多,目前使用最多的方式。
Django 提供的 get_response 方法可能是一个实际视图(如果当前中间件是最后列出的中间件),或者是列表中的下一个中间件。我们不需要知道或关心它到底是什么,它只是代表了下一步要进行的操作。
两个注意事项:
- Django仅使用 get_response 参数初始化中间件,因此不能为 init() 添加其他参数。
- 与每次请求都会调用 call() 方法不同,当 Web 服务器启动后,init() 只被调用一次。
-
新建一个应用来演示中间件
python manage.py startapp middle_demo
(自行配置urls.py)
-
应用下新建一个middleware.py
class SimpleMiddleware1: # 第一个中间件示例 # 固定写法, 必须有 def __init__(self, get_response): self.get_response = get_response # 配置和初始化 # call方法中定义一些中间件的操作 def __call__(self, request): # 在这里编写视图和后面的中间件被调用之前需要执行的代码 # 这里其实就是旧的process_request()方法的代码 print("SimpleMiddleware1的process_request()方法被调用") response = self.get_response(request) # 在这里编写视图调用后需要执行的代码 # 这里其实就是旧的process_response()方法的代码 print("SimpleMiddleware1的process_response()方法被调用") return response def process_view(self, request, view_func, view_args, view_kwargs): print("SimpleMiddleware1的process_view()方法被调用") # 出现异常时,会执行这个代码 def process_exception(self,request,exception): print("SimpleMiddleware1的process_exception()方法被调用") def process_template_response(self,request,response): # 默认不执行这个函数,除非views函数中返回的实例对象(注意这里这个词)中有render()方法 print("SimpleMiddleware1的process_template_response()方法被调用") return response class SimpleMiddleware2: # 第二个中间件示例 # 固定写法, 必须有 def __init__(self, get_response): self.get_response = get_response # 配置和初始化 # call方法中定义一些中间件的操作 def __call__(self, request): # 在这里编写视图和后面的中间件被调用之前需要执行的代码 # 这里其实就是旧的process_request()方法的代码 print("SimpleMiddleware2的process_request()方法被调用") response = self.get_response(request) # 在这里编写视图调用后需要执行的代码 # 这里其实就是旧的process_response()方法的代码 print("SimpleMiddleware2的process_response()方法被调用") return response def process_view(self, request, view_func, view_args, view_kwargs): print("SimpleMiddleware2的process_view()方法被调用") # 出现异常时,会执行这个代码 def process_exception(self,request,exception): print("SimpleMiddleware2的process_exception()方法被调用") def process_template_response(self,request,response): # 默认不执行这个函数,除非views函数中返回的实例对象(注意这里这个词)中有render()方法 print("SimpleMiddleware2的process_template_response()方法被调用") return response
-
配置settings.py
MIDDLEWARE = [ 'middle_demo.middleware.SimpleMiddleware1', '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', 'middle_demo.middleware.SimpleMiddleware2', ]
可以调用我们之前配置好的登录接口,在控制台上看到中间件的顺序:
4. 中间件应用
-
应用实例一:IP拦截
如果我们想限制某些IP对服务器的访问,可以在settings.py中添加一个BLACKLIST(全大写)列表,将被限制的IP地址写入其中。然后,我们就可以编写下面的中间件了:
from django.http import HttpResponseForbidden from django.conf import settings class BlackListMiddleware(): def __init__(self, get_response): self.get_response = get_response def __call__(self, request): if request.META['REMOTE_ADDR'] in getattr(settings, "BLACKLIST", []): return HttpResponseForbidden('
该IP地址被限制访问!
') response = self.get_response(request) return response -
应用实例二:DEBUG页面
网站上线正式运行后,我们会将DEBUG改为 False,这样更安全。但是发生服务器5xx系列错误时,管理员却不能看到错误详情,调试很不方便。有没有办法比较方便地解决这个问题呢?
- 普通访问者看到的是500错误页面
- 管理员看到的是错误详情Debug页面
利用中间件就可以做到!代码如下:
import sys from django.views.debug import technical_500_response from django.conf import settings class DebugMiddleware(): def __init__(self, get_response): self.get_response = get_response def __call__(self, request): response = self.get_response(request) return response def process_exception(self, request, exception): # 如果是管理员,则返回一个特殊的响应对象,也就是Debug页面 # 如果是普通用户,则返回None,交给默认的流程处理 if request.user.is_superuser or request.META.get('REMOTE_ADDR') in settings.ADMIN_IP: return technical_500_response(request, *sys.exc_info())
这里通过if判断,当前登录的用户是否超级管理员,或者当前用户的IP地址是否在管理员IP地址列表中。符合两者之一,即判断当前用户有权限查看Debug页面。
接下来注册中间件,然后在测试视图中添加一行raise。再修改settings.py,将Debug设为False,提供ALLOWED_HOSTS = ["*"],设置比如ADMIN_IP = ['192.168.0.100'],然后启动服务器0.0.0.0:8000,从不同的局域网IP来测试这个中间件。
正常情况下,管理员应该看到类似下面的Debug页面:
RuntimeError at /midtest/ No active exception to reraise Request Method: GET Request URL: http://192.168.0.100:8000/midtest/ Django Version: 2.0.7 Exception Type: RuntimeError Exception Value: No active exception to reraise .....
而普通用户只能看到:
A server error occurred. Please contact the administrator.
安装redis
安装参考:https://www.jianshu.com/p/bb7c19c5fc47
pycharm集成化插件Iedis
安装参考:https://blog.csdn.net/babados/article/details/78575145
因在mac上是付费的(不知道win要不要付费),故没有使用。
自己使用了叫做Redis桌面管理工具的软件:http://www.pc6.com/mac/486661.html
redis配置和测试
-
安装django-redis
pip install django-redis==4.11.0
-
配置settings.py
添加:
CACHES = { # 配置缓存数据为redis "default": { "BACKEND": "django_redis.cache.RedisCache", "LOCATION": "redis://127.0.0.1:6379", "OPTIONS": { "CLIENT_CLASS": "django_redis.client.DefaultClient", "PASSWORD":"12345" #redis密码,如果没设密码可以不用配置 } } }
-
测试redis安装是否成功
# 终端输入 # 启动redis 服务端 redis-server # 启动redis客户端 redis-cli
from django_redis import get_redis_connection
-
获取缓存库链接
cache = get_redis_connection("default") # default是settings.py中配置的项
-
添加缓存数据
cache.set("abc","aaaa1234",30*60)
在redis客户端可以查看
token
-
token简介
在实现登录功能的时候,正常的B/S应用都会使用cookie+session的方式来做身份验证,后台直接向cookie中写数据,但是由于移动端的存在,移动端是没有cookie机制的,所以使用token可以实现移动端和客户端的token通信。
-
验证流程
整个基于Token的验证流程如下:
- 客户端使用用户名跟密码请求登录
- 服务器收到请求,去验证用户名和密码
- 验证成功后,服务端会签发一个Token,再把这个Token发送到客户端
客户端收到的Token以后可以把它存储起来,比如放在Cookie或LocalStorage里 - 客户端每次向服务器发送其他请求的时候都要带着服务器签发的Token
- 服务器收到请求,去验证客户端请求里面带着的Token,如果验证成功,就像客户端返回请求的数据
-
-
JWT标准
构造Token的方法挺多的,可以说只要是客户端和服务器端约定好了格式,是想怎么写就怎么写的,然而还有一些标准写法,例如JWT读作/jot/,表示:JSON Web Tokens。
JWT标准的Token有三个部分:
- header
- payload
- signature
三个部分会用点分割开,并且都会使用Base64编码,所以真正的Token看起来像这样:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJuaW5naGFvLm5ldCIsImV4cCI6IjE0Mzg5NTU0NDUiLCJuYW1lIjoid2FuZ2hhbyIsImFkbWluIjp0cnVlfQ.SwyHTEx_RQppr97g4J5lKXtabJecpejuef8AqKYMAJc
-
Header
header部分主要是两部分内容,一个是Token的类型,另一个是使用的算法,比如下面的类型就是JWT,使用的算法是HS256:
{ "typ": "JWT", "alg": "HS256" }
上面的内容要用 Base64 的形式编码一下,所以就变成这样:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9
-
Payload
Payload 里面是 Token 的具体内容,这些内容里面有一些是标准字段,你也可以添加其它需要的内容。下面是标准字段:- iss:Issuer,发行者
- sub:Subject,主题
- aud:Audience,观众
- exp:Expiration time,过期时间
- nbf:Not before
- iat:Issued at,发行时间
- jti:JWT ID
-
Signature
JWT的最后一部分是Signature,这部分相当于前两段的摘要,用来防止其他人来篡改Token中的信息,在处理时可以首先将前两段生成的内容使用Base64生成一下再加盐然后利用MD5等摘要算法在生成一遍。
-
token生成
-
服务端生成Token
在服务端生成Token的时候,需要解决两个问题
- 使用什么加密算法
- Token如何存储
-
加密算法
这里的加密算法并不是MD5,SHA1这样的哈希算法,因为这种算法是无法解密的,只能用来生成摘要,在Django中内置了一个加密前面模块django.core.signing模块,可以用来加密和解密任何数据,使用签名模块的dumps和load函数来实现。
from django.core import signing value = signing.dumps({"foo":"bar"}) src = signing.loads(value) print(value) print(src)
结果是
eyJmb28iOiJiYXIifQ:1NMg1b:zGcDE4-TCkaeGzLeW9UQwZesciI {‘foo’: ‘bar’}
-
-
token存储
项目文件下新建一个utils目录,token.py
import time from django.core import signing import hashlib from django_redis import get_redis_connection cache = get_redis_connection("default") HEADER = {'typ': 'JWT', 'alg': 'default'} KEY = 'DAYBREAK' SALT = 'www.daybreak.com' # 生成摘要的盐 TIME_OUT = 30 * 60 # 30min def encrypt(obj): """加密""" value = signing.dumps(obj, key=KEY, salt=SALT) value = signing.b64_encode(value.encode()).decode() return value def decrypt(src): """解密""" src = signing.b64_decode(src.encode()).decode() raw = signing.loads(src, key=KEY, salt=SALT) return raw # 重点掌握 def create_token(username): """生成token信息""" # 1. 加密头信息 header = encrypt(HEADER) # 2. 构造Payload payload = {"username": username, "iat": time.time()} payload = encrypt(payload) # 3. 生成签名 md5 = hashlib.md5() md5.update(("%s.%s" % (header, payload)).encode()) signature = md5.hexdigest() token = "%s.%s.%s" % (header, payload, signature) # 存储到缓存中 cache.set(username, token, TIME_OUT) return token def get_payload(token): payload = str(token).split('.')[1] payload = decrypt(payload) return payload # 通过token获取用户名 def get_username(token): payload = get_payload(token) return payload['username'] # 重点掌握 def check_token(token): if token is None: return False username = get_username(token) last_token = cache.get(username) if last_token: cache.expire(username, TIME_OUT) return True return False def delete_token(username): last_token = cache.get(username) if last_token: cache.delete(username) return True return False
-
重点理解
create_token()
创建token、check_token()
验证tokenfrom utils.token import * token = create_token("leitx") # 生成token check_token(token) # 验证token,认证成功返回True
-
WSGIHTTP
基于中间件实现用户登录验证
-
utils目录下,新建login_middleware.py
请求之前就要做处理,所以不需要实现后面这些方法
process_view
、process_exception
、process_template_response
import json import re from utils.token import check_token # 设置白名单,这些请求不需要验证token,比如注册、登录接口 from django.http import HttpResponse white_list = ['/middle_demo/login/', '/middle_demo/signup/'] # 设置黑名单,作为示例 black_list = ['/middle_demo/black/'] class LoginMiddleware: # 固定写法, 必须有 def __init__(self, get_response): self.get_response = get_response # 配置和初始化 # call方法中定义一些中间件的操作 def __call__(self, request): request_url = request.path_info # 获取请求的url # 通过正则判断url # for p in white_list: # r = re.compile(p) # if r.match(request_url): # response = self.get_response(request) # return response # 不会正则的这样判断 # 如果是白名单 for p in white_list: if request_url in p: response = self.get_response(request) return response # 如果是黑名单 for p in black_list: if request_url in p: response = HttpResponse() response.content = json.dumps({"code": "9999", "message": "非法请求", "data": None}) response["Content-Type"] = "application/json;charset=UTF-8" return response # 获取请求头token的值 token = request.META.get("HTTP_TOKEN") if check_token(token): response = self.get_response(request) return response response = HttpResponse() response.content = json.dumps({"code": "9999", "message": "用户未登录或token过期", "data": None}) response["Content-Type"] = "application/json;charset=UTF-8" return response
-
request.META
是一个Python字典包含了所有本次HTTP请求的Header信息,他把请求头的中的key都转为大写,并都加上HTTP_ 前缀,所以获取请求token值时,key使用"HTTP_TOKEN"
-
-
配置settings.py中的中间件
因为中间件代码中有return,触发return时,不会再执行后面的中间件request部分,所以一般自定义的中间件都放在最下面。
MIDDLEWARE = [ 'middle_demo.middleware.SimpleMiddleware1', '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', 'middle_demo.middleware.SimpleMiddleware2', 'utils.login_middleware.LoginMiddleware', ]
-
在middle_demo的app中的view.py
import json from django.contrib.auth import authenticate from django.contrib.auth.models import User from django.http import JsonResponse, HttpResponse from django.views import View from utils.token import create_token # Create your views here. def bye(request): print("想说拜拜") return HttpResponse("成功获取bye") class Signup(View): def post(self, request): data = json.loads(request.body) username = data.get("username",None) password = data.get("password",None) email = data.get("email",None) try: user = User.objects.create_user(username=username, password=password, email=email) token = create_token(user.username) return JsonResponse({"code": "0000", "message": "注册成功", "data": token}) except: return JsonResponse({"code": "9999", "message": "用户已注册或信息缺失", "data": None}) class Login(View): def post(self, request): data = json.loads(request.body) username = data['username'] password = data['password'] # 校验用户名和密码,成功返回user对象,失败返回None user = authenticate(username=username, password=password) if user: token = create_token(user.username) return JsonResponse({"code": "0000", "message": "登录成功", "data": token}) else: return JsonResponse({"code": "9999", "message": "用户名或密码不正确", "data": None})
-
Jmeter调用白名单、其他接口对比
- 踩坑:代码中写token是存在redis中的,所以一定要开启redis服务。
白名单: '/middle_demo/signup/'
白名单:'/middle_demo/login/'
非白名单,没有加token
非白名单,加token
格式化响应输出
Django中请求的生命周期
步骤
第一步:浏览器发起请求
第二步:WSGI创建socket服务端,接收请求(Httprequest)
第三步:中间件处理请求
第四步:url路由,根据当前请求的URL找到视图函数
第五步:view视图,进行业务处理(ORM处理数据,从数据库取到数据返回给view视图;view视图将数据渲染到template模板;将数据返回)
第六步:中间件处理响应
第七步:WSGI返回响应(HttpResponse)
第八步:浏览器渲染
FBV模式和CBV模式(了解)
一个url对应一个视图函数,这个模式叫做FBV(Function Base Views),即函数视图
一个url对应一个类,这个模式叫做CBV(Class Base views),即类视图
restframework框架
restful 规范
Restful API是目前比较成熟的一套互联网应用程序的API设计理念,Rest是一组架构约束条件和原则,如何Rest约束条件和原则的架构,我们就称为Restful架构,Restful架构具有结构清晰、符合标准、易于理解以及扩展方便等特点,受到越来越多网站的采用!
Restful API接口规范包括以下部分:
- 协议
API与用户的通信协议,总是使用HTTPs协议。
- 域名
应该尽量将API部署在专用域名之下,如https://api.专属域名.com
;如果确定API很简单,不会有进一步扩展,可以考虑放在主域名下,如https://专属域名.com/api/
。
- 版本
可以将版本号放在HTTP头信息中,也可以放入URL中,如https://api.专属域名.com/v1/
- 路径
路径是一种地址,在互联网上表现为网址,在RESTful架构中,每个网址代表一种资源(resource),所以网址中不能有动词,只能有名词,而且所用的名词往往与数据库的表格名对应。一般来说,数据库中的表都是同种记录的"集合"(collection),所以API中的名词也应该使用复数,如https://api.专属域名.com/v1/students
。
- HTTP****动词
对于资源的具体操作类型,由HTTP动词表示,HTTP动词主要有以下几种,括号中对应的是SQL命令。
-
- GET(SELECT):从服务器取出资源(一项或多项);
-
- POST(CREATE):在服务器新建一个资源;
-
- PUT(UPDATE):在服务器更新资源(客户端提供改变后的完整资源);
-
- PATCH(UPDATE):在服务器更新资源(客户端提供改变的属性);
-
- DELETE(DELETE):从服务器删除资源;
-
- HEAD:获取资源的元数据;
-
- OPTIONS:获取信息,关于资源的哪些属性是客户端可以改变的。
- 过滤信息
如果记录数量很多,服务器不可能都将它们返回给用户,API会提供参数,过滤返回结果,常见的参数有:
-
- ?limit=20:指定返回记录的数量为20;
-
- ?offset=8:指定返回记录的开始位置为8;
-
- ?page=1&per_page=50:指定第1页,以及每页的记录数为50;
-
- ?sortby=name&order=asc:指定返回结果按照name属性进行升序排序;
-
- ?animal_type_id=2:指定筛选条件。
- 状态码
服务器会向用户返回状态码和提示信息,以下是常用的一些状态码:
-
- 200 OK - [GET]:服务器成功返回用户请求的数据;
-
- 201 CREATED - [POST/PUT/PATCH]:用户新建或修改数据成功;
-
- 202 Accepted - [*]:表示一个请求已经进入后台排队(异步任务);
-
- 204 NO CONTENT - [DELETE]:用户删除数据成功;
-
- 400 INVALID REQUEST - [POST/PUT/PATCH]:用户发出的请求有错误,服务器没有进行新建或修改数据的操作;
-
- 401 Unauthorized - [*]:表示用户没有权限(令牌、用户名、密码错误);
-
- 403 Forbidden - [*] 表示用户得到授权(与401错误相对),但是访问是被禁止的;
-
- 404 NOT FOUND - [*]:用户发出的请求针对的是不存在的记录,服务器没有进行操作;
-
- 406 Not Acceptable - [GET]:用户请求的格式不可得;
-
- 410 Gone -[GET]:用户请求的资源被永久删除,且不会再得到的;
-
- 422 Unprocesable entity - [POST/PUT/PATCH] 当创建一个对象时,发生一个验证错误;
-
- 500 INTERNAL SERVER ERROR - [*]:服务器发生错误,用户将无法判断发出的请求是否成功。
- 错误处理
如果状态码是4xx,就会向用户返回出错信息,一般来说,返回的信息中将error作为键名,出错信息作为键值。
- 返回结果
针对不同操作,服务器向用户返回的结果应该符合以下规范:
-
- GET /collection:返回资源对象的列表(数组);
-
- GET /collection/resource:返回单个资源对象;
-
- POST /collection:返回新生成的资源对象;
-
- PUT /collection/resource:返回完整的资源对象;
-
- PATCH /collection/resource:返回完整的资源对象;
-
- DELETE /collection/resource:返回一个空文档。
- Hypermedia API
RESTful API最好做到Hypermedia,即返回结果中提供链接,连向其他API方法,使得用户不查文档,也知道下一步应该做什么。
以上是Restful API设计应遵循的十大规范,除此之外,Restful API还需注意身份认证应该使用OAuth 2.0框架,服务器返回的数据格式,应该尽量使用JSON,避免使用XML。