美多商城前三天重点内容大盘点

2019-07-18 14:41:22 浏览数 (1)

每日分享

代码语言: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( 创建工作的进程)特别重要,我们每次开始都需要启动。

代码语言:javascript复制
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

代码语言:javascript复制
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框架中的英文单词

0 人点赞