1. Cookie与Session

1.1 Cookie

1.1.1 特点

  • Cookie以键值对的格式进行信息的存储。
  • Cookie基于域名安全,不同域名的Cookie是不能互相访问的,如访问itcast.cn时向浏览器中写了Cookie信息,使用同一浏览器访问baidu.com时,无法访问到itcast.cn写的Cookie信息。
  • 当浏览器请求某网站时,会将浏览器存储的跟网站相关的所有Cookie信息提交给网站服务器。

    1.1.2 使用

  1. 设置Cookie:使用**HttpResponse.set_cookie()**方法来设置

    1. HttpResponse.set_cookie(cookie名称, value=cookie值, max_age=cookie有效期) # max_age单位为秒,默认为None 。如果是临时cookie,可将max_age设置为None。
    1. def cookie(request):
    2. response = HttpResponse('ok')
    3. response.set_cookie('itcast1', 'python1') # 临时cookie
    4. response.set_cookie('itcast2', 'python2', max_age=3600) # 有效期一小时
    5. return response
  2. 读取Cookie:通过HttpResponse对象的COOKIES属性来读取本次请求携带的cookie值。**request.COOKIES**为字典类型

    1. def cookie(request):
    2. cookie1 = request.COOKIES.get('itcast1')
    3. print(cookie1)
    4. return HttpResponse('OK')
  3. 删除Cookie:通过HttpResponse对象中的delete_cookie方法来删除。

    1. response.delete_cookie('itcast2')

    1.2 Session

    1.2.1 启用session

    Django项目默认启用Session。如需禁用session,只需要在MIDDLEWARE中注释掉即可。

    1.2.2 存储方式

    在settings.py文件中,可以设置session数据的存储方式,可以保存在数据库、本地缓存等。

  4. 数据库:

    1. 设置**SESSION_ENGINE='django.contrib.sessions.backends.db'**
    2. 在项INSTALLED_APPS中安装Session应用,之后会在数据库中自动生成含有键,值,过期时间的数据库表。
      1. INSTALLED_APPS = [
      2. ...,
      3. 'django.contrib.sessions'
      4. ]
  5. Redis

    1. 安装包:pip install django-redis
    2. 配置:
      1. CACHES = {
      2. 'default': {
      3. 'BACKEND': 'django_redis.cache.RedisCache',
      4. 'LOCATION': 'redis://127.0.0.1:6379/1',
      5. 'OPTIONS': {
      6. 'CLIENT_CLASS': 'django_redis.client.DefaultClient',
      7. }
      8. }
      9. }
      10. SESSION_ENGINE = 'django.contrib.sessions.backends.cache'
      11. SESSION_CACHE_ALIAS = 'default'
      注意事项:
      如果redis的ip地址不是本地回环127.0.0.1,而是其他地址,访问Django时,可能出现Redis连接错误:
      redis_error.png
      解决办法:
  6. 打开redis的配置文件**redis.conf**,并将ip地址添加进去。

  7. 重新启动redis服务:sudo service redis-server restart

    1.2.3 Session使用

    通过HttpRequest对象的session属性进行会话的读写操作。

  8. 以键值对的格式写session

    1. request.session['键']=值
  9. 根据键读取值

    1. request.session.get('键',默认值)
  10. 清除所有session,在存储中删除值部分

    1. request.session.clear()
  11. 清除session数据,在存储中删除session的整条数据

    1. request.session.flush()
  12. 删除session中的指定键及值,在存储中只删除某个键及对应的值。

    1. del request.session['键']
  13. 设置session的有效期

    1. request.session.set_expiry(value)
  • 如果value是一个整数,session将在value秒没有活动后过期。
  • 如果value为0,那么用户session的Cookie将在用户的浏览器关闭时过期。
  • 如果value为None,那么session有效期将采用系统默认值, 默认为两周,可以通过在settings.py中设置SESSION_COOKIE_AGE来设置全局默认值。

2. JWT的使用

后端在接受前端登录请求并验证完用户的身份后,需要向用户签发JWT,在需要用到用户身份信息的时候,还需要校验用户的JWT。关于签发和校验JWT,我们可以使用**Django REST framework JWT****PyJWT**扩展完成。

2.1 drf JWT实现

2.1.1 安装配置

安装:**pip install djangorestframework-jwt**

  1. REST_FRAMEWORK = {
  2. 'DEFAULT_AUTHENTICATION_CLASSES': (
  3. 'rest_framework_jwt.authentication.JSONWebTokenAuthentication',
  4. 'rest_framework.authentication.SessionAuthentication',
  5. 'rest_framework.authentication.BasicAuthentication',
  6. ),
  7. }
  8. JWT_AUTH = {
  9. 'JWT_EXPIRATION_DELTA': datetime.timedelta(days=1), # token有效期
  10. }

2.1.2 账户登录

1. 业务说明

验证用户名和密码,验证成功后,为用户签发JWT,前端将签发的JWT保存下来。

2. 后端接口设计

