1. Cookie与Session
1.1 Cookie
1.1.1 特点
- Cookie以键值对的格式进行信息的存储。
- Cookie基于域名安全,不同域名的Cookie是不能互相访问的,如访问itcast.cn时向浏览器中写了Cookie信息,使用同一浏览器访问baidu.com时,无法访问到itcast.cn写的Cookie信息。
- 当浏览器请求某网站时,会将浏览器存储的跟网站相关的所有Cookie信息提交给网站服务器。
1.1.2 使用
设置Cookie:使用
**HttpResponse.set_cookie()**方法来设置HttpResponse.set_cookie(cookie名称, value=cookie值, max_age=cookie有效期) # max_age单位为秒,默认为None 。如果是临时cookie,可将max_age设置为None。
def cookie(request):response = HttpResponse('ok')response.set_cookie('itcast1', 'python1') # 临时cookieresponse.set_cookie('itcast2', 'python2', max_age=3600) # 有效期一小时return response
读取Cookie:通过HttpResponse对象的COOKIES属性来读取本次请求携带的cookie值。
**request.COOKIES**为字典类型def cookie(request):cookie1 = request.COOKIES.get('itcast1')print(cookie1)return HttpResponse('OK')
删除Cookie:通过HttpResponse对象中的delete_cookie方法来删除。
response.delete_cookie('itcast2')
1.2 Session
1.2.1 启用session
Django项目默认启用Session。如需禁用session,只需要在MIDDLEWARE中注释掉即可。
1.2.2 存储方式
在settings.py文件中,可以设置session数据的存储方式,可以保存在数据库、本地缓存等。
数据库:
- 设置
**SESSION_ENGINE='django.contrib.sessions.backends.db'** - 在项INSTALLED_APPS中安装Session应用,之后会在数据库中自动生成含有键,值,过期时间的数据库表。
INSTALLED_APPS = [...,'django.contrib.sessions']
- 设置
Redis
- 安装包:
pip install django-redis - 配置:
注意事项:CACHES = {'default': {'BACKEND': 'django_redis.cache.RedisCache','LOCATION': 'redis://127.0.0.1:6379/1','OPTIONS': {'CLIENT_CLASS': 'django_redis.client.DefaultClient',}}}SESSION_ENGINE = 'django.contrib.sessions.backends.cache'SESSION_CACHE_ALIAS = 'default'
如果redis的ip地址不是本地回环127.0.0.1,而是其他地址,访问Django时,可能出现Redis连接错误:
解决办法:
- 安装包:
打开redis的配置文件
**redis.conf**,并将ip地址添加进去。重新启动redis服务:
sudo service redis-server restart1.2.3 Session使用
通过HttpRequest对象的session属性进行会话的读写操作。
以键值对的格式写session
request.session['键']=值
根据键读取值
request.session.get('键',默认值)
清除所有session,在存储中删除值部分
request.session.clear()
清除session数据,在存储中删除session的整条数据
request.session.flush()
删除session中的指定键及值,在存储中只删除某个键及对应的值。
del request.session['键']
设置session的有效期
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**
REST_FRAMEWORK = {'DEFAULT_AUTHENTICATION_CLASSES': ('rest_framework_jwt.authentication.JSONWebTokenAuthentication','rest_framework.authentication.SessionAuthentication','rest_framework.authentication.BasicAuthentication',),}JWT_AUTH = {'JWT_EXPIRATION_DELTA': datetime.timedelta(days=1), # token有效期}
2.1.2 账户登录
1. 业务说明
验证用户名和密码,验证成功后,为用户签发JWT,前端将签发的JWT保存下来。
2. 后端接口设计
请求方式: POST meiduo_admin/authorizations/
请求参数: JSON 或 表单
| 参数名 | 类型 | 是否必须 | 说明 |
|---|---|---|---|
| username | str | 是 | 用户名 |
| password | str | 是 | 密码 |
返回数据: JSON
{"username": "python","user_id": 1,"token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjo5LCJ1c2VybmFtZSI6InB5dGhvbjgiLCJleHAiOjE1MjgxODI2MzQsImVtYWlsIjoiIn0.ejjVvEWxrBvbp18QIjQbL1TFE0c0ejQgizui_AROlAU"} #放在请求体的形式
| 返回值 | 类型 | 是否必须 | 说明 |
|---|---|---|---|
| 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 中,创建
def jwt_response_payload_handler(token, user=None, request=None):""" 自定义jwt认证成功返回数据 """return {'token': token,'id': user.id,'username': user.username}
修改配置文件
# JWT配置JWT_AUTH = {'JWT_EXPIRATION_DELTA': datetime.timedelta(days=1),'JWT_RESPONSE_PAYLOAD_HANDLER': 'meiduo_admin.utils.jwt_response.jwt_response_payload_handler',}
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方法的思路:
- 根据username参数查找用户User对象,在查询条件中在加上is_staff=True的条件
- 若查找到User对象,调用User对象的check_password方法检查密码是否正确
在meiduo_mall/utils/authenticate.py中编写:
from django.contrib.auth.backends import ModelBackendimport refrom users.models import Userclass MeiduoModelBackend(ModelBackend):def authenticate(self, request, username=None, password=None, **kwargs):# 判断是否通过vue组件发送请求if request is None:try:user = User.objects.get(username=username, is_staff=True)except:return None# 判断密码if user.check_password(password):return userelse:# 变量username的值,可以是用户名,也可以是手机号,需要判断,再查询try:# if re.match(r'^1[3-9]\d{9}$', username):# user = User.objects.get(mobile=username)# else:# user = User.objects.get(username=username)user = User.objects.get(username=username)except:# 如果未查到数据,则返回None,用于后续判断try:user = User.objects.get(mobile=username)except:return None# return None# 判断密码if user.check_password(password):return userelse:return None
前端保存token
我们可以将JWT保存在cookie中,也可以保存在浏览器的本地存储里,我们保存在浏览器本地存储中
浏览器的本地存储提供了sessionStorage 和 localStorage 两种:
- sessionStorage 浏览器关闭即失效
- localStorage 长期有效
使用方法
sessionStorage.变量名 = 变量值 // 保存数据sessionStorage.变量名 // 读取数据sessionStorage.clear() // 清除所有sessionStorage保存的数据localStorage.变量名 = 变量值 // 保存数据localStorage.变量名 // 读取数据localStorage.clear() // 清除所有localStorage保存的数据
var vm = new Vue({...methods: {...on_submit: function(){axios.post(...).then(response => {// 记录用户的登录状态sessionStorage.clear();localStorage.clear();localStorage.token = response.data.token;localStorage.username = response.data.username;localStorage.user_id = response.data.id;location.href = '/index.html';}).catch(...)}}})
