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') # 临时cookie
response.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 restart
1.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 ModelBackend
import re
from users.models import User
class 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 user
else:
# 变量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 user
else:
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(...)
}
}
})