Django框架—cookie和session
1.什么是会话
会话,可以理解为客户端与服务器之间的一次会晤,在一次会晤中可能包含多次的请求和响应,直到这次会晤结束,这整个过程就是一次会话。
比如你访问浏览器,浏览器客户端向某一服务器发出第一个请求开始,会话就开始了,直到客户关闭了浏览器会话结束。
在一个会话中的多个请求中共享数据,就是会话跟踪技术,例如:
-
请求银行主页;
-
请求登录(请求参数是用户名和密码);
-
请求转账(请求参数与转账相关的数据);
-
请求信誉卡还款(请求参数与还款相关的数据)。
在上述会话中,当前用户信息在这次会话中是共享的,登录的人和转账以及还款的人一定是相同的,也就是再一次会话中我们要实现数据共享的能力,但是我们知道http协议是无状态和无连接的,这个时候就需要别的机制来实现,也就是cookie技术。
二、cookie
1.Cookie的简介
我们知道HTTP协议是无状态的。
所谓无状态的意思是每次请求都是独立的,它的执行情况和结果与前面的请求和之后的请求都无直接关系,它不会受前面的请求响应情况直接影响,也不会直接影响后面的请求响应情况。
也就是说浏览器的请求对服务器来说,每次都是新的。
状态可以理解为客户端和服务器在某次会话中产生的数据,那无状态的就以为这些数据不会被保留。会话中产生的数据又是我们需要保存的,也就是说要“保持状态”。因此Cookie就是在这样一个场景下诞生。
2.cookie工作原理
cookie是浏览器的技术,Cookie具体指的是一段小信息,它是服务器发送出来存储在浏览器上的一组组键值对,下次访问服务器时浏览器会自动携带这些键值对,以便服务器提取有用信息。
cookie的工作原理是:浏览器访问服务端,带着一个空的cookie,然后由服务器产生内容,浏览器收到相应后保存在本地;当浏览器再次访问时,浏览器会自动带上Cookie,这样服务器就能通过Cookie的内容来判断请求者是“谁”。
cookie的查看方式
使用chrome浏览器,F12打开开发者工具,查看cookie
cookie的工作图解
cookie规范
-
Cookie大小上限为4KB;
-
一个服务器最多在客户端浏览器上保存20个Cookie;
-
一个浏览器最多保存300个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。
关于cookie理解:
一个浏览器客户端并一个服务端对应一组cookie,多账号会覆盖。
意思是说,如果你使用同一个浏览器,登录一个帐号访问一个网址,只有一组cookie,如果你登录另外一个帐号访问这个网址,那么也只能存在一组cookie,后面登录的cookie会把之前的cookie顶替掉。
3.Django中操作cookie
Ctrl + Shift + del三个键来清除页面缓存和cookie,将来这个操作你会用的很多
获取cookie方法
request.COOKIES['key'] # 通过键获取cookies中的某个值 request.COOKIES.get('key') # 推荐 request.get_signed_cookie(key, default=RAISE_ERROR, salt='', max_age=None)
参数说明:
-
default: 默认值
-
salt: 加密盐
-
max_age: 后台控制过期时间
设置cookie方法
cookie设置需要通过HttpResponse响应对象调用set_cookie方法来给cookie添加数据。
response = HttpResponse(...) # 或者 response = render(request,...) # 设置不加盐cookie内容 response.set_cookie(key,value) # 设置加盐cookie内容 response.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获取(不是绝对,底层抓包可以获取到也可以被覆盖)
删除cookie方法
删除cookie是通过HttpResponse对象调用delete_cookie方法删除
def logout(request): response = redirect("/login/") # 获取响应对象 response.delete_cookie("user") # 删除用户浏览器上之前设置的usercookie值 return repsonse
4.cookie带装饰器的登录验证
url.py文件
from cookie_lesson import views urlpatterns = [ # url(r'^admin/', admin.site.urls), url(r'^login/', views.login,name="login"), url(r'^home/', views.home,name="home"), url(r'^logout/', views.logout,name="logout"), ]
home.html文件
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <h1>欢迎来到主页</h1> <a href="{% url 'logout' %}"> 注销 </a> <script src="https://cdn.bootcss.com/jquery/3.4.1/jquery.js"></script> <script></script> </body> </html>
login.html文件
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <h1>登录界面</h1> <form action="{% url 'login' %}" method="post"> {% csrf_token %} 用户名:<input type="text" name="username"> 密码:<input type="password" name="password"> <input type="submit"> </form> <script src="https://cdn.bootcss.com/jquery/3.4.1/jquery.js"></script> <script></script> </body> </html>
views.py文件
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
def auth(func): """定义装饰器,来对每个操作验证是否登录""" def wrapper(request): if request.method == "GET": # 获取cookie中的数据,如果有而且与服务端发送的cookie数据一致,就通过验证,无需重新登录,直接访问 is_login = request.COOKIES.get('is_login') username = request.COOKIES.get('user') if is_login == 'True' and username == "alex": ret = func(request) return ret return redirect('login') # 如果发现cookie中没有,则跳转登录页面 return wrapper def login(request): """登录函数""" if request.method=="GET": return render(request,'login.html') else: username = request.POST.get("username") password = request.POST.get("password") if username == "alex" and password == "alex": # 验证账号密码 ret = render(request,'home.html') # 登录成功后,添加cookie数据返回客户端 ret.set_cookie("is_login",True) ret.set_cookie("user",username) return ret else: return redirect('login') @auth def home(request): """展示主页,登录状态能访问""" return render(request, "home.html") @auth def logout(request): """注销,登录状态能访问""" res = redirect('login') res.delete_cookie('is_login') res.delete_cookie('user') return res
三、session
1.session的由来
Cookie虽然在一定程度上解决了“保持状态”的需求,但是由于Cookie本身最大支持4096字节,以及Cookie本身保存在客户端,可能被拦截或窃取。
因此就需要有一种新的东西,它能支持更多的字节,并且他保存在服务器,有较高的安全性。这就是Session。
由于HTTP协议的无状态特征,服务器根本就不知道访问者是“谁”,我们仍需要cookie来起到桥接作用。
工作原理
我们可以给每个客户端的Cookie分配一个唯一的id,这样用户在访问时,通过Cookie,服务器就知道来的人是“谁”。然后我们再根据不同的Cookie的id,在服务器上保存一段时间的私密资料,如“账号密码”等等。
cookie和session区别
Cookie弥补了HTTP无状态的不足,让服务器知道来的人是“谁”;但是Cookie以文本的形式保存在本地,自身安全性较差;
通过Cookie识别不同的用户,对应的在Session里保存私密的信息以及超过4096字节的文本。
2.Django中的session
在Django框架中,DJango框架已将cookie和session的数据封装好,存储在自己的数据库中的Django_session表中。
session的相关方法
django将session的数据封装在request请求体中,注意session_id是Django帮我们自动生成的随机字符串。
获取session方法
request.session['key'] request.session.get('key',None)
从cookie里面将sessionid的值取出来,将django-session表里面的对应sessionid的值的那条记录中的session-data字段的数据给你拿出来(并解密)
设置session值
request.session['key'] = 123 # 给session添加键值对数据 request.session.setdefault('key',123) # 如果键存在,不设置,不存在设置键值对
django-session这个表,你不能通过orm来直接控制,因为你的models.py里面没有这个对应关系
删除session值
-
del request.session['key'] :django-session表里面同步删除
-
request.session.delete():仅删除当前会话的所有django_session表中的数据,不常用
-
request.session.flush():删除当前的会话django_session表中数据并删除会话的Cookie,常用
session的其他方法
-
request.session.session_key :获取session_id的值
-
request.session.keys():获取数据库中session的所有键
-
request.session.values():获取数据库中session所有的值
-
request.session.items():获取数据库中session数据的所有键值对
-
request.session.clear_expired():删除数据库中过期的session数据
-
request.session.set_expiry(value):设置会话的超时时间。
-
value:整数,表示多少秒失效;datetime表示到该时间失效;0表示关闭浏览器就失效;None,依赖全局session失效。
-
3.Session版登陆验证实例
这里只展示views.py文件的代码,其他与模板文件和url配置与前面cookie版相同。
思路:我们平时访问页面,必须先登录,正常登陆会跳转到home页面。但是有一种情况,有人会直接访问index页面,比如淘宝的购物车,没有登录时去结算,会跳转到登录页面。
就好比你淘宝去登录成功了,就是直接跳到了结算页面,并不需要你再去点结算页面。
这样用户体验才会比较舒服。
views.py文件
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
def auth(func): """定义装饰器,来对每个操作验证是否登录""" def wrapper(request): if request.method == "GET": # 1.从cookie中找到session_id的值:随机字符串 # 2.通过随机字符串去数据库django_session表中查找记录,拿到后进行对比 is_login = request.session.get('is_login') username = request.session.get('user') if is_login == True: ret = func(request) return ret return redirect('login') # 如果发现cookie中没有或者数据与服务端不一致,则跳转登录页面 return wrapper def login(request): """登录函数""" if request.method=="GET": return render(request,'login.html') else: username = request.POST.get("username") password = request.POST.get("password") if username == "alex" and password == "alex": # 验证账号密码 request.session['is_login'] = True request.session['user'] = username # 1.生成随机字符串session_id:随机字符串 # 2.加密用户信息,保存到数据库,Django中保存在Django_session表中 # 3.将session_id:随机字符串放到cookie发送客户端 return redirect('home') else: return redirect('login') @auth def home(request): """展示主页,登录状态能访问""" return render(request, "home.html") @auth def logout(request): """注销,登录状态能访问""" request.session.flush() # 删除当前的会话数据并删除会话的Cookie # request.session.delete() # 仅仅删除当前会话的所有Session数据 return redirect('login')
4.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,默认修改之后才保存(默认)
5.CBV模式添加装饰器
CBV添加装饰器的方式:视图函数中已经学习过。
-
加在CBV视图的某个方法上,如get或post方法上。
-
加在dispatch方法上,为所有函数加上装饰器,注意执行父类的dispatch方法,并返回。
-
加在视图类上,但method_decorator必须传 name 关键字参数。
需要导入一个装饰器方法
from django.utils.decorators import method_decorator
项目实践
一个完整的url路由分发,装饰器验证登录的通过CBV模式实现的登录验证项目
项目下总路由分发文件urls.py
from django.conf.urls import url,include from django.contrib import admin urlpatterns = [ # url(r'^admin/', admin.site.urls), url(r'^CBV_decoration', include("CBV_decoration.urls")), # include路径分发到应用CBV_decoration下的urls ]
应用CBV_decoration下的urls文件
from django.conf.urls import url from CBV_decoration.views import Logout,Login,Home,Index urlpatterns = [ url(r'/login/', Login.as_view(),name="login"), # 登录页面url url(r'/home/', Home.as_view(),name="home"), # home页面url url(r'/index/', Index.as_view(),name="index"), # index页面url url(r'/logout/', Logout.as_view(),name="logout"), # 注销url ]
应用CBV_decoration下的views.py
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
from django.shortcuts import render,redirect,HttpResponse from django.views import View from django.utils.decorators import method_decorator from functools import wraps # Create your views here. def auth(func): """定义装饰器,来对每个操作验证是否登录""" def wrapper(request,*args,**kwargs): if request.method == "GET": next_url = request.get_full_path() if request.session.get('user'): # 如果拿到了用户,说明已经登录,直接允许访问想要访问的页面 ret = func(request,*args,**kwargs) return ret else: # 如果没有登录,存下想要访问的url,让他先去登录 return redirect('CBV_decoration/login/?next={}'.format(next_url)) # 如果发现cookie中没有取到用户,则跳转登录页面,并且将想要访问的路径封装在get请求的url中 return wrapper class Login(View): """登录函数""" def get(self,request): return render(request,'login.html') def post(self,request): username = request.POST.get("username") password = request.POST.get("password") if username == "alex" and password == "alex": # 验证账号密码 request.session['user'] = username # 1.生成随机字符串session_id:随机字符串 # 2.加密用户信息,保存到数据库,Django中保存在Django_session表中 # 3.将session_id:随机字符串放到cookie发送客户端 next_url = request.GET.get('next') # 获取未登录是访问页面的路径 if next_url: # 如果有直接跳转 return redirect(next_url) else: # 没有的话正常登录到home页面 return redirect('home') class Home(View): """展示主页,登录状态能访问""" @method_decorator(auth) def dispatch(self, request, *args, **kwargs): ret = super().dispatch(request, *args, **kwargs) return ret def get(self,request): return render(request, "home.html") class Index(View): @method_decorator(auth) def dispatch(self, request, *args, **kwargs): ret = super().dispatch(request, *args, **kwargs) return ret def get(self,request): return render(request, "index.html") class Logout(View): """注销,登录状态能访问""" @method_decorator(auth) def dispatch(self, request, *args, **kwargs): ret = super().dispatch(request, *args, **kwargs) return ret def get(self,request): request.session.flush() # 删除当前的会话数据并删除会话的Cookie # request.session.delete() # 仅仅删除当前会话的所有Session数据 return redirect('login')
模板template文件夹
login.html
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <h1>登录界面</h1> <form action="" method="post"> <!--注意action路径不填,提交到当前路径--> {% csrf_token %} 用户名:<input type="text" name="username"> 密码:<input type="password" name="password"> <input type="submit"> </form> <script src="https://cdn.bootcss.com/jquery/3.4.1/jquery.js"></script> <script></script> </body> </html>
home.html
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <h1>Home页面</h1> <a href="{% url 'index' %}">进入首页</a> <br> <a href="{% url 'logout' %}"> 注销 </a> <script src="https://cdn.bootcss.com/jquery/3.4.1/jquery.js"></script> <script></script> </body> </html>
index.html
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <h1>index页面</h1> <br> <a href="{% url 'logout' %}"> 注销 </a> <script src="https://cdn.bootcss.com/jquery/3.4.1/jquery.js"></script> <script></script> </body> </html>
四、jQuery操作cookie
通过jQuery获取cookie的数据,添加或者删除cookie键值对。
使用之jQuery操作cookie需要引入jQuery.cookie.js插件,而jQuery.cookie.js基于jQuery;
jQuery.cookie.js下载路径:http://plugins.jquery.com/cookie/
<script src="{% static 'jquery/3.4.1/jquery.js' %}"></script> <!--导入jQuery--> <script src="{% static 'jquery.cookie.js' %}"></script> <!--引用jquery操作cookie的插件-->
1.添加一个cookie会话键值对
$.cookie('key','value')
这里没有指明 cookie有效时间,所创建的cookie有效期默认到用户关闭浏览器为止,所以被称为 “会话cookie(session cookie)”。
2.创建有效期的cookie
$.cookie('key', 'value', { expires: 7 });
这里指明了cookie有效时间,所创建的cookie被称为“持久 cookie (persistent cookie)”。注意单位是:天;
3.创建cookie并设置cookie的有效路径
$.cookie('key', 'value', { expires: 7, path: '/' });
在默认情况下,只有设置 cookie的网页才能读取该 cookie。
如果想让一个页面读取另一个页面设置的cookie,必须设置cookie的路径。cookie的路径用于设置能够读取 cookie的顶级目录。
将这个路径设置为网站的根目录,可以让所有网页都能互相读取 cookie (一般不要这样设置,防止出现冲突)。
4.读取cookie
$.cookie('key');
5.删除cookie
$.cookie('the_cookie', null); //通过传递null作为cookie的值即可
6.可选参数
-
expires:(Number|Date)有效期;设置一个整数时,单位是天;也可以设置一个日期对象作为Cookie的过期日期;
-
path:(String)创建该Cookie的页面路径;
-
domain:(String)创建该Cookie的页面域名;
-
secure:(Booblean)如果设为true,那么此Cookie的传输会要求一个安全协议,例如:HTTPS;
$.cookie('key','value',{ expires:7, path:'/', domain:'jquery.com', secure:true })