本教程的知识点为: 项目准备 项目准备 配置 1. 修改settings/dev.py 文件中的路径信息 2. INSTALLED_APPS 3. 数据库 用户部分 图片 1. 后端接口设计: 视图原型 2. 具体视图实现 用户部分 使用Celery完成发送 判断帐号是否存在 1. 判断用户名是否存在 后端接口设计: 用户部分 JWT 什么是JWT 起源 传统的session认证 用户部分 登录 1. 业务说明 2. 后端接口设计 3. 后端实现 登录 使用登录的流程 创建模型类 urllib使用说明 登录回调处理 登录 使用登录的流程 创建模型类 urllib使用说明 绑定用户身份接口 邮件与验证 学习目标: 业务说明: 技术说明: 保存邮箱并发送验证邮件 省市区地址查询 数据库建表 说明 页面静态化 注意 定时任务 安装 部分 详情页 异步任务的触发 。 后端接口设计 收货地址 使用缓存 安装 使用方法 为省市区视图添加缓存 数据库表设计 表结构 数据表结构 首页数据表结构 Docker使用 Docker简介 用户浏览历史记录 1. 保存 后端接口设计 后端实现 搜索 1. 需求分析 2. 搜索引擎原理 3. Elasticsearch 部分 业务需求分析 技术实现 数据存储设计 1. Redis保存已登录用户 商品部分 业务需求分析 技术实现 查询数据 1. 后端接口设计 部分 业务需求分析 技术实现 登录合并 修改登录视图 部分 保存 1. 后端接口设计 2. 后端实现 保存的思路 创建数据库模型类 接入 开发平台登录 沙箱环境 Xadmin 1. 安装 2. 使用 站点的全局配置 站点Model管理。 在Ubuntu中安装 2. 启动与停止 3. 镜像操作 端与自定义文件存储系统 1. 的Python客户端 安装 使用。
完整笔记代码请移步:
博客文章1: https://blog.csdn.net/m0_72919230/article/details/141266371
博客文章2: https://blog.csdn.net/m0_72919230/article/details/141200991
感兴趣的小伙伴可以自取哦~
全套教程部分目录:
部分文件图片:
图形验证码
图形验证码接口设计和定义
1. 图形验证码接口设计
1.请求方式
选项 | 方案 |
---|---|
请求方法 | GET |
请求地址 | image_codes/(?P[\w-]+)/ |
> | |
2.请求参数:路径参数 |
参数名 | 类型 | 是否必传 | 说明 |
---|---|---|---|
uuid | string | 是 | 唯一编号 |
> | |||
3.响应结果:image/jpg |
2. 图形验证码接口定义
1.图形验证码视图
class ImageCodeView(View):
"""图形验证码"""
def get(self, request, uuid):
"""
:param request: 请求对象
:param uuid: 唯一标识图形验证码所属于的用户
:return: image/jpg
"""
pass
2.总路由
# verifications
url(r'^', include('verifications.urls')),
3.子路由
# 图形验证码
url(r'^image_codes/(?P<uuid>[\w-]+)/$', views.ImageCodeView.as_view()),
图形验证码后端逻辑
1. 准备captcha扩展包
提示:
captcha
扩展包用于后端生成图形验证码
可能出现的错误
- 报错原因:环境中没有Python处理图片的库:PIL
解决办法
- 安装Python处理图片的库:
pip install Pillow
2. 准备Redis数据库
准备Redis的2号库存储验证码数据
"verify_code": { # 验证码
"BACKEND": "django_redis.cache.RedisCache",
"LOCATION": "redis://127.0.0.1:6379/2",
"OPTIONS": {
"CLIENT_CLASS": "django_redis.client.DefaultClient",
}
},
3. 图形验证码后端逻辑实现
class ImageCodeView(View):
"""图形验证码"""
def get(self, request, uuid):
"""
:param request: 请求对象
:param uuid: 唯一标识图形验证码所属于的用户
:return: image/jpg
"""
# 生成图片验证码
text, image = captcha.generate_captcha()
# 保存图片验证码
redis_conn = get_redis_connection('verify_code')
redis_conn.setex('img_%s' % uuid, constants.IMAGE_CODE_REDIS_EXPIRES, text)
# 响应图片验证码
return http.HttpResponse(image, content_type='image/jpg')
图形验证码前端逻辑
1. Vue实现图形验证码展示
1.register.js
mounted(){
// 生成图形验证码
this.generate_image_code();
},
methods: {
// 生成图形验证码
generate_image_code(){
// 生成UUID。generateUUID() : 封装在common.js文件中,需要提前引入
this.uuid = generateUUID();
// 拼接图形验证码请求地址
this.image_code_url = "/image_codes/" + this.uuid + "/";
},
......
}
2.register.html
<li>
<label>图形验证码:</label>
<input type="text" name="image_code" id="pic_code" class="msg_input">
<img :src="image_code_url" @click="generate_image_code" alt="图形验证码" class="pic_code">
<span class="error_tip">请填写图形验证码</span>
</li>
3.图形验证码展示和存储效果
2. Vue实现图形验证码校验
1.register.html
<li>
<label>图形验证码:</label>
<input type="text" v-model="image_code" @blur="check_image_code" name="image_code" id="pic_code" class="msg_input">
<img :src="image_code_url" @click="generate_image_code" alt="图形验证码" class="pic_code">
<span class="error_tip" v-show="error_image_code">[[ error_image_code_message ]]</span>
</li>
2.register.js
check_image_code(){
if(!this.image_code) {
this.error_image_code_message = '请填写图片验证码';
this.error_image_code = true;
} else {
this.error_image_code = false;
}
},
3.图形验证码校验效果
短信验证码
短信验证码逻辑分析
知识要点
- 保存短信验证码是为注册做准备的。
- 为了避免用户使用图形验证码恶意测试,后端提取了图形验证码后,立即删除图形验证码。
- Django不具备发送短信的功能,所以我们借助第三方的容联云通讯短信平台来帮助我们发送短信验证码。
容联云通讯短信平台
1. 容联云通讯短信平台介绍
1.容联云官网
- 容联云通讯网址:[
- 注册并登陆
2.容联云管理控制台
3.容联云创建应用
4.应用申请上线,并进行资质认证
5.完成资质认证,应用成功上线
6.添加测试号码
7.短信模板
2. 容联云通讯短信SDK测试
1.模板短信SDK下载
-
[
2.模板短信SDK使用说明
-
[
3.集成模板短信SDK
-
CCPRestSDK.py
:由容联云通讯开发者编写的官方SDK文件,包括发送模板短信的方法 ccp_sms.py
:调用发送模板短信的方法
4.模板短信SDK测试
ccp_sms.py
文件中
# -*- coding:utf-8 -*-
from verifications.libs.yuntongxun.CCPRestSDK import REST
# 说明:主账号,登陆云通讯网站后,可在"控制台-应用"中看到开发者主账号ACCOUNT SID
_accountSid = '8aaf070862181ad5016236f3bcc811d5'
# 说明:主账号Token,登陆云通讯网站后,可在控制台-应用中看到开发者主账号AUTH TOKEN
_accountToken = '4e831592bd464663b0de944df13f16ef'
# 请使用管理控制台首页的APPID或自己创建应用的APPID
_appId = '8aaf070868747811016883f12ef3062c'
# 说明:请求地址,生产环境配置成app.cloopen.com
_serverIP = 'sandboxapp.cloopen.com'
# 说明:请求端口 ,生产环境为8883
_serverPort = "8883"
# 说明:REST API版本号保持不变
_softVersion = '2013-12-26'
# 云通讯官方提供的发送短信代码实例
# 发送模板短信
# @param to 手机号码
# @param datas 内容数据 格式为数组 例如:{'12','34'},如不需替换请填 ''
# @param $tempId 模板Id
def sendTemplateSMS(to, datas, tempId):
# 初始化REST SDK
rest = REST(_serverIP, _serverPort, _softVersion)
rest.setAccount(_accountSid, _accountToken)
rest.setAppId(_appId)
result = rest.sendTemplateSMS(to, datas, tempId)
print(result)
for k, v in result.items():
if k == 'templateSMS':
for k, s in v.items():
print('%s:%s' % (k, s))
else:
print('%s:%s' % (k, v))
if __name__ == '__main__':
# 注意: 测试的短信模板编号为1
sendTemplateSMS('17600992168', ['123456', 5], 1)
5.模板短信SDK返回结果说明
{
'statusCode': '000000', // 状态码。'000000'表示成功,反之,失败
'templateSMS':
{
'smsMessageSid': 'b5768b09e5bc4a369ed35c444c13a1eb', // 短信唯一标识符
'dateCreated': '20190125185207' // 短信发送时间
}
}
3. 封装发送短信单例类
1.封装发送短信单例类
class CCP(object):
"""发送短信的单例类"""
def __new__(cls, *args, **kwargs):
# 判断是否存在类属性_instance,_instance是类CCP的唯一对象,即单例
if not hasattr(CCP, "_instance"):
cls._instance = super(CCP, cls).__new__(cls, *args, **kwargs)
cls._instance.rest = REST(_serverIP, _serverPort, _softVersion)
cls._instance.rest.setAccount(_accountSid, _accountToken)
cls._instance.rest.setAppId(_appId)
return cls._instance
2.封装发送短信单例方法
def send_template_sms(self, to, datas, temp_id):
"""
发送模板短信单例方法
:param to: 注册手机号
:param datas: 模板短信内容数据,格式为列表,例如:['123456', 5],如不需替换请填 ''
:param temp_id: 模板编号,默认id为1的模板
:return: 发短信结果
"""
result = self.rest.sendTemplateSMS(to, datas, temp_id)
if result.get("statusCode") == "000000":
# 返回0,表示发送短信成功
return 0
else:
# 返回-1,表示发送失败
return -1
3.测试单例类发送模板短信结果
if __name__ == '__main__':
# 注意: 测试的短信模板编号为1
CCP().send_template_sms('17600992168', ['123456', 5], 1)
4. 知识要点
- 容联云通讯只是发送短信的平台之一,还有其他云平台可用,比如,阿里云等,实现套路都是相通的。
- 将发短信的类封装为单例,属于性能优化的一种方案。
短信验证码后端逻辑
1. 短信验证码接口设计
1.请求方式
选项 | 方案 |
---|---|
请求方法 | GET |
请求地址 | /sms_codes/(?P1[3-9]\d{9})/ |
> | |
2.请求参数:路径参数和查询字符串 |
参数名 | 类型 | 是否必传 | 说明 |
---|---|---|---|
mobile | string | 是 | 手机号 |
image_code | string | 是 | 图形验证码 |
uuid | string | 是 | 唯一编号 |
> | |||
3.响应结果:JSON |
字段 | 说明 |
---|---|
code | 状态码 |
errmsg | 错误信息 |
2. 短信验证码接口定义
class SMSCodeView(View):
"""短信验证码"""
def get(self, reqeust, mobile):
"""
:param reqeust: 请求对象
:param mobile: 手机号
:return: JSON
"""
pass
3. 短信验证码后端逻辑实现
class SMSCodeView(View):
"""短信验证码"""
def get(self, reqeust, mobile):
"""
:param reqeust: 请求对象
:param mobile: 手机号
:return: JSON
"""
# 接收参数
image_code_client = reqeust.GET.get('image_code')
uuid = reqeust.GET.get('uuid')
# 校验参数
if not all([image_code_client, uuid]):
return http.JsonResponse({'code': RETCODE.NECESSARYPARAMERR, 'errmsg': '缺少必传参数'})
# 创建连接到redis的对象
redis_conn = get_redis_connection('verify_code')
# 提取图形验证码
image_code_server = redis_conn.get('img_%s' % uuid)
if image_code_server is None:
# 图形验证码过期或者不存在
return http.JsonResponse({'code': RETCODE.IMAGECODEERR, 'errmsg': '图形验证码失效'})
# 删除图形验证码,避免恶意测试图形验证码
try:
redis_conn.delete('img_%s' % uuid)
except Exception as e:
logger.error(e)
# 对比图形验证码
image_code_server = image_code_server.decode() # bytes转字符串
if image_code_client.lower() != image_code_server.lower(): # 转小写后比较
return http.JsonResponse({'code': RETCODE.IMAGECODEERR, 'errmsg': '输入图形验证码有误'})
# 生成短信验证码:生成6位数验证码
sms_code = '%06d' % random.randint(0, 999999)
logger.info(sms_code)
# 保存短信验证码
redis_conn.setex('sms_%s' % mobile, constants.SMS_CODE_REDIS_EXPIRES, sms_code)
# 发送短信验证码
CCP().send_template_sms(mobile,[sms_code, constants.SMS_CODE_REDIS_EXPIRES // 60], constants.SEND_SMS_TEMPLATE_ID)
# 响应结果
return http.JsonResponse({'code': RETCODE.OK, 'errmsg': '发送短信成功'})
短信验证码前端逻辑
1. Vue绑定短信验证码界面
1.register.html
<li>
<label>短信验证码:</label>
<input type="text" v-model="sms_code" @blur="check_sms_code" name="sms_code" id="msg_code" class="msg_input">
<a @click="send_sms_code" class="get_msg_code">[[ sms_code_tip ]]</a>
<span class="error_tip" v-show="error_sms_code">[[ error_sms_code_message ]]</span>
</li>
2.register.js
check_sms_code(){
if(this.sms_code.length != 6){
this.error_sms_code_message = '请填写短信验证码';
this.error_sms_code = true;
} else {
this.error_sms_code = false;
}
},
2. axios请求短信验证码
1.发送短信验证码事件处理
send_sms_code(){
// 避免重复点击
if (this.sending_flag == true) {
return;
}
this.sending_flag = true;
// 校验参数
this.check_mobile();
this.check_image_code();
if (this.error_mobile == true || this.error_image_code == true) {
this.sending_flag = false;
return;
}
// 请求短信验证码
let url = '/sms_codes/' + this.mobile + '/?image_code=' + this.image_code+'&uuid='+ this.uuid;
axios.get(url, {
responseType: 'json'
})
.then(response => {
if (response.data.code == '0') {
// 倒计时60秒
var num = 60;
var t = setInterval(() => {
if (num == 1) {
clearInterval(t);
this.sms_code_tip = '获取短信验证码';
this.sending_flag = false;
} else {
num -= 1;
// 展示倒计时信息
this.sms_code_tip = num + '秒';
}
}, 1000, 60)
} else {
if (response.data.code == '4001') {
this.error_image_code_message = response.data.errmsg;
this.error_image_code = true;
} else { // 4002
this.error_sms_code_message = response.data.errmsg;
this.error_sms_code = true;
}
this.generate_image_code();
this.sending_flag = false;
}
})
.catch(error => {
console.log(error.response);
this.sending_flag = false;
})
},
2.发送短信验证码效果展示
补充注册时短信验证逻辑
1. 补充注册时短信验证后端逻辑
1.接收短信验证码参数
sms_code_client = request.POST.get('sms_code')
2.保存注册数据之前,对比短信验证码
redis_conn = get_redis_connection('verify_code')
sms_code_server = redis_conn.get('sms_%s' % mobile)
if sms_code_server is None:
return render(request, 'register.html', {'sms_code_errmsg':'无效的短信验证码'})
if sms_code_client != sms_code_server.decode():
return render(request, 'register.html', {'sms_code_errmsg': '输入短信验证码有误'})
2. 补充注册时短信验证前端逻辑
1.register.html
<li>
<label>短信验证码:</label>
<input type="text" v-model="sms_code" @blur="check_sms_code" name="sms_code" id="msg_code" class="msg_input">
<a @click="send_sms_code" class="get_msg_code">[[ sms_code_tip ]]</a>
<span v-show="error_sms_code" class="error_tip">[[ error_sms_code_message ]]</span>
{% if sms_code_errmsg %}
<span class="error_tip">{{ sms_code_errmsg }} </span>
{% endif %}
</li>