Cookie前置
会话跟踪
我们需要先了解一下什么是会话!可以把会话理解为客户端与服务器之间的一次会晤,在一次会晤中可能会包含多次请求和响应。
例如你给10086打个电话,你就是客户端,而10086服务人员就是服务器了。从双方接通电话那一刻起,会话就开始了,到某一方挂断电话表示会话结束。
在通话过程中,你会向10086发出多个请求,那么这多个请求都在一个会话中。 客户向某一服务器发出第一个请求开始,会话就开始了,直到客户关闭了浏览器会话结束。
在一个会话的多个请求中共享数据,这就是会话跟踪技术。例如在一个会话中的请求如下:
请求银行主页;
请求登录(请求参数是用户名和密码);
请求转账(请求参数与转账相关的数据);
请求信誉卡还款(请求参数与还款相关的数据)。
在这个会话中当前用户信息必须在这个会话中共享的,因为登录的是张三,那么在转账和还款时一定是相对张三的转账和还款!
这就说明我们必须在一个会话过程中有共享数据的能力。而web中这种能力的实现就要依靠cookie和session
http协议
无状态: 不会记录任何客户端和服务端的信息
无连接(closed)(长连接(keepalive)): 一次请求一次响应,就断开连接了. 服务器的连接数是有限的
在早期的HTTP/1.0中,每次http请求都要创建一个连接,而创建连接的过程需要消耗资源和时间,为了减少资源消耗,缩短响应时间,就需要重用连接。
在后来的HTTP/1.0中以及HTTP/1.1中,引入了重用连接的机制,就是在http请求头中加入Connection: keep-alive来告诉对方这个请求响应完成后不要关闭,
下一次咱们还用这个请求继续交流。协议规定HTTP/1.0如果想要保持长连接,需要在请求头中加上Connection: keep-alive,而HTTP/1.1默认是支持长连接的,有没有这个请求头都行。
当然了,协议是这样规定的,至于支不支持还得看服务器(比如tomcat)和客户端(比如浏览器)的具体实现。
在实践过程中发现谷歌浏览器使用HTTP/1.1协议时请求头中总会带上Connection: keep-alive,
另外通过httpclient使用HTTP/1.0协议去请求tomcat时,即使带上Connection: keep-alive请求头也保持不了长连接。
如果HTTP/1.1版本的http请求报文不希望使用长连接,则要在请求头中加上Connection: close,接收到这个请求头的对端服务就会主动关闭连接。
但是http长连接会一直保持吗?肯定是不会的。一般服务端都会设置keep-alive超时时间。
超过指定的时间间隔,服务端就会主动关闭连接。同时服务端还会设置一个参数叫最大请求数,比如当最大请求数是300时,
只要请求次数超过300次,即使还没到超时时间,服务端也会主动关闭连接。
Cookie
1、Cookie的由来
大家都知道HTTP协议是无状态的。
无状态的意思是每次请求都是独立的,它的执行情况和结果与前面的请求和之后的请求都无直接关系,它不会受前面的请求响应情况直接影响,也不会直接影响后面的请求响应情况。
一句有意思的话来描述就是人生只如初见,对服务器来说,每次的请求都是全新的。
状态可以理解为客户端和服务器在某次会话中产生的数据,那无状态的就以为这些数据不会被保留。会话中产生的数据又是我们需要保存的,也就是说要“保持状态”。因此Cookie就是在这样一个场景下
诞生。
并且还有一个问题就是,你登陆我的网站的时候,我没法确定你是不是登陆了,之前我们学的django,虽然写了很多页面,但是用户不用登陆都是可以看所有网页的,只要他知道网址就行,但是我们为了自己的安全机制,我们是不是要做验证啊,访问哪一个网址,都要验证用户的身份,但是还有保证什么呢,用户登陆过之后,还要保证登陆了的用户不需要再重复登陆,就能够访问我网站的其他的网址的页面,对不对,但是http无状态啊,怎么保证这个事情呢?此时就要找cookie了。
2、什么是Cookie
首先来讲,cookie是浏览器的技术,Cookie具体指的是一段小信息,它是服务器发送出来存储在浏览器上的一组组键值对,可以理解为服务端给客户端的一个小甜点,下次访问服务器时浏览器会自动携带这些键值对,以便服务器提取有用信息。
Cookie的原理
cookie的工作原理是:浏览器访问服务端,带着一个空的cookie,然后由服务器产生内容,浏览器收到相应后保存在本地;当浏览器再次访问时,浏览器会自动带上Cookie,这样服务器就能通过Cookie的内容来判断这个是“谁”了。
cookie是浏览器技术,针对的就是http协议无状态,导致不能和服务端维持会话,例子:登录,登录之后,看到响应结果,继续访问其他有页面,服务端要判断我是谁.
3、查看Cookie
我们使用Chrome浏览器,打开开发者工具。
cookie图解
4、Cookie规范
Cookie大小上限为4KB;
一个服务器最多在客户端浏览器上保存20个Cookie;
一个浏览器最多保存300个Cookie,因为一个浏览器可以访问多个服务器。
上面的数据只是HTTP的Cookie规范,但在浏览器大战的今天,一些浏览器为了打败对手,为了展现自己的能力起见,可能对Cookie规范“扩展”了一些,例如每个Cookie的大小为8KB,最多可保存500个Cookie等!但也不会出现把你硬盘占满的可能!
注意,不同浏览器之间是不共享Cookie的。也就是说在你使用IE访问服务器时,服务器会把Cookie发给IE,然后由IE保存起来,当你在使用FireFox访问服务器时,不可能把IE保存的Cookie发送给服务器。
Cookie与HTTP头
Cookie是通过HTTP请求和响应头在客户端和服务器端传递的:
Cookie:请求头,客户端发送给服务器端;
格式:Cookie: a=A; b=B; c=C。即多个Cookie用分号离开; Set-Cookie:响应头,服务器端发送给客户端;
一个Cookie对象一个Set-Cookie: Set-Cookie: a=A Set-Cookie: b=B Set-Cookie: c=C
Cookie的覆盖
如果服务器端发送重复的Cookie那么会覆盖原有的Cookie,例如客户端的第一个请求服务器端发送的Cookie是:Set-Cookie: a=A;第二请求服务器端发送的是:Set-Cookie: a=AA,那么客户端只留下一个Cookie,即:a=AA。
新建个项目django_cookie先把csrf注释了,因为它影响cookie实验
视图函数
from django.http import HttpResponse from django.shortcuts import render, redirect from django.views import View # Create your views here. def login(request): method = request.method if method == 'GET': return render(request,'login.html') else: username = request.POST.get('username') password = request.POST.get('password') if username == 'root' and password == '123': return redirect('home') return HttpResponse('用户名或密码有误') class Home(View): def get(self,request): return render(request,'home.html') def cart(request): cart_lst = ['茅台','劳力士','拉菲','兰博基尼','库里南'] return render(request,'cart.html',{'data':cart_lst})
登录页面
首页
购物车
但是这样有个问题,如果用户记住网址,可以绕过灯枯直接访问首页和购物车,所以这样很不安全
访问首页和购物车需要验证才更合理,此时就可以借助cookie
目前,我们没有设置cookie
5、django中操作cookie
设置Cookie
rep = HttpResponse(…)
rep = render(request, …)
rep.set_cookie(key,value,…)
rep.set_signed_cookie(key,value,salt=‘加密盐’, max_age=None, …)
参数:
key, 键
value=‘’, 值
max_age=None, 超时时间
expires=None, 超时时间(IE requires expires, so set it if hasn’t been already.)
path=‘/’, Cookie生效的路径,/ 表示根路径,特殊的:根路径的cookie可以被任何url的页面访问
domain=None, Cookie生效的域名
secure=False, https传输
httponly=False 只能http协议传输,无法被JavaScript获取(不是绝对,底层抓包可以获取到也可以被覆盖)
signed_cookie 只是加了签名的 cookie, 而不是被加密的 cookie.
signed_cookie 的作用是防止用户私自纂改.参考: Securing Web Cookies With Signatures
So once I’ve logged in, we set a username cookie containing “Michael Brunton-Spall”, or uid=1 or something.
The problem with this is that the user is in total control of this cookie
单纯的记录 uid 或者用户名在 cookie 中很容易被篡改(也是不建议将用户敏感信息记录在cookie中的原因), 万一攻击者把uid=1换成uid=2岂不是可以访问 uid=2用户的资源了吗? 而如果换成uid=2:1fPjh2:iQGDDYNcgSYkIFqa2ixqakj6-gI那么服务端不仅检验uid, 还检验uid=2后面的签名字段, 即是调用HttpRequest.get_signed_cookie(key=key, salt=salt), 这样即使用户把 cookie 中的 value 换成 uid=2, 但是没有签名, 服务端照样拒绝访问资源.
另外, Django 的 cookie 签名是用的Base64_with_hmac, 参考: Source code for django.core.signing
如果需要在 cookie 里设置被加密的 value, 需要自行对 value 进行加密(好像只能是对称加密), 比如使用hashlib.sha256{参考: hashlib — Secure hashes and message digests}:
set_cookie方法源码:
max_age和expires不能同时使用
设置cookie案例,我们在home页面设置
没设置之前,我们访问home页面,请求信息里面不带cookie
设置cookie
重启服务器,访问home页面
可以看到浏览器就包含了我们设置的cookie,这个是响应的cookie,浏览器将cookie保存到本地
响应头里面包含我们设置的响应cookie键值对
设置cookie的第一次访问,请求里面是不带cookie的
第二次访问,可以看到请求头已经包含了cookie,浏览器自动将cookie键值对加到请求头里面
此时浏览器的cookie既包含请求cookie,也包含响应cookie。因为home设置的cookie还会响应回来,相同的键是更新cookie
我们访问购物车,请求头也带着cookie,但是home页面设置的cookie
此时只包含请求cookie,没有响应cookie,因为cart页面没设置cookie
第一次访问带上cookie后,只要访问这个网站的不管什么页面,都会带上cookie
现在每个页面都能自由访问,这是不应该的,应该做认证
获取Cookie
request.COOKIES[‘key’] request.COOKIES.get(‘key’)
request.get_signed_cookie(key, default=RAISE_ERROR, salt=‘’, max_age=None) #获取签名cookie
参数:
default: 默认值
salt: 加密盐
max_age: 后台控制过期时间
也可以通过request.META 来获取请求头键值对
request.META 字典类型数据,所有的请求头的键都加上了一个HTTP_键名称,键值全部是大写
针对cookie,我们不用META。可以用专门的request.COOKIES.get(‘key’) 来获取cookie
在登录处。登录成功设置cookie。购物车和home都添加cookie验证,没有登录不让看页面,直接跳转到登录页面
封装个验证方法
此时登录购物车网址,直接跳转到登录页面
不包含任何cookie
登录成功
然后再次访问购物车,携带cookie,不用再次登录
可以看到cookie是字典类型数据
6、修改Cookie
在设置cookie的地方,保持键不变,值改一下 就是修改cookie。修改后重启服务,再次登录
此时,再点击购物车,由于携带的cookie发生变化,不满足原来的判断条件,已经不能在进入购物车界面
所以一旦修改cookie,记得在判断处也要及时同步做好修改
整个流程
浏览器自动将每个网站的cookie保存到了客户端本地
网址1: {xx:oo,xx1:oo1}
网址2: {xx:oo,xx1:oo1}
网址3: {xx:oo,xx1:oo1}
每次请求对应网址时,都会自动携带这个网址对应的cookie数据,放到http请求头部信息中.
cookie是浏览器技术,如果自己写的爬虫去爬页面,去模拟浏览器请求,是不会携带cookie的
可以自己想办法获取cookie:
模拟登录上网站,拿到cookie后,后面的请求携带上cookie
现在只要登录过一次,再次打开网站都能访问,这样很不安全。怎么办呢?
cookie要设置有效期,甚至删除cookie操作
原码,max_age表示超时时间
max_age和expires不能同时使用
7、设置cookie超时时间
浏览器登录,可以看到cookie有个有效期
过了时间cookie就被删除,需要重新登录
8、删除Cookie
退出登录时使用
def logout(request):
rep = redirect(“/login/”)
rep.delete_cookie(“user”) # 删除用户浏览器上之前设置的user cookie值
return rep
前端url反向解析
点登出后,再次访问购物车页面,cookie已被删除,需要重新登录
使用cookie关闭浏览器后,再次登录,之前的cookie会全部清除
cookie设置中文时的编码问题:cookie在设置时不允许出现中文。非要设置中文的怎么办,看下面的解决方案:
Cookie规范
Cookie大小上限为4KB;
一个服务器最多在客户端浏览器上保存20个Cookie;
一个浏览器最多保存300个Cookie,因为一个浏览器可以访问多个服务器。
上面的数据只是HTTP的Cookie规范,但在浏览器大战的今天,一些浏览器为了打败对手,为了展现自己的能力起见,可能对Cookie规范“扩展”了一些,例如每个Cookie的大小为8KB,最多可保存500个Cookie等!但也不会出现把你硬盘占满的可能!
注意,不同浏览器之间是不共享Cookie的。也就是说在你使用IE访问服务器时,服务器会把Cookie发给IE,然后由IE保存起来,当你在使用FireFox访问服务器时,不可能把IE保存的Cookie发送给服务器。
cookie设置的数据是明文存储在客户端的,不安全
由于cookie存储数据的大小有限制,并且明文存贮,看上去不安全.出现了session