十一、Django的cookie和session
一、Django的cookie和session
一、会话
会话可以理解为客户端与服务器之间的一次会晤,在一次会晤中可能会包含多次请求和响应。客户向某一服务器发出第一个请求开始,会话就开始了,直到客户关闭了浏览器会话结束。
在一个会话的多个请求中,需要共享数据,就是会话跟踪技术。比如你在登陆银行主页,请求登录、请求取款、请求转账、请求还款...,在这个会话中,当前的用户信息必须在这个会话中是共享的,因为登录的是你,那么取款转账时一定是相对你的取款转账 。这就说明我们必须在一个会话过程中有共享数据的能力。
在web中这种能力的实现就要依靠cookie和session
二、cookie
-
Cookie的由来
HTTP协议是无状态的。就是每次请求都是独立的,它的执行情况和结果与前面的请求和之后的请求都无直接关系,它不会受前面的请求响应情况直接影响,也不会直接影响后面的请求响应情况。
客户端和服务器在某次会话中产生的数据,那无状态的就以为这些数据不会被保留。会话中产生的数据又是我们需要保存的,也就是说要“保持状态” ,因此就需要用到cookie
用django,虽然写了许多的页面,但是用户可以不用登录也能看所有的网页,只需要知道网址就行,但为了安全机制,需要做个验证,无论访问哪个网址需要验证用户的身份;当用户登录后,要需要验证是否是登录的状态,不需要再重复登录,但是http是无状态的,此时就需要用到cookie了。
-
什么是Cookie
cookie是浏览器的技术,Cookie具体指的是一段小信息,它是服务器发送出来存储在浏 览器上的一组组键值对,可以理解为服务端给客户端的一个小甜点,下次访问服务器时浏览器会自动携带这些键值对,以便服务器提取有用信息。
-
Cookie的原理
工作原理:浏览器访问服务端,带着一个空的cookie,然后由服务器产生内容,浏览器收到相应后保存在本地;当浏览器再次访问时,浏览器会自动带上Cookie,这样服务器就能通过Cookie的内容来判断这个是“谁”了。
-
网页下查看cookie
- cookie规范
-
Cookie大小上限为4KB;
-
一个服务器最多在客户端浏览器上保存20个Cookie;
-
一个浏览器最多保存300个Cookie,因为一个浏览器可以访问多个服务器。
随着互联网技术发展,可能对Cookie规范“扩展”了一些,例如每个Cookie的大小为8KB,最多可保存500个Cookie等。
注意:不同浏览器之间是不共享Cookie的。也就是说在你使用IE访问服务器时,服务器会把Cookie发给IE,然后由IE保存起来,当你在使用Google Chrome访问服务器时,不可能把IE保存的Cookie发送给服务器 。
- cookie的传递
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
一个用户 —— 一个浏览器 —— 一个服务端
BS:统一接口,浏览器输入一个网址,就能打开一个网站
-
Cookie的覆盖
如果服务器端发送重复的Cookie那么会覆盖原有的Cookie,例如客户端的第一个请求服务器端发送的Cookie是:Set-Cookie: a=A;第二请求服务器端发送的是:Set-Cookie: a=AA,那么客户端只留下一个Cookie,即:a=AA。
三、django中操作cookie
Ctrl + Shift + del三个键来清除页面缓存和cookie
-
获取cookie
request.COOKIE['key'] # 或者 request.COOKIE.get('key')
request.get_signed_cookie(key, default=RAISE_ERROR, salt='', max_age=None)
参数:
default: 默认值
salt: 加密盐
max_age: 后台控制过期时间 -
设置cookie
rep = HttpResponse(...) # 或 rep = render(request, ...)
rep.set_cookie(key,value,...)
return reprep.set_signed_cookie(key,value,salt='加密盐', max_age=None, ...)
参数:
key, 键
value='', 值
max_age=None, 超时时间(给一个以秒为单位的数据,经过这个时间后失效)
expires=None, 超时时间(给一个时间日期格式的数据,到那个时候自动失效)
path='/', Cookie生效的路径,/ 表示根路径,特殊的:根路径的cookie可以被任何url的页面访问
domain=None, Cookie生效的域名
secure=False, https传输
httponly=False 只能http协议传输,无法被JavaScript获取(不是绝对,底层抓包可以获取到也可以被覆盖) -
删除cookie
def logout(request):
rep = redirect("/login/")
rep.delete_cookie("user") # 删除用户浏览器上之前设置的usercookie值
return rep -
cookie的验证
-
比如,我们想自己设置一个cookie,键是k1,值是v1:
def index(request):
ret = HttpResponse('ok')
ret.set_cookie('k1','v1')
return ret
结果在网页端开发工具的cookie可见,设置成功了:
-
然后通过对比我们自己设定的cookie值,如果有cookie的键k1对应的值是v1,就能访问制作的home页面
home.html:
这是home页面
def home(request): is_login = request.COOKIES.get('k1') if is_login == 'v1': return render(request,'home.html') else: return HttpResponse('gun')
出现问题:就是直接访问127.0.0.1:8000/home,也会直接拿到home页面,当我们在网页端查看cookie的时候,仍然有k1对应v1,这是因为缓存的机制,需要我们Ctrl + Shift + del三个键来清除页面缓存和cookie
此时直接访问home页面,因为cookie中没有了键k1对应的值是v1,就拿不到home页面
解决:需要先访问index路径,经过index视图函数,给cookie设置一个键值对k1:v1,然后再访问home页面,浏览器还是带着本地的cookie,此时就可以拿到home的页面了 -
登录的验证
需求:制作三个页面,一个登录,一个index页面,一个home页面,用户想访问index和home页面都必须先经过login页面
-
先配置路径
from django.conf.urls import url
from app01 import viewsurlpatterns = [ url(r'^index/', views.index,name='index'), url(r'^home/', views.home,name='home'), url(r'^login/', views.login,name='login'), ]
-
制作三个页面
home.html:
这是home页面
去往index页面
index.html: <body> 来到了index的页面 <a href="/home/">返回home页面</a> </body> login.html: 欢迎来到登陆的页面 <form action="/login/" method="post"> {% csrf_token %} 用户名:<input type="text" name="uname"> 密码:<input type="password" name="pwd"> <input type="submit"> </form>
-
写视图函数
from django.shortcuts import render,HttpResponse,redirectdef login(request): if request.method == 'GET': return render(request,'login.html') else: username = request.POST.get('uname') password = request.POST.get('pwd') if username == 'yangzm' and password == '123': ret = redirect('home') ret.set_cookie('is_login',True) return ret else: return redirect('login') def index(request): is_login = request.COOKIES.get('is_login') # print(is_login,type(is_login)) # True <class 'str'> if is_login == 'True': return render(request,'index.html') else: return redirect('login') def home(request): is_login = request.COOKIES.get('is_login') if is_login == 'True': return render(request,'home.html') else: return redirect('login')
在login函数中,先返回一个登录界面,然后得到用户提交的数据判断,验证成功了就设置cookie的值,否则在返回登录页面重新登录
在index、home函数中,判断cookie设置的值,验证成功就返回页面,否则重定向回到登录页面 -
视图函数 改进版——加装饰器
from django.shortcuts import render,HttpResponse,redirectdef logining(f): def inner(request,*args,**kwargs): is_login = request.COOKIES.get('is_login') if is_login == 'True': ret = f(request,*args,**kwargs) return ret else: return redirect('login') return inner def login(request): if request.method == 'GET': return render(request,'login.html') else: username = request.POST.get('uname') password = request.POST.get('pwd') if username == 'yangzm' and password == '123': ret = redirect('home') ret.set_cookie('is_login',True) # ret.set_cookie('is_login',True,10) 设置超时函数10s,超过10scookie就失效了 return ret else: return redirect('login') @logining def index(request): return render(request,'index.html') @logining def home(request): return render(request,'home.html')
四、session
session是服务端技术,服务器在运行时可以为每一个用户的浏览器创建一个其独享的session对象,由于 session为用户浏览器独享,所以用户在访问服务器的web资源时 ,可以把各自的数据放在各自的session中,当用户再去访问该服务器中的其它web资源时,其它web资源再从用户各自的session中 取出数据为用户服务。
Cookie虽然在一定程度上解决了 “保持状态” 的需求,但是cookie最大支持4096字节,而且cookie保存在客户端,可能别拦截或者盗取
比如我设置了三组的cookie值,都会在网页看得到:
session就解决了以上问题,即支持更多的字节,并且保存在服务端,有较高的安全性
给每个客户端的cookie分配一个唯一的id,这样用户在访问时,通过cookie,服务器就知道来的人是“谁”。然后我们再根据不同的Cookie的id,在服务器上保存一段时间的私密资料,如“账号密码”等等
五、django中操作session
一个用户拿到session_id,只要没清除cookie,就还会有session_id,就不会生成一条新纪录了;
清除了cookie,session_id也没有了,此时再来访问就会新生成一条新的数据
一个用户 -- 一个浏览器 -- 对应一个session记录
-
设置值
request.session['k1'] = 123
request.session.setdefault('k1',123) # 存在则不设置自动生成随机字符串,将这个随机字符串和用户数据(加密后)和过期时间保存到了django-session表里面,帮你将这个随机字符串以sessionid:随机字符串的形式添加到cookie里面返回给浏览器(sessionid名字是可以改的)
但是注意一个事情,django-session这个表,你不能通过orm来直接控制,因为你的models.py里面没有这个对应关系
-
取值
request.session['k1']
request.session.get('k1',None)request.session这句是帮你从cookie里面将sessionid的值取出来,将django-session表里面的对应sessionid的值的那条记录中的session-data字段的数据给你拿出来(并解密),get方法就取出k1这个键对应的值
-
删除值
del request.session['k1'] # django-session表里面同步删除
-
登录验证
def home(request):
# is_login = request.session['is_login'] # 这么取值,找不到is_login这个键就会报错
is_login = request.session.get('is_login')# 这样取值不会报错
# print(is_login,type(is_login)) # True <class 'bool'>
'''
1.从cookie里面拿出了session_id:xxx这个随机字符串
2.去django-session表里面查询对应的数据
3.反解加密的用户数据,并获取用户需要的数据
'''
if is_login == True:return render(request,'home.html') else: return redirect('login')
def login(request):
if request.method == 'GET':
return render(request,'login.html')
else:
username = request.POST.get('uname')
password = request.POST.get('pwd')
if username == 'yangzm' and password == '123':
request.session['is_login'] = True
request.session['username'] = 'yang'
# 1.生成了session_id:随机字符串dsdggdf
# 2.在cookie里面加上了一个键值对session_id:dsdggdf
# 3.将用户的数据进行了加密,并保存到django-session表里面
'''
session_key session_data
dsdggdf 用户数据加密后的字符串
'''
return redirect('home')
else:
return redirect('login')
加入退出功能:
url(r'^logout/', views.logout,name='logout'),
home.html:
<div>
<a href="/logout/">退出</a>
</div>
def logout(request):
request.session.flush()
return redirect('login')
-
其他方法
所有 键、值、键值对
request.session.keys()
request.session.values()
request.session.items()会话session的key
session_key = request.session.session_key 获取sessionid的值
将所有Session失效日期小于当前日期的数据删除,将过期的删除
request.session.clear_expired()
检查会话session的key在数据库中是否存在
request.session.exists("session_key") #session_key就是那个sessionid的值
删除当前会话的所有Session数据
request.session.delete()
删除当前的会话数据并删除会话的Cookie。
request.session.flush() #常用,清空所有cookie---删除session表里的这个会话的记录,
这用于确保前面的会话数据不可以再次被用户的浏览器访问
例如,django.contrib.auth.logout() 函数中就会调用它。设置会话Session和Cookie的超时时间
request.session.set_expiry(value)
* 如果value是个整数,session会在些秒数后失效。
* 如果value是个datatime或timedelta,session就会在这个时间后失效。
* 如果value是0,用户关闭浏览器session就会失效。
* 如果value是None,session会依赖全局session失效策略。 -
改session_id
在settings里面设置:
SESSION_COOKIE_NAME = 'niubi'
然后在网页查看cookie:
-
Django中的Session配置
-
数据库Session
SESSION_ENGINE = 'django.contrib.sessions.backends.db' # 引擎(默认) -
缓存Session
SESSION_ENGINE = 'django.contrib.sessions.backends.cache' # 引擎
SESSION_CACHE_ALIAS = 'default' # 使用的缓存别名(默认内存缓存,也可以是memcache),此处别名依赖缓存的设置 -
文件Session
SESSION_ENGINE = 'django.contrib.sessions.backends.file' # 引擎
SESSION_FILE_PATH = None # 缓存文件路径,如果为None,则使用tempfile模块获取一个临时地址tempfile.gettempdir() -
缓存+数据库
SESSION_ENGINE = 'django.contrib.sessions.backends.cached_db' # 引擎 -
加密Cookie Session
SESSION_ENGINE = 'django.contrib.sessions.backends.signed_cookies' # 引擎
其他公用设置项:
SESSION_COOKIE_NAME = "sessionid" # Session的cookie保存在浏览器上时的key,即:sessionid=随机字符串(默认)
SESSION_COOKIE_PATH = "/" # Session的cookie保存的路径(默认)
SESSION_COOKIE_DOMAIN = None # Session的cookie保存的域名(默认)
SESSION_COOKIE_SECURE = False # 是否Https传输cookie(默认)
SESSION_COOKIE_HTTPONLY = True # 是否Session的cookie只支持http传输(默认)
SESSION_COOKIE_AGE = 1209600 # Session的cookie失效日期(2周)(默认)
SESSION_EXPIRE_AT_BROWSER_CLOSE = False # 是否关闭浏览器使得Session过期(默认)
SESSION_SAVE_EVERY_REQUEST = False # 是否每次请求都保存Session,默认修改之后才保存(默认) -