【Django 012】Django2.2会话技术详解之cookie

请求的生命周期是从request开始到response就结束了,非常短。那么下一次访问的时候服务端是如何识别该客户端,从而造成是长连接的效果呢?这一切都归功于会话技术,这一节我们就一起来看看会话技术的一种:cookie。

我是T型人小付,一位坚持终身学习的互联网从业者。喜欢我的博客欢迎在csdn上关注我,如果有问题欢迎在底下的评论区交流,谢谢。

会话技术

会话技术有如下三种,这一篇我们来看看cookie,剩下的两种下一篇再看

  • cookie
    客户端会话技术,数据以键值对存储在客户端。支持过期时间,只针对本网站,不能跨网站或者跨域名。服务端通过HttpResponse发送给客户端。

  • session

  • token

HttpResponse设置cookie

HttpResponse有一个方法set_cookie()用来设置cookie。

创建两个路由和view函数,分别用来设置和获取cookie

path('setcookie/', views.setcookie, name='setcookie'),
path('getcookie/', views.getcookie, name='getcookie'),
def setcookie(request):
    name = request.GET.get('name')
    age = request.GET.get('age')
    response = HttpResponse()
    response.content=('name:{}<br>age:{}'.format(name,age))
    response.set_cookie('name',name)
    response.set_cookie('age',age)
    return response


def getcookie(request):
    response = HttpResponse()
    name = request.COOKIES.get('name')
    age = request.COOKIES.get('age')
    response.write('Get cookies as below<br>')
    response.write('name:{}<br>age:{}'.format(name,age))
    response.flush()
    return response

setcookie()函数中,利用url的查询参数传递给后端两个变量,并利用HttpResponse的set_cookie()方法设置了cookie的两个键值对。如果访问http://127.0.0.1:8000/three/setcookie/?name=xiaofu&age=88,结果如下
1-setcookie.png

之后这两个cookie就保存在本地浏览器了,再次访问同一个网站的不同路径会默认带上所有的cookie。如果访问http://127.0.0.1:8000/three/getcookie/,结果如下
2-getcookie.png
可以看到发送给服务端的request带上了cookie信息。

这里除了刚才我们自己创建的两个cookie,还有一个防跨站的csrftoken,以后再细讲

cookie的有效期

假如把浏览器关掉,再次访问http://127.0.0.1:8000/three/getcookie/,结果如下
3-expire.png
发现刚才设置的cookie居然在本地消失了。

这里涉及到了cookie的有效期,默认情况下是关闭浏览器则失效。不过也可以在HttpResponse().set_cookie()的时候指定过期时间。

修改上面的view函数如下

def setcookie(request):
    name = request.GET.get('name')
    age = request.GET.get('age')

    response = HttpResponse()
    response.content=('name:{}<br>age:{}'.format(name,age))
    response.set_cookie('name',name,max_age=60)
    response.set_cookie('age',age,max_age=60)
    return response

这里的max_age是一个以秒为单位的时间差值,过了这个时间cookie失效。还有一个参数是expires可以接受一个时间戳,不过一般情况下使用max_age即可。这里设置的就是两个cookie都是一分钟后失效。

再一分钟之内除非是清除缓存,不然无论关闭浏览器多少次这两个cookie都不会消失。一分钟以后cookie自动消失,即使不关闭浏览器。

很多网站的登陆密码保存7天也是一样的道理

简化登录场景实例

下面用实例来感受下cookie的使用。

一个简化的用户登录场景,交互逻辑如下:

  1. 用户访问/login/页面填写一个登录的表单
  2. 表单中的提交按钮访问/dologin/API提交用户名和密码,并设置为对应cookie
  3. 服务器自动跳转到用户个人主页/homepage/,显示用户已成功登录
  4. cookie没过期的情况下用户可直接进入个人主页,不然自动跳转到登陆页面

3个路由

按照上面的逻辑,创立三个路由规则如下

