理论上,一个用户的所有请求操作都应该属于同一个会话,而另一个用户的所有请求操作则应该属于另一个会话,二者不能混淆。而Web应用程序是使用HTTP协议传输数据的,HTTP协议是无状态的协议,一旦数据交换完毕,客户端与服务器端的连接就会关闭,再次交换数据需要建立新的连接。这就意味着服务器无法从连接上跟踪会话,无法从网络连接知道客户身份,那怎么办呢?
那就给每个客户端颁发一个通行证,每人一个,无论谁访问都必须携带自己通行证,这样服务器就能从通行证上确认客户身份了
Cookie就是这样一种机制,是一种客户端会话技术
Cookie实际上是一小段的文本信息,客户端请求服务器,如果服务器需要记录该用户状态,就使用response向客户端浏览器颁发一个Cookie,客户端浏览器会把Cookie保存起来,当浏览器再请求该网站时,浏览器把请求的网址连同该Cookie一同提交给服务器,服务器检查该Cookie,以此来辨认用户状态。服务器还可以根据需要修改Cookie的内容
cookie本身由服务器生成,以key-value的形式进行存储,通过Response将cookie写到浏览器上,下一次访问,浏览器会根据不同的规则携带cookie过来
注意:cookie不能跨浏览器,一般不跨域
在views.py中添加login视图函数,如下
from django.http import HttpResponse
from django.shortcuts import render
from App.models import User
def login(request):
if request.method == 'GET':
return render(request,'login.html')
elif request.method == 'POST':
username = request.POST.get('username')
password = request.POST.get('password')
users = User.objects.filter(username=username,password=password)
if users.exists():
res = HttpResponse('登录成功!')
res.set_cookie('userid',users.first().id)
return res
else:
return render(request,'login.html')
使用response.set_cookie()进行设置cookie
response.set_cookie(key,value[,max_age=None,expires=None])
max_age: 整数, 单位为秒, 指定cookie过期时间, 默认为None, 表示浏览器关闭失效
expires: 指定过期时间, 还支持datetime或timedelta, 可以指定一个具体日期时间
例如:
# 第一种方法
expires = datetime.datetime(2019, 1, 1, 2, 3, 4)
# 第二种方法
expires = datetime.datetime.now() + datetime.timedelta(days=1) # 1天后失效
注意:max_age和expries两个选一个指定
login.html代码如下:
<body>
<h2>登录h2>
<hr>
<form action="{% url 'login' %}" method="post">
{% csrf_token %}
<p>用户名:<input type="text" name="username">p>
<p>密码:<input type="password" name="password">p>
<p><input type="submit" value="登录">p>
form>
body>
csrf:跨站伪造请求
解决csrf在post请求时产生的403错误:
注释settings.py中MIDDLEWARE的csrf相关中间件
在form表单中添加{% csrf_token %}
有一些功能需要用户登录后才能访问,比如购物车,这时就可以获取cookie,验证用户是否登录,代码如下:
def cart(request):
userid = request.COOKIES.get('userid')
user = User.objects.filter(id=userid).first()
if user:
return HttpResponse('购物车')
else:
return HttpResponse('请先登录')
使用request.COOKIES.get(key)获取cookie
当用户退出登录时,要删除cookie,代码如下:
def logout(request):
res = HttpResponse('注销')
res.delete_cookie('userid')
return res
使用response.delete_cookie(key)来删除cookie
session是一种服务器端会话技术,依赖于cookie
session数据存储在数据库django_session表中,并且做了基本的数据安全处理(base64编码),如下图:
将Session在数据库中的session_key,当作sessionid,存储在cookie中,session_data则为用户信息
zaidjango中启用session,需要在settings.py文件中配置,如下:
INSTALLED_APPS = [
'django.contrib.sessions'
]
MIDDLEWARE = [
'django.contrib.sessions.middleware.SessionMiddleware'
]
一般创建应用时,会自动添加,无需手动添加
request.session['userid'] = users.first().id
request.session.set_expiry(60*60*24) # 秒, 设置过期时间
每个HttpRequest对象都有一个session属性,也是一个类字典对象
# 第一种方法
userid = request.session.get('userid')
# 第二种方法
userid = request.session['userid'] # key不存在时报错
# 第一种方法
session_key = request.session.session_key # 获取当前请求的session的key
request.session.delete(session_key)
# 第二种方法
request.session.delete('userid')
# 第三种方法
del request.session['userid'] # key不存在时报错
session是有一级缓存的,目的是为了减少查询数据库的时间,提高效率,一级缓存的生命周期和session是一样的
session.flush()和session.clear()就针对session的一级缓存的处理
简单的说:
Token是一种服务端会话技术,相当于手动实现的session,主要用在移动端,通过特定算法得到唯一的Token值
使用UserToken表保存token值
class User(models.Model):
username = models.CharField(max_length=10)
password = models.CharField(max_length=32)
class UserToken(models.Model):
token = models.CharField(max_length=100, unique=True, null=False, verbose_name='用户令牌/用户的唯一标识')
user = models.OneToOneField(User, on_delete=models.PROTECT ,verbose_name='关联的用户')
out_time = models.DateTimeField(null=False, verbose_name='过期时间')
import datetime
import uuid
from django.http import HttpResponse
from django.shortcuts import render
from App.models import User, UserToken
def login(request):
if request.method == 'GET':
return render(request,'login.html')
elif request.method == 'POST':
username = request.POST.get('username')
password = request.POST.get('password')
users = User.objects.filter(username=username,password=password)
if users.exists():
res = HttpResponse('登录成功!')
# 创建一个唯一的token值
token = uuid.uuid4().hex
# 设置过期时间
out_time = datetime.datetime.utcnow() + datetime.timedelta(days=1)
u_token = UserToken.objects.filter(user__id=users.first().id).first()
if u_token:
# 如果当前登录的用户已经存在对应的token记录,则修改token记录即可
u_token.token = token
u_token.out_time = out_time
u_token.save()
else:
# 如果第一次登录,创建token
user_token = UserToken()
user_token.token = token
user_token.user = users.first()
user_token.out_time = out_time
user_token.save()
# 借助cookie将token存入浏览器端
res.set_cookie('token', token, max_age=60*60*24)
return res
else:
return render(request,'login.html')
def cart(request):
token = request.COOKIES.get('token')
user_token = UserToken.objects.filter(token=token).first()
# 判断token是否存在
if user_token:
print(user_token.out_time)
print(datetime.datetime.utcnow())
# 判断token是否过期
if user_token.out_time.timestamp() > datetime.datetime.utcnow().timestamp():
return HttpResponse('购物车')
return HttpResponse('请先登录')
def logout(request):
res = HttpResponse('注销')
# 删除cookie中的token
res.delete_cookie('token')
# 删除usertoken表中的记录
token = request.COOKIES.get('token')
UserToken.objects.filter(token=token).delete()
return res