请求方式: POST meiduo_admin/authorizations/
请求参数: JSON 或 表单

参数名 类型 是否必须 说明
username str 用户名
password str 密码

返回数据: JSON

  1. {
  2. "username": "python",
  3. "user_id": 1,
  4. "token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjo5LCJ1c2VybmFtZSI6InB5dGhvbjgiLCJleHAiOjE1MjgxODI2MzQsImVtYWlsIjoiIn0.ejjVvEWxrBvbp18QIjQbL1TFE0c0ejQgizui_AROlAU"
  5. } #放在请求体的形式
返回值 类型 是否必须 说明
username str 用户名
id int 用户id
token str 身份认证凭据

3. 后端实现

Django REST framework JWT提供了登录签发JWT的视图,可以直接使用
from rest_framework_jwt.views import obtain_jwt_token urlpatterns = [ url(r’^authorizations/$’, obtain_jwt_token), ]
但是默认的返回值仅有token,我们还需在返回值中增加username和user_id。
通过修改该视图的返回值可以完成我们的需求。
在users/utils.py 中,创建

  1. def jwt_response_payload_handler(token, user=None, request=None):
  2. """ 自定义jwt认证成功返回数据 """
  3. return {
  4. 'token': token,
  5. 'id': user.id,
  6. 'username': user.username
  7. }

修改配置文件

  1. # JWT配置
  2. JWT_AUTH = {
  3. 'JWT_EXPIRATION_DELTA': datetime.timedelta(days=1),
  4. 'JWT_RESPONSE_PAYLOAD_HANDLER': 'meiduo_admin.utils.jwt_response.jwt_response_payload_handler',
  5. }

4. 增加支持管理员用户登录账号

JWT扩展的登录视图,在收到用户名与密码时,也是调用Django的认证系统中提供的authenticate()来检查用户名与密码是否正确。
我们可以通过修改Django认证系统的认证后端(主要是authenticate方法)来支持登录账号既可以是用户名也可以是手机号。
修改Django认证系统的认证后端需要继承django.contrib.auth.backends.ModelBackend,并重写authenticate方法。
authenticate(self, request, username=None, password=None, **kwargs)方法的参数说明:

  • request 本次认证的请求对象
  • username 本次认证提供的用户账号
  • password 本次认证提供的密码

我们想要让管理员用户才能登录我们的admin后台,这时我们就要修改django原有的用户验证方法。
重写authenticate方法的思路:

  1. 根据username参数查找用户User对象,在查询条件中在加上is_staff=True的条件
  2. 若查找到User对象,调用User对象的check_password方法检查密码是否正确

在meiduo_mall/utils/authenticate.py中编写:

  1. from django.contrib.auth.backends import ModelBackend
  2. import re
  3. from users.models import User
  4. class MeiduoModelBackend(ModelBackend):
  5. def authenticate(self, request, username=None, password=None, **kwargs):
  6. # 判断是否通过vue组件发送请求
  7. if request is None:
  8. try:
  9. user = User.objects.get(username=username, is_staff=True)
  10. except:
  11. return None
  12. # 判断密码
  13. if user.check_password(password):
  14. return user
  15. else:
  16. # 变量username的值,可以是用户名,也可以是手机号,需要判断,再查询
  17. try:
  18. # if re.match(r'^1[3-9]\d{9}$', username):
  19. # user = User.objects.get(mobile=username)
  20. # else:
  21. # user = User.objects.get(username=username)
  22. user = User.objects.get(username=username)
  23. except:
  24. # 如果未查到数据,则返回None,用于后续判断
  25. try:
  26. user = User.objects.get(mobile=username)
  27. except:
  28. return None
  29. # return None
  30. # 判断密码
  31. if user.check_password(password):
  32. return user
  33. else:
  34. return None

在配置文件中告知Django使用我们自定义的认证后端

前端保存token

我们可以将JWT保存在cookie中,也可以保存在浏览器的本地存储里,我们保存在浏览器本地存储中
浏览器的本地存储提供了sessionStorage 和 localStorage 两种:

  • sessionStorage 浏览器关闭即失效
  • localStorage 长期有效

使用方法

  1. sessionStorage.变量名 = 变量值 // 保存数据
  2. sessionStorage.变量名 // 读取数据
  3. sessionStorage.clear() // 清除所有sessionStorage保存的数据
  4. localStorage.变量名 = 变量值 // 保存数据
  5. localStorage.变量名 // 读取数据
  6. localStorage.clear() // 清除所有localStorage保存的数据
  1. var vm = new Vue({
  2. ...
  3. methods: {
  4. ...
  5. on_submit: function(){
  6. axios.post(...)
  7. .then(response => {
  8. // 记录用户的登录状态
  9. sessionStorage.clear();
  10. localStorage.clear();
  11. localStorage.token = response.data.token;
  12. localStorage.username = response.data.username;
  13. localStorage.user_id = response.data.id;
  14. location.href = '/index.html';
  15. })
  16. .catch(...)
  17. }
  18. }
  19. })