文章目录
概述
理论上,一个用户的所有请求操作都应该属于同一个会话,而另一个用户的所有请求操作则应该属于另一个会话,二者不能混淆。而Web应用程序是使用HTTP协议传输数据的,HTTP协议是无状态的协议,一旦数据交换完毕,客户端与服务器端的连接就会关闭,再次交换数据需要建立新的连接。这就意味着服务器无法从连接上跟踪会话,无法从网络连接知道客户身份,那怎么办呢?
那就给每个客户端颁发一个通行证,每人一个,无论谁访问都必须携带自己通行证,这样服务器就能从通行证上确认客户身份了
Cookie
原理
Cookie就是这样一种机制,是一种客户端会话技术
Cookie实际上是一小段的文本信息,客户端请求服务器,如果服务器需要记录该用户状态,就使用response向客户端浏览器颁发一个Cookie,客户端浏览器会把Cookie保存起来,当浏览器再请求该网站时,浏览器把请求的网址连同该Cookie一同提交给服务器,服务器检查该Cookie,以此来辨认用户状态。服务器还可以根据需要修改Cookie的内容
cookie本身由服务器生成,以key-value的形式进行存储,通过Response将cookie写到浏览器上,下一次访问,浏览器会根据不同的规则携带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
有一些功能需要用户登录后才能访问,比如购物车,这时就可以获取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
当用户退出登录时,要删除cookie,代码如下:
def logout(request):
res = HttpResponse('注销')
res.delete_cookie('userid')
return res
使用response.delete_cookie(key)来删除cookie
cookie小结
- 浏览器的会话技术,cookie存储在客户端
- cookie一般不跨域,不能跨浏览器
- cookie一般是小于4KB的文本
- cookie是由服务器端设置,cookie内容一般和当前用户相关
- 优点:数据存在在客户端,减轻服务器端的压力,提高网站的性能
- 缺点:安全性不高,在客户端机很容易被查看或破解用户会话信息
Session
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'
]
一般创建应用时,会自动添加,无需手动添加
设置session
request.session['userid'] = users.first().id
request.session.set_expiry(60*60*24) # 秒, 设置过期时间
每个HttpRequest对象都有一个session属性,也是一个类字典对象
获取session
# 第一种方法
userid = request.session.get('userid')
# 第二种方法
userid = request.session['userid'] # key不存在时报错
删除session
# 第一种方法
session_key = request.session.session_key # 获取当前请求的session的key
request.session.delete(session_key)
# 第二种方法
request.session.delete('userid')
# 第三种方法
del request.session['userid'] # key不存在时报错
clear和flush
session是有一级缓存的,目的是为了减少查询数据库的时间,提高效率,一级缓存的生命周期和session是一样的
session.flush()和session.clear()就针对session的一级缓存的处理
简单的说:
- session.flush():删除当前的会话数据并删除会话的cookie,将session的缓存中的数据与数据库同步
- session.clear():清除所有会话,清除session中的缓存数据(不管缓存与数据库的同步)
session小结
- 服务端的会话技术,session数据保存在服务端
- 优点:安全
- 缺点:对服务器有一定的压力
- 注意:使用ajax跨域请求时,跨域的时候所创建的session是不会被浏览器保存下来的。所以每次进行跨域请求时,服务器都认为不是同一个浏览器所发起的请求,session也会不一样
Token
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='过期时间')
设置token
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')
获取token
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('请先登录')
删除token
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