path('login/',views.login, name='login'),
path('dologin/',views.dologin, name='dologin'),
path('homepage/',views.homepage,name='homepage'),

login

创建一个模板文件login.html以及对应的view函数如下

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Login</title>
</head>
<body>
<form action="{% url 'three:dologin' %}" method="post">
    <label for="name">Name: </label><input type="text" id="name" name="name" placeholder="Your name"><br>
    <label for="password">Password: </label><input type="password" id="password" name="password" placeholder="Your password"><br>
    <input type="submit">

</form>
</body>
</html>
def login(request):
    return render(request,'login.html')

这里有一个名字输入框和密码输入框,密码并没有在后台进行校验,这里只是起一个展示作用。点击提交按钮,会通过反向解析访问上面的dologin路由。效果如下
4-login.png
可以看到访问login的时候没有带任何自定义cookie,点击Submit就到了下一步

dologin API

创建对应的view函数如下

def dologin(request):
    name = request.POST.get('name')
    passwd = request.POST.get('password')
    response = HttpResponseRedirect(reverse('three:homepage'))
    response.set_cookie('name',name)
    response.set_cookie('password',passwd)
    return response

正常情况下需要在这一步进行密码校验,这里简化起见,只是获取了上一步表单传进来的数据,存到了cookie,然后做了一个重定向。

homepage

创建对应的view函数如下

def homepage(request):
    name = request.COOKIES.get('name')
    if name:
        return HttpResponse(request.COOKIES.get('name')+' has succesfully login.')
    else:
        return HttpResponseRedirect(reverse('three:login'))

如果成功获取到了姓名就显示个人信息,不然直接跳转到登录界面。效果如下

首先在上一步的dologin有一个302跳转,在这一步设置了两个cookie
5-302.png
然后在访问主页的时候带上了刚才设置的两个cookie,正常显示个人信息
6-homepage.png
如果清理掉cookie,再次访问主页
7-clear.png
会自动跳转到登录页面
8-login.png

存储中文cookie

假如我们在上一步的登陆页面的名字打一个中文会如何呢?我们来试试。

填写完用户名和密码
9-chinese.png
发现浏览器卡在了dologin这个API
10-dologin.png
看一下日志,发现有如下报错

[22/Mar/2020 12:56:31] "POST /three/dologin/ HTTP/1.1" 302 0
Traceback (most recent call last):
  File "/home/fuhx/anaconda3/lib/python3.7/wsgiref/handlers.py", line 138, in run
    self.finish_response()
  File "/home/fuhx/anaconda3/lib/python3.7/wsgiref/handlers.py", line 180, in finish_response
    self.write(data)
  File "/home/fuhx/anaconda3/lib/python3.7/wsgiref/handlers.py", line 274, in write
    self.send_headers()
  File "/home/fuhx/anaconda3/lib/python3.7/wsgiref/handlers.py", line 333, in send_headers
    self._write(bytes(self.headers))
  File "/home/fuhx/anaconda3/lib/python3.7/wsgiref/headers.py", line 142, in __bytes__
    return str(self).encode('iso-8859-1')
UnicodeEncodeError: 'latin-1' codec can't encode characters in position 135-136: ordinal not in range(256)

看样子cookie是不支持中文的。

补充一下,所有web技术的cookie都是不支持中文的

但是既然都是编码,将中文换一种编码存储在cookie中,在需要展示的时候再转换回来应该就可以达到目的。目前比较常见用于完成这一操作的是一种叫做BASE64的编码方式。

base64

Base64是一种基于64个可打印字符来表示二进制数据的表示方法。由于log264=6,所以每6个bit为一个单位,对应64个字符中的一个。3个字节有24个bit,对应于4个Base64单元,即3个字节可由4个可打印字符来表示。但是原始数据长度不一定总是3的倍数,转换成Base64末尾就会有等号来填充。

Base64通常用于传输信道只支持ASCII字符,不方便传输二进制流的场合。或者原始数据含有非ASCII字符,容易出现编码问题的场合,例如中文。

