每日分享
代码语言:javascript复制This is your life. Do what you love, and do it often.
你自己的人生,要经常做你喜欢的事情。不要让其他人左右打扰你的人生。
美多商城前三天重点内容大盘点
文章导航
1.自定义Django认证系统用户模型类
2.跨域请求
3.celery异步任务发短信
4.JWT认证机制
5.自定义jwt扩展登录视图响应数据函数
6.自定义Django认证后端类(登录账户支持用户名和手机号)
7.QQ登录开发流程(流程图,可以自己画一下)
1.自定义Django认证系统用户模型类
1.1Django自带模型类介绍
Django中其实提供了用户模型类User保存用户的数据,让我们先来看一下自带的模型类都包含了些什么:
1.它包含了我们最常用的一些字段,如:username、password、email、isstaff(是否可以访问admin站点)、isactive(用户的账号是否激活)、issuperuser(是不是拥有超级管理员权限)、lastlogin(用户最后的登录时间)、datejoined(账户的创建时间)、firstname、last_name。
2.还有常用的方法:
set_password(raw_password)
设置用户密码,将用户输入的明文密码进行hash转换。
check_password(raw_password)
如果传入的明文密码是正确的返回True,和上面的配合使用。
上面的虽然很好,但是并不适用于特殊情况,比如我们在项目中需要定义一个手机号的字段,我们可以继承Django自带的模型类,然后扩展我们需要的字段即可。Django提供的用户模型类是 django.contrib.auth.models.AbstractUser
,我们导入便可使用。
1.2自定义用户模型类步骤
我们在编写子应用的目录apps中创建Django应用users,并在配置文件中注册users应用。
在创建好的应用models.py中定义用户的用户模型类。
代码语言:javascript复制class User(AbstractUser):
"""用户模型类"""
mobile = models.CharField(max_length=11, unique=True, verbose_name='手机号')
class Meta:
db_table = 'tb_users'
verbose_name = '用户'
verbose_name_plural = verbose_name
我们自定义的用户模型类还不能直接被Django的认证系统所识别,需要在配置文件中告知Django认证系统使用我们自定义的模型类。
在配置文件中进行设置
代码语言:javascript复制# AUTH_USER_MODEL = '子应用.模型类'
AUTH_USER_MODEL = 'users.User'
注意:我们对于AUTHUSERMODEL参数的设置一定要在第一次数据库迁移之前就设置好,否则后续使用可能出现未知错误。
执行数据库迁移
代码语言:javascript复制python manage.py makemigrations
python manage.py migrate
2.跨域请求
其实跨域请求很简单,就是源请求地址和被请求地址不是同源(同源地址要求两个url地址的IP、端口和协议完全一致),这个请求就是跨域请求。
在美多商城项目中,注册页面,我们填写了手机号获取短信验证码的时候,就是一个跨域请求。前端页面是http://www.meiduo.site:8080/register.html,而点击获取短信验证码按钮的时候,要访问后端的接口http://api.meiduo.site:8000/sms_codes/13188888888/。协议相同;IP其实也是相同的,虽然我们设置的不一样,但是他们指向的ip都是127.0.0.1;但是端口号不一样,前端访问的是8080,后端设置的端口是8000。其中前端的页面就是源请求地址,后端的页面就是被请求地址。
注意:浏览器在发起ajax跨域请求时,会有CORS跨域请求的限制。其他的形式,比如图片跳转地址或者表单提交的地址,在跨域请求的时候没有限制。CSRF跨站请求也是跨域请求。
在发起跨域请求时,在请求中携带一个请求头:
Origin:源请求地址
被请求的服务器在返回响应时,如果允许源地址对其进行跨域请求,需要在响应时携带一个响应头:
Access-Control-Allow-Origin:源请求地址
浏览器如果发现被请求的服务器在返回响应时,没有携带 Access-Control-Allow-Origin:源请求地址
响应头,浏览器会直接将请求驳回,然后进行报错。
2.1使用
安装
代码语言:javascript复制pip install django-cors-headers
添加应用
代码语言:javascript复制INSTALLED_APPS = (
...
'corsheaders',
...
)
中间件设置
代码语言:javascript复制MIDDLEWARE = [
'corsheaders.middleware.CorsMiddleware',
...
]
添加白名单
代码语言:javascript复制# CORS
CORS_ORIGIN_WHITELIST = (
'127.0.0.1:8080',
'localhost:8080',
'www.meiduo.site:8080',
)
CORS_ALLOW_CREDENTIALS = True # 允许携带cookie
凡是出现在白名单中的域名,都可以访问后端接口 CORSALLOWCREDENTIALS 指明在跨域访问中,后端是否支持对cookie的操作。
3.celery异步任务发短信
3.1原过程
客户端向服务器请求获取短信验证码,服务器中调用了一个方法 send_template_sms
然后向第三方云通讯发送了一个请求,请求云通讯发送短信,云通讯返回给服务器一个响应,最后服务器向客户端返回响应。
3.2问题
上面的问题就是,如果网络差,我们服务器向云通讯发送请求后,服务器长时间得不到回应,那么也没法给客户端返回响应,最直观的现象就是,前端页面的获取短信验证码按钮没有出现倒计时。有可能一直等待,用户不知道啊,用户会以为没有发送短信验证码,然后疯狂点,最后那么用户体验贼差,这网站还开不开,气走了上帝,流失了客户,损失多么惨重。
3.3celery发短信
概念:
1.任务执行者( worker
):提前创建的进程
2.任务发出者:发出任务信息,让执行者去调用某个函数( 任务函数
)
3.中间人( broker
):存放任务消息。
本质:通过提前创建的进程调用函数来实现异步的任务。
创建的进程可以在不同的服务器上。
特点:
1.任务执行者的进程可以单独在其他电脑上进行创建。
2.中间人又叫做任务队列,先添加到队列中的任务消息会先被worker所执行。
3.生产者-消费者模型。
注意:中间人可以是rabbit-mq,也可以是redis,我们使用redis。
发短信的过程:
这个过程就变成了:当用户点击了发送短信验证码的时候,客户端向服务器发送了一个请求来获取短信验证码,服务器立马向客户端返回响应(其实启动了异步任务,请求第三方发送短信验证码,正因为是异步,所以服务器不需等待云通讯的响应即可去干另一件事,就是向客户端返回响应),客户端开始倒计时。我们设置了60秒的等待时间,足以弥补网络的延迟。
3.4使用
1.安装
代码语言:javascript复制pip install celery
2.创建一个Celery类的对象并进行配置,是为了配置中间人的地址。
代码语言:javascript复制# main.py
from celery import Celery
# 创建Celery类的对象
celery_app = Celery('demo')
# 加载配置
celery_app.config_from_object('配置文件的包路径')
代码语言:javascript复制# config.py
# 设置中间人地址borker
# broker_url = 'redis://<host>:<port>/<db>'
broker_url = 'redis://127.0.0.1:6379/3'
3.封装任务函数
代码语言:javascript复制@celery_app.task(name='send_sms_code')
def send_sms_code(a,b):
# ...
pass
4.启动celery的worker( 创建工作的进程
)特别重要,我们每次开始都需要启动。
celery -A 'celery_app对象所在文件包路径' worker -l <日志级别>
日志级别:critial fatal、error、warn、info、debug
5.发出任务消息
代码语言:javascript复制send_sms_code.delay()
4.JWT认证机制
4.1session认证机制:
代码语言:javascript复制用户登录:
1.接收参数并进行校验(将用户名和密码校验)
2.检验用户名和密码是否正确
3.保存用户的登录信息
session['user_id'] = 2
session['username'] = 'ethanyan'
session['mobile'] = '13288888888'
4.返回应答,登录成功
在返回应答时,会让客户端保存cookie和sessionid( 客户端session信息标识
),在之后客户端访问服务器时,就会携带sessionid,服务器就可以根据sessionid取出对应的session信息并对用户登录的状态进行判断。
session认证机制存在问题:
a.session数据存储服务器,如果登录用户过多,会过多占用服务器存储空间。
b.session是依赖于cookie的,如果cookie被截获,可能会造成CSRF伪造。
c.对于分布式网站应用中,如果session存储在内存中,session的共享会产生问题。(在网站部署的时候,有很多服务器运行着,某台服务器内存中存着一位用户的session,其他服务器中是没有的。Nginx在转发的时候,有可能下次交给了其他服务器处理该用户的请求,然后就没有了给用户的一些信息,比如登录状态。)
优点:
a.存储在session中数据更加安全
4.2JWT认证机制
代码语言:javascript复制用户登录:
1.接收参数并进行校验(将用户名和密码校验)
2.检验用户名和密码是否正确
3.由服务器生成一个字符串(jwt token),保存了登录用户的身份信息
公安局(服务器)--->身份证(jwt token)
4.返回响应时,需要将jwt token返回给客户端
客户端需要将jwt token保存下来,然后在请求服务器时,如果需要对用户的身份进行认证,客户端则需要将jwt token传递给服务器,由服务器对jwt token进行校验,来对用户进行认证。
优点:
a.jwt token是由客户端进行保存的,不会占用服务器存储空间。
缺点:
a.因为jwt token是存储在客户端,所以jwt token不建议存放一些敏感数据。
jwt token字符串格式:
是一个字符串,由三部分组成,用 .
隔开
a.header(头部)
代码语言:javascript复制{
"token类型",
"signature签名加密算法",
}
使用base64对头部信息进行加密( 编码
),加密之后生成的字符串就是header内容。
b.payload(载荷)
存储的是有效数据
代码语言:javascript复制{
"user_id":"用户id",
"username":"用户名",
"email":"邮箱",
"exp":"token有效时间"
...
}
上面的exp(token有效期)是UTC时间,我们采用的北京时间相比是领先8个小时的。
使用base64对载荷信息进行加密( 编码
),加密之后生成的字符串就是payload内容。
c.signature(签名)
作用:防止将jwt token被伪造
1.签名的生成过程
答:服务器在生成jwt token时,会将header和payload字符串进行拼接,用 .
隔开,然后使用一个只有服务器知道的密钥对拼接后的内容进行加密,加密之后生成的字符串就是signature内容。
2.签名验证过程?
答:当客户端将jwt token传递给服务器之后,服务器首先需要进行签名认证,签名验证的过程:
- 将客户端传递的jwt token中的header和payload字符串进行拼接,用
.
隔开 - 使用服务器之间的密钥对拼接之后的字符串进行加密
- 将加密之后的内容和将客户端传递的jwt token中signature进行对比,如果不一致,就说明jwt token是被伪造的。
注意点:
a.payload不要存放一些敏感数据
b.服务器密钥需要保持好,
c.如果可以,使用HTTPS协议。
5.自定义jwt扩展登录视图响应数据函数
jwt扩展中提供了一个登录视图 obtain_jwt_token
这个登录视图就是接收username和password,并对账户名和密码进行校验,校验通过之后会生成一个jwt token,并在响应时返回。
自定义jwt扩展登录视图相应数据的函数:
代码语言:javascript复制def jwt_response_payload_handler(token,user=None,request=None):
"""
自定义jwt扩展登录视图的响应数据函数
"""
return {
'user_id':user.id,
'username':user.username,
'token':token
}
配置:
代码语言:javascript复制# JWT扩展配置
JWT_AUTH = {
# 设置JWT的有效时间
'JWT_EXPIRATION_DELTA': datetime.timedelta(days=1),
# 指定jwt扩展登录视图响应数据函数
'JWT_RESPONSE_PAYLOAD_HANDLER':
'users.utils.jwt_response_payload_handler'
}
6.自定义Django认证后端类(登录账户支持用户名和手机号)
1. obtain_jwt_token
登录视图中没有自己实现账户名和密码校验的代码,而是调用了Django认证系统中一个函数进行账户和密码的校验。
2.认证系统中的 authenticate
from django.contrib.auth import authenticate
而 authenticate
方法内容也没有自己实现账户和密码校验的代码,而是调用Django认证后端类中 authenticate
进行账户和密码的校验。
3.Django认证后端类
代码语言:javascript复制from django.contrib.auth.backends import ModelBackend
在 ModelBackend
类中 authenticate
最终实现了账户和密码校验代码,但是账户仅支持用户名。
自定义Django认证后端类:
代码语言:javascript复制class UsernameModelAuthBackend(ModelBackend):
def authenticate(self,request,username=None,password=None,**kwargs):
"""
username既可以传递用户名也可以传递手机号
username:用户名或者手机号
password:密码
"""
# 根据用户名或手机号查询用户的信息
pass
# 如果用户存在,再校验密码
pass
指定Django认证后端类:
代码语言:javascript复制AUTHENTICATION_BACKENDS = ['自定义Django认证后端类']
7.QQ登录开发流程(流程图,可以自己画一下)
1.客户端请求获取QQ登录网址。
2.我们自己服务器返回QQ登录网址和参数给客户端。
3.客户端请求QQ登录网址。
4.QQ服务器最终返回QQ授权登录页面。
5.用户授权登录QQ。
6.QQ服务器响应时让客户端重定向访问callback回调网址,并携带code和state参数。
7.浏览访问callback回调网址。客户端还向我们自己的服务器发起一个页面请求,获取QQ登录用户openid并处理,传递code。
8.我们自己的服务器凭code请求QQ服务器获取access_token。
9.qq服务器返回access_token。
10.我们自己的服务器凭access_token请求访问QQ服务器获取openid。
11.QQ服务器返回openid。
12.我们的服务器根据openid判断是否绑定过本网站用户(查一下我们数据库中的表)。
13.如果绑定过,我们的服务器直接签发jwt token并返回给客户端。
14.如果未绑定过,我们自己的服务器将openid加密并返回给客户端。
15.客户端请求绑定QQ登录用户。
16.我们自己的服务器保存绑定的数据。
17.我们自己的服务器签发jwt token并返回给客户端。
优质文章推荐:
公众号使用指南
redis操作命令总结
前端中那些让你头疼的英文单词
Flask框架重点知识总结回顾
项目重点知识点详解
难点理解&面试题问答
flask框架中的一些常见问题
团队开发注意事项
浅谈密码加密
Django框架中的英文单词
Django中数据库的相关操作
DRF框架中的英文单词