day4~day6
JWT的使用
什么是JWT
jwt token 简单来说就是身份的绑定、认定,便于这个是属于自己的,下次登录,再次确认的身份信息就可以了
认证成功后会保存在session中,用于存储
CSRF:基于cookie进行用户的识别,如果别截获,用户会受到跨域请求伪造的攻击
JWT的流程
· 用户使用用户名密码
来请求服务器
· 服务器进行验证用户的信息
· 服务器通过验证发送给用户一个token
· 客户端存储token,并在每次请求时附送上这个token值
· 服务端验证token值,并返回数据
这个token必须要在每次请求时传递给服务端,它应该保存在请求头里, 另外,服务端要支持CORS(``跨来源资源共享``)
策略。
JWT的样子
jwt是由三段信息构成的,将他们三个拼接,组合就成了jwt的字符串。例如这个:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ
第一部分是头部
由两个组成:
{
‘typ’: ‘JWT’,
‘alg’: ‘HS256’
}
并进行base64编码,其中的‘=’替换为“”“”,为第一部分
eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9
第二部分就是payload
存放有效信息的地方,重点包含三个部分
· 标准中注册的声明
· 公共的声明
· 私有的声明
标准中注册的声明(建议但不强制使用) :
· iss: jwt签发者
· sub: jwt所面向的用户
· aud: 接收jwt的一方
· exp: jwt的过期时间,这个过期时间必须要大于签发时间
· nbf: 定义在什么时间之前,该jwt都是不可用的.
· iat: jwt的签发时间
· jti: jwt的唯一身份标识,主要用来作为一次性token,从而回避重放攻击。
公共的声明: 公共的声明可以添加任何的信息,一般添加用户的相关信息或其他业务需要的必要信息,但不建议添加敏感信息,因为该部分在客户端可解码.
私有的声明: 私有声明是提供者和消费者所共同定义的声明,一般不建议存放敏感信息,因为base64是对称解码的,意味着该部分信息可以归类为明文信息。
{
“sub”: “1234567890”,
“name”: “John Doe”,
“admin”: true
}
也是同样,进行base64编码,‘=’号替换为“”“”,英文的双引号,得到了的部分。
eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9
第三部分是 signature(签名)
jwt简单就是一个签名的信息,由三部分组成:
· header (base64后的)
· payload (base64后的)
· secret
这个部分就是需要base64编码后的header和base64编码后的payload使用点(.)进行连接组成字符串,然后header中的加密方式加secret(秘密的)组合加密,在进行base64编码,就构成了第三部分。
把这三部分,连接成一个完成的字符串,就构成了一个完整的jwt。(就如果我上面举的例子)
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ
注意: secret是保存在服务器端的,jwt的签发生成也是在服务器端的,secret就是用来进行jwt的签发和jwt的验证,所以,它就是你服务端的私钥,在任何场景都不应该流露出去。一旦客户端得知这个secret,那就意味着客户端是可以自我签发jwt了.
短信验证码
容联云介绍及文档
容联云地址:https://www.yuntongxun.com/
文档:https://doc.yuntongxun.com/p/5a531a353b8496dd00dcdfe2
容联云发送短信文档
python SDK文档:https://doc.yuntongxun.com/p/5f029ae7a80948a1006e776e
安装SDK
pip install ronglian_sms_sdk
思路图:
在users应用下新建utils.py, 内部定义发送短信的方法
from ronglian_sms_sdk import SmsSDK
import json
import random
accId = '8a216da8762cb4570176f6b1f0c54906'
accToken = '30e8687e49a24bd3b7e5e2e5f0048697'
appId = '8a216da8762cb4570176f6b1f1ff490c'
def send_message(sms_code, mobile, expire=5):
sdk = SmsSDK(accId, accToken, appId)
tid = '1' # 容联云分配的一个测试短信验证码模版
datas = ("%s"%sms_code, '%s'%expire)
resp = sdk.sendMessage(tid, mobile, datas)
print(resp)
res_json = json.loads(resp)
print(res_json.get('statusCode'))
if res_json.get('statusCode') == '000000':
return True
else:
return False
短信验证码接口
class SmsCodeAPIView(APIView):
# 生成短信验证码
def get(self, request):
mobile = request.query_params.get('mobile')
# 生成随机码
sms_code = random.randint(10000, 99999)
result = send_message(sms_code, mobile) #
if not result:
return Response({'msg': '发送失败', 'code': 400})
else:
# 发送短信成功,保存sms_code到redis当中
redis_conn = redis.Redis(host="localhost", port=6379)
key = "sms_%s"%mobile
redis_conn.set(key, sms_code, ex=300)
return Response({'msg': '发送成功', 'code': 200})
Celery异步发送短信
celery介绍
Celery安装
celery,分布式异步任务队列
eventlet,并发网络库 gevent 协程库
pip install celery==4.4.7
pip install eventlet==0.26.1
创建worker
- 创建一个文件laufing.py,内部编写如下代码
#实例化对象
from celery import Celery
# 第一个参数worker name
# broker 代理,消息中间件
app = Celery("myworker", broker="redis://:laufing@localhost:6379/4")
#也可以app.conf.broker_url = "xxxx"
#创建任务函数
@app.task
def task1():
print("正在执行任务...")
- 前台启动worker进程。
- cmd命令输入如下:
windows 下需要加-P eventlet 或者 -P gevent
#因为celery 4.x 对window支持的不太好
celery -A 脚本名 worker --loglevel=info -P eventlet
#linux
celery -A 脚本名 worker -l info
- 在django项目主应用下创建celery.py文件,配置以下内容:
# celery.py文件
import os
from celery import Celery
from django.conf import settings
# 为celery配置环境变量,识别和加载django的配置文件
# 因为worker是脱离django启动的,且依赖其配置
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'mdpro.settings') # 主应用
# 创建celery实例
app = Celery('mdpro')
# 指定celery消息队列的配置
app.config_from_object('mdpro.config', namespace='CELERY')
#也可以app.conf.update(BROKER_URL="XXX")
# 从所有的django-app中加载任务
app.autodiscover_tasks(settings.INSTALLED_APPS)
- 在django项目主应用mdpro下创建config.py文件,配置以下内容:
# 消息中间人broker设置
broker_url = 'redis://127.0.0.1:6379/15' #不能写localhost
# 结果存储设置
result_backend = 'redis://127.0.0.1:6379/14'
- 在django项目其他应用下,这里是users,创建tasks.py
# tasks.py
from ronglian_sms_sdk import SmsSDK
from mdpro.celery import app
import json
import random
accId = '8a216da8757784cd017586e2a0280446'
accToken = '92fbee01e5474904a437b062ea43baf4'
appId = '8a216da8757784cd017586e2a0f4044c'
@app.task
def send_message(phone, msg_code):
sdk = SmsSDK(accId, accToken, appId)
tid = '1' # 容联云分配的一个测试短信验证码模版
mobile = phone # 接收短信的手机号
datas = (msg_code, '5')
resp = sdk.sendMessage(tid, mobile, datas)
resp_json = json.loads(resp)
return resp_json
- 视图中发送异步任务
class GenerateVerifyCode(APIView):
"""
生成手机号验证码
"""
def post(self, request):
code_id = request.data.get('code_id')
phone = request.data.get('phone')
msg_code = '%06d' % random.randint(0, 1000000)
#发送任务
res = send_message.delay(phone, msg_code) # 0或者 11111
print("异步队列的响应", res) #
sms_redis.set(code_id, msg_code, ex=300)
return Response({'msg': 'OK', 'code': 200})
以上就是异步的创建应用
code == “204” 验证码过期或者错误
code == “205” 用户名或密码错误
code == “206” 没有管理员权限
code == “207” 密码已重置
code == “200” 登录成功, 进入首页
code == “201” 注册成功,需申请管理员权限 可以使用django的超级管理后台
code == 208 第一次登录需使用手机号进行注册