Base64编码仅仅用于bytes类型的数据。 此外,编码处理的输出结果总是一个bytes字符串。 但是数据在python3中统一使用Unicode编码,如果想混合使用Base64编码的数据和Unicode文本,必须添加一个额外的Unicode转为bytes的编码步骤

Python3中Unicode和Base64混合

字符串转Base64

In [28]: name='小付'                                                                                                                                                         

In [29]: name_bytes=name.encode('utf-8')                                                                                                                                     

In [30]: name_bytes                                                                                                                                                          
Out[30]: b'\xe5\xb0\x8f\xe4\xbb\x98'

In [31]: name_b64=base64.b64encode(name_bytes)                                                                                                                               

In [32]: name_b64                                                                                                                                                            
Out[32]: b'5bCP5LuY'

这里字符串前面的b代表bytes类型

Base64解码为字符串

In [32]: name_b64                                                                                                                                                            
Out[32]: b'5bCP5LuY'

In [33]: name_bytes=base64.b64decode(name_b64)                                                                                                                               

In [34]: name_bytes                                                                                                                                                          
Out[34]: b'\xe5\xb0\x8f\xe4\xbb\x98'

In [35]: name=name_bytes.decode('utf-8')                                                                                                                                     

In [36]: name                                                                                                                                                                
Out[36]: '小付'

利用base64解决cookie中文问题

这里需要注意的是,cookie在本地是以bytes类型保存的,但是传递到了服务器端却自动变为了str类型,所以这里面还需要有一步转换,见下面实例

修改上面的dologinhomepage两个view函数如下

def dologin(request):
    name = request.POST.get('name')
    passwd = request.POST.get('password')
    name_b64 = base64.b64encode(name.encode('utf-8'))
    response = HttpResponseRedirect(reverse('three:homepage'))
    response.set_cookie('name',name_b64)
    response.set_cookie('password',passwd)
    return response

这里没有太多要说的,将编码后的base64数据作为cookie下发下来

def homepage(request):
    name_b64 = request.COOKIES.get('name')
    name = base64.b64decode(name_b64[1:]).decode('utf-8')
    if name:
        return HttpResponse(name+' has succesfully login.')
    else:
        return HttpResponseRedirect(reverse('three:login'))

这里就要注意了,name_64是一个str类型,所以在进行base64解码前先要将这个str最前面本来用来表示bytes类型的b给去掉。base64.b64decode()也是可以接受str类型的,所以没有问题。

这样修改以后成功进行个人主页,可以看到本地存储的cookie是b开头的str
11-chinese.png

删除cookie

还是以上面登录的例子来展开,如果要加入一个注销用户的操作又该怎么做呢?

所谓注销用户,本质上就是删除本地存储的用户cookie,下次登陆的时候重新填写用户名和密码。

创建一个logout的API如下

path('logout/',views.logout,name='logout'),

添加一个homepage.html如下

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Homepage</title>
</head>
<body>
<h2>Welcome back {{ name }}</h2>
<a href="{% url 'three:logout' %}">Logout</a>
</body>
</html>

用户点击Logout链接就会访问logout这个API。

然后修改和添加view函数如下

def homepage(request):
    name_b64 = request.COOKIES.get('name')
    if name_b64:
        name = base64.b64decode(name_b64[1:]).decode('utf-8')
        return render(request,'homepage.html',context={'name':name})
    else:
        return HttpResponseRedirect(reverse('three:login'))


def logout(request):
    response = HttpResponseRedirect(reverse('three:login'))
    response.delete_cookie('name')
    response.delete_cookie('password')
    return response

这时候正常登陆后的页面如下
12-newhome.png
点击Logout按钮,退回到登陆页面,可以看到本地的两个cookie都被删除了
13-logout.png

总结

会话技术的cookie我们就聊到这,下一篇我们来看看第二种:session。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值