目录
一、用户认证session
1.1 原理
1.1.1基础定义
- Http特性:HTTP 是一种"无状态"协议,这意味着每次客户端检索网页时,客户端打开一个单独的连接到 Web 服务器,服务器会自动不保留之前客户端请求的任何记录
- Cookie定义:存储在客户端计算机上(一般是浏览器中)的文本文件,并保留了各种跟踪信息
- 识别返回用户包括四个步骤:
- 第一步:浏览器向服务器发送请求
- 第二步:服务器脚本验证请求,并向浏览器发送一组 Cookie
- 第三步:浏览器将这些信息存储在本地计算机上,以备将来使用
- 第四步:当下一次浏览器向 Web 服务器发送任何请求时,浏览器会把这些 Cookie 信息放到request的请求头中发送到服务器,服务器将使用这些信息来识别用户
- 认证意义:确定当前发过来的请求(request)的人的身份,根据需要每次请求都验证,后续DRF有jwt身份认证,此处session仅作了解即可
1.1.2 session
- 定义:保存在服务端的键值对,并依托cookie技术实现的用户验证
- 验证流程
- session存储
- 存储位置:默认为数据库中django_session表中
- session_key:即为sessionID,以cookie形式发到浏览器保存,验证的关键纽带
- session_data:保存用户信息,服务端自由设置,并密文保存在表中,用于以后取用
- expire_date:保存该条记录的过期时间(默认14天)
1.2 代码写法
1.2.1 session配置设置
- 位置:
settings.py
中最后一行新增 - 代码(以下均为默认,只需写修改过的)
# Session的cookie保存在浏览器上时的key名:默认sessionid SESSION_COOKIE_NAME = "sessionid" # 是否Https传输cookie:默认否 SESSION_COOKIE_SECURE = False # 是否只支持http传输cookie:默认是 SESSION_COOKIE_HTTPONLY = True # Session的cookie有效期秒数,从发送时为起算点:默认两周 SESSION_COOKIE_AGE = 1209600 # 是否关闭浏览器时,Session即过期:默认否 SESSION_EXPIRE_AT_BROWSER_CLOSE = False # 每次请求是否更新有效期起算点:默认否 SESSION_SAVE_EVERY_REQUEST = False
1.2.2 存储账户的表
- 文件api/models.py
- 代码
from django.db import models class Account(models.Model): name = models.CharField(max_length=24, primary_key=True) password = models.CharField(max_length=48)
- 表样式
1.2.3 视图函数
账户视图函数
- 位置:api/account.py
- 功能:用来处理跟账户相关的视图函数
- 代码
from django.http import HttpResponse, JsonResponse from api import models # 处理注册请求:注册的账号、密码,会被加入到数据库Account表中 def sign_in(request): # 获取用户输入的账号密码 name = request.POST.get("name") password = request.POST.get("password") # 查询注册的账号是否已在Account表中 user = models.Account.objects.filter(name=name).first() # 若账号不在Account表中,则新增账号,密码 if not user: models.Account.objects.create(name=name, password=password) # 此处也可以返回数据给前端做展示或路由跳转 # 另一种返回 return JsonResponse({"code": True , "mesg": "注册成功"}) return HttpResponse("注册成功") # 若账号在Account表中,则返回数据给前端做展示或路由跳转 else: # 另一种返回 return JsonResponse({"code": False , "mesg": "用户已存在"}) return HttpResponse("用户已存在") # 处理登陆请求:会验证账号、密码,并构建session相关 def log_in(request): # 获取请求的账号、密码 name = request.POST.get("name") password = request.POST.get("password") # 查询注册的账号是否已在Account表中 user = models.Account.objects.filter(name=name).first() # 若账号不在Account表中,则返回数据给前端做展示或路由跳转 if not user: # 另一种返回 return JsonResponse({"code": False , "mesg": "用户不存在"}) return HttpResponse("用户不存在") # 若账号在Account表中,则判断密码,若正确,设置session elif password == user.password: # 下一语句执行两步: # 1、设置django_session表的键值session_key,session_data, expire_data # 2、服务器将session_key作为cookie传入客户端并写入浏览器 request.session['user_info'] = {"name": name} # 登陆成功,返回数据给前端做展示或路由跳转 # 另一种返回 return JsonResponse({"code": True , "mesg": "登录成功"}) return HttpResponse("登录成功") else: # 用户密码错误,返回数据给前端做展示或路由跳转 # 另一种返回 return JsonResponse({"code": False , "mesg": "密码错误"}) return HttpResponse("密码错误") # 处理登出请求 def log_out(request): # 删除django_session表的键值session_key,session_data, expire_data # 同时向浏览器发响应,删除浏览器上的cookie request.session.flush() # 注销成功 # 另一种返回 return JsonResponse({"code": True, "mesg": "注销成功"}) return HttpResponse("注销成功") # 装饰器:在需要登录验证的视图函数上装饰 def auth_check(func): # 第一参数必须放置request,否则下面的is_login读不出来 def wrapper_in(request, *argv, **kw): # 获取session中的user_info字段,若未登录则不会存在,设置默认值为False # 读取session具体数据:name = request.session.get("user_info")["name"] is_login = request.session.get("user_info", False) if not is_login: # 若未登录,直接跳出,返回数据给前端做展示或路由跳转 # 另一种返回 return JsonResponse({"code": False , "mesg": "未登陆"}) return HttpResponse("未登陆") # 若已登录,正常执行被装饰的函数 return func(request, *argv, **kw) return wrapper_in
常规视图函数
- 位置:api/views.py
- 功能:存常规逻辑视图,需要登录验证的加装饰器
- 代码
from django.http import HttpResponse from api.account import auth_check # 登录的用户,可以直接访问到index函数,未登录的用户返回:"未登陆" # 返回数据给前端做展示或路由跳转 @auth_check def index(request): # 传字典 # return JsonResponse({"code": True , "mesg": "这是主页返回信息"}) # 或者传字典式列表,但是要关闭safe选项 # return JsonResponse([{"code": True}, {"mesg": "这是主页返回信息"}], safe=False) return HttpResponse("这是主页返回信息")
1.2.4 账户路由
- 文件urls.py
- 代码
from django.urls import path from api import views, account urlpatterns = [ path('signin/', account.sign_in), path('index/', views.index), path('login/', account.log_in), path('logout/', account.log_out), ]
1.3 遇到问题
遇到问题:RuntimeError: ‘cryptography’ package is required for sha256_password or caching_sha2_password auth methods
原因:用mysql数据库会有这个问题,SQLlit3无此问题
解决:pip install cryptography (别挂科学上网软件)
1.4 验证账号路由
- signin验证
- login验证
- logout验证
- 装饰器验证
二、表单验证Form
2.1 基础操作
- Form组件:可以对用户提交的数据进行校验,并返回错误信息与前端交互
- 代码写法
文件/api/my_formfrom django.forms import fields, Form # 用于定义表单验证规则 from django.http import HttpResponse, JsonResponse # 用于给前端返回信息 from api import models # 用于跟数据库交互 class UserForm(Form): name = fields.CharField( # 字符型验证规则 required=True, # 默认,可不写 min_length=4, # 最小长度 max_length=14, # 最大长度 error_messages={ # 定义错误显示别名(对应上面) "required": "not null", "min_length": "too short", "max_length": "too long", }) age = fields.IntegerField( # 整型验证规则 required=True, # 默认,可不写 min_value=12, # 最小值 max_value=60, # 最大值 error_messages={ # 定义错误显示别名(对应上面) "required": "not null", "invalid": "form is wrong", # 格式错误(如果填了字符) "min_value": "too young", "max_value": "too old", }) # 路由:http://127.0.0.1:8000/auth/ 会转到这个视图函数 def check(request): # 处理验证的视图函数 obj = UserForm(request.POST) # 创建UserForm类的对象,并将request.POST传进去 # 调用UserForm类的基类Form的is_valid方法进行判断加工,返回True或False if obj.is_valid(): # 如果通过验证 data = obj.cleaned_data # 获取验证后的数据,data为字典 # 浏览器传进来的键必须与规则中的键一致,即request中的name和UserForm中的name键名一致 # 传入的键数目可以大于规则中的键数目 user_name = request.POST.get('name') # 获取并判断用户是否在数据库中,注意先验证,后拿数据库,可以减小数据库压力 is_in = models.Account.objects.filter(name=user_name) if is_in: # 另一种返回 return JsonResponse({"code": False , "mesg": "用户已存在"}) return HttpResponse("用户已存在") # 用户不在数据库中,则新增 models.Account.objects.create(name=data['name'], age=data['age']) # 另一种返回 return JsonResponse({"code": True, "mesg": "已新增"}) return HttpResponse("已新增") # 未通过验证,则返回所有错误信息obj.errors的Json格式的数据给浏览器 return HttpResponse(obj.errors.as_json())
- 路由写法
文件/main/urls.pyfrom django.urls import path from api import my_form urlpatterns = [ path('auth/', my_form.check), ]
- 数据库写法
文件/api/models.pyfrom django.db import models class Account(models.Model): name = models.CharField(max_length=24, primary_key=True) age = models.CharField(max_length=48)
2.2 验证结果
- name、age均不满足
- 有一个不满足
- 两者都满足
- 二次新增相同name
2.3 验证参数默认值
括号内为基类
继承基类的派生类都具有基类的参数
################# 重要 #####################
Field
required=True, 是否允许为空
error_messages=None, 错误信息 {'required': '是否可空', 'invalid': '格式错误'}
show_hidden_initial=False, 是否在当前插件后面再加一个隐藏的且具有默认值的插件(可用于检验两次输入是否一直)
validators=[], 自定义验证规则
localize=False, 是否支持本地化(用于时间转换)
################# 重要 #####################
CharField(Field)
max_length=None, 最大长度
min_length=None, 最小长度
strip=True 是否移除用户输入空白
################# 重要 #####################
IntegerField(Field)
max_value=None, 最大值
min_value=None, 最小值
FloatField(IntegerField)
...
################# 重要 #####################
DecimalField(IntegerField)
max_value=None, 最大值
min_value=None, 最小值
max_digits=None, 总长度
decimal_places=None, 小数位长度
BaseTemporalField(Field)
input_formats=None 时间格式化
################# 重要 #####################
DateField(BaseTemporalField) 格式:2015-09-01
TimeField(BaseTemporalField) 格式:11:12
DateTimeField(BaseTemporalField)格式:2015-09-01 11:12
DurationField(Field) 时间间隔:%d %H:%M:%S.%f
...
################# 重要 #####################
RegexField(CharField)
regex, 自定制正则表达式
max_length=None, 最大长度
min_length=None, 最小长度
error_message=None, 忽略,错误信息使用 error_messages={'invalid': '格式错误'}
EmailField(CharField)
...
FileField(Field)
allow_empty_file=False 是否允许空文件
ImageField(FileField)
...
注:需要PIL模块,pip3 install Pillow
以上两个字典使用时,需要注意两点:
- form表单中 enctype="multipart/form-data"
- view函数中 obj = MyForm(request.POST, request.FILES)
URLField(Field)
...
BooleanField(Field)
...
NullBooleanField(BooleanField)
...
ChoiceField(Field)
...
choices=(), 选项,如:choices = ((0,'上海'),(1,'北京'),)
required=True, 是否必填
widget=None, 插件,默认select插件
ComboField(Field)
fields=() 使用多个验证,如下:即验证最大长度20,又验证邮箱格式
fields.ComboField(fields=[
fields.CharField(max_length=20),
fields.EmailField(),
])
MultiValueField(Field)
PS: 抽象类,子类中可以实现聚合多个字典去匹配一个值,要配合MultiWidget使用
SplitDateTimeField(MultiValueField)
input_date_formats=None, 格式列表:['%Y--%m--%d', '%m%d/%Y', '%m/%d/%y']
input_time_formats=None 格式列表:['%H:%M:%S', '%H:%M:%S.%f', '%H:%M']
FilePathField(ChoiceField) 文件选项,目录下文件显示在页面中
path, 文件夹路径
match=None, 正则匹配
recursive=False, 递归下面的文件夹
allow_files=True, 允许文件
allow_folders=False, 允许文件夹
required=True,
widget=None,
GenericIPAddressField
protocol='both', both,ipv4,ipv6支持的IP格式
unpack_ipv4=False 解析ipv4地址,如果是::ffff:192.0.2.1时候,可解析为192.0.2.1,
PS:protocol必须为both才能启用
################# 重要 #####################
SlugField(CharField) 数字,字母,下划线,减号(连字符)
...
UUIDField(CharField) uuid类型
...