Django框架之六-->Cookie和Session
会话(Session)跟踪是Web程序中常用的技术,用来跟踪用户的整个会话。常用的会话跟踪技术是Cookie与Session。Cookie通过在客户端记录信息确定用户身份,Session通过在服务器端记录信息确定用户身份。
Cookie(储存在用户本地终端上的数据)
Cookie,有时也用其复数形式 Cookies,指某些网站为了辨别用户身份、进行 session 跟踪而储存在用户本地终端上的数据(通常经过加密)。
HTTP协议是一种无状态协议,在数据交换完毕后,服务器端和客户端的链接就会关闭,每次交换数据都需要建立新的链接。这就需要在请求中存放一段可以让客户端区分服务端的内容,这段内容就是Cookie.
Cookie就是由服务器发给客户端的特殊信息,而这些信息以文本文件的方式存放在客户端,然后客户端每次向服务器发送请求的时候都会带上这些特殊的信息。
Cookie特性:
1. 服务端可以在浏览器上设置COOKIE 保存在浏览器上的
2. 每次请求会携带COOKIE
3. Cookie是有效时间的
4. 可以用来做登录或其他事情
提示:Cookie中保存中文只能编码。一般使用UTF-8编码即可。不推荐使用GBK等中文编码,因为浏览器不一定支持,而且JavaScript也不支持GBK编码。
# 来自https://my.oschina.net/xianggao/blog/395675?fromerr=GC9KVenE 当用户使用浏览器访问一个支持Cookie的网站的时候,用户会提供包括用户名在内的个人信息并且提交至服务器; 接着,服务器在向客户端回传相应的超文本的同时也会发回这些个人信息,当然这些信息并不是存放在HTTP响应体(Response Body)中的, 而是存放于HTTP响应头(Response Header);当客户端浏览器接收到来自服务器的响应之后,浏览器会将这些信息存放在一个统一的位置, 对于Windows操作系统而言,我们可以从: [系统盘]:\Documents and Settings\[用户名]\Cookies目录中找到存储的Cookie;自此, 客户端再向服务器发送请求的时候,都会把相应的Cookie再次发回至服务器。而这次,Cookie信息则存放在HTTP请求头(Request Header)了。 有了Cookie这样的技术实现,服务器在接收到来自客户端浏览器的请求之后,就能够通过分析存放于请求头的Cookie得到客户端特有的信息, 从而动态生成与该客户端相对应的内容。通常,我们可以从很多网站的登录界面中看到“请记住我”这样的选项,如果你勾选了它之后再登录,那么 在下一次访问该网站的时候就不需要进行重复而繁琐的登录动作了,而这个功能就是通过Cookie实现的。 Web应用程序是使用HTTP协议传输数据的。HTTP协议是无状态的协议。一旦数据交换完毕,客户端与服务器端的连接就会关闭,再次交换数据需要 建立新的连接。这就意味着服务器无法从连接上跟踪会话。Cookie就是这样的一种机制。它可以弥补HTTP协议无状态的不足。 如果你把Cookies看成为http协议的一个扩展的话,理解起来就容易的多了,其实本质上cookies就是http的一个扩展。有两个http头部是专门 负责设置以及发送cookie的,它们分别是Set-Cookie以及Cookie。当服务器返回给客户端一个http响应信息时,其中如果包含Set-Cookie这个 头部时,意思就是指示客户端建立一个cookie,并且在后续的http请求中自动发送这个cookie到服务器端,直到这个cookie过期。如果cookie的 生存时间是整个会话期间的话,那么浏览器会将cookie保存在内存中,浏览器关闭时就会自动清除这个cookie。另外一种情况就是保存在客户端的 硬盘中,浏览器关闭的话,该cookie也不会被清除,下次打开浏览器访问对应网站时,这个cookie就会自动再次发送到服务器端。一个cookie的设 置以及发送过程分为以下四步: # 客户端发送一个http请求到服务器端 # 服务器端发送一个http响应到客户端,其中包含Set-Cookie头部 # 客户端发送一个http请求到服务器端,其中包含Cookie头部 # 服务器端发送一个http响应到客户端
登录百度后的响应头
登录百度后的请求头
Django中操作Coolie
设置与获取Coolie
普通版
# 在响应对象上设置Cookie rep = HttpResponse(...) rep = render(request, ...) rep = redirect(...) rep.set_cookie(key,value,...) set_cookie(self, key, value='', max_age=None, expires=None, path='/',domain=None, secure=False, httponly=False) # key, 键 # value='', 值 # max_age=None, Cookie保存时间(s) # expires=None, Cookie过期时间(datetime时间对象) # path='/', Cookie生效的路径,/ 表示根路径,特殊的:根路径的cookie可以被任何url的页面访问 # domain=None, Cookie生效的域名 # secure=False, https传输 # httponly=False 只能http协议传输,无法被JavaScript获取(不是绝对,底层抓包可以获取到也可以被覆盖)
# 获取cookie
request.COOKIES.get(键)
加盐版
比普通版多了一个盐,取值时也要根据盐取 # 设置Cookie rep = HttpResponse(...) rep = render(request, ...) rep = redirect(...) rep.set_signed_cookie(key,value,salt='加密盐',...) # 获取 get_signed_cookie(self, key, default=RAISE_ERROR, salt='', max_age=None) # salt: 加密盐 # default: 取不到的默认值
Cookie登录装饰器
# 注意模板中表单提交的url是不固定的 # 装饰器 def time(func): def inner(request): user = request.COOKIES.get("user") # 获取Cookie url = request.get_full_path() # 获取带参数的url if user: # 如果获取到了Cookie说明登陆过 return func(request) # 跳转到要去的页面 # 没有登陆过跳转到登录页面,并且携带跳转之前的url return redirect("/login/?from={}".format(url)) return inner # 登录视图函数 def login(request): url = request.GET.get("from") # 获得跳转来的url if request.method == "POST": name = request.POST.get("user") pwd = request.POST.get("pwd") # 验证密码 if (name,pwd) in user_info: if url: # 如果是跳转来的,就跳转回去 ret = redirect(url) else: # 如果是直接登录就跳转到主页 ret = redirect("//") ret.set_cookie("user",name) # 添加Cookie信息 return ret return render(request,"loggin.html") # 其他页面 @time def show(request): #inner(request) user = request.COOKIES.get("user") return render(request,"show.html",{"user":user})
Session
除了使用Cookie,Web应用程序中还经常使用Session来记录客户端状态。Session是服务器端使用的一种记录客户端状态的机制,使用上比Cookie简单一些,相应的也增加了服务器的存储压力。
Session技术则是服务端的解决方案,它是通过服务器来保持状态的。由于Session这个词汇包含的语义很多,因 此需要在这里明确一下 Session的含义。首先,我们通常都会把Session翻译成会话,因此我们可以把客户端浏 览器与服务器之间一系列交互的动作称为一个 Session。从这个语义出发,我们会提到Session持续的时间,会 提到在Session过程中进行了什么操作等等;其次,Session指的是服务器端为客户端所开辟的存储空间,在其中 保存的信息就是用于保持状态。从这个语义出发,我们则会提到往Session中存放什么内容,如何根据键值从 Session中获取匹配的内容等。要使用Session,第一步当然是创建Session了。那么Session在何时创建呢?当 然还是在服务器端程序运行的过程中创建的,不同语言实现的应用程序有不同创建Session的方法,而在Java中 是通过调用HttpServletRequest的getSession方法(使用true作为参数)创建的。在创建了Session的同时, 服务器会为该Session生成唯一的Session id,而这个Session id在随后的请求中会被用来重新获得已经创建的 Session;在Session被创建之后,就可以调用Session相关的方法往Session中增加内容了,而这些内容只会保 存在服务器中,发到客户端的只有Session id;当客户端再次发送请求的时候,会将这个Session id带上,服务 器接受到请求之后就会依据Session id找到相应的Session,从而再次使用之。正式这样一个过程,用户的状态也 就得以保持了。 ###什么是Session### Session是另一种记录客户状态的机制,不同的是Cookie保存在客户端浏览器中,而 Session保存在服务器上。客户端浏览器访问服务器的时候,服务器把客户端信息以某种形式记录在服务器上。这 就是Session。客户端浏览器再次访问时只需要从该Session中查找该客户的状态就可以了。 如果说Cookie机制是通过检查客户身上的“通行证”来确定客户身份的话,那么Session机制就是通过检查服务器 上的“客户明细表”来确认客户身份。Session相当于程序在服务器上建立的一份客户档案,客户来访的时候只需要 查询客户档案表就可以了。 虽然Session保存在服务器,对客户端是透明的,它的正常运行仍然需要客户端浏览器的支持。这是因为Session 需要使用Cookie作为识别标志。
Session是基于Cookie的,Django中request对象设置Session之后会给客户端添加一个Cookie,同时将该Cookie对应的信息存放在本地.
Session为客户端发送的Cookie
服务端存储的数据 - 客户端的值便是服务端数据的主键,session_data是键值对序列化加密后得到的,expire_date是session_key过期时间,
客户端发来请求时,服务端拿到sessionid对应的值,再根据该值拿到该用户对应的信息.
无论是客户端Cookie失效,还是服务端Session失效,都会让客户端与服务端失去联系
使用Session
def index(request): # 获取、设置、删除Session中数据 request.session['k1'] # 获取 拿不到报错 request.session.get('k1',None) # 获取 request.session['k1'] = 123 # 设置 request.session.setdefault('k1',123) # 存在则不设置 del request.session['k1'] # 删除键值对并不是删除session # 所有 键、值、键值对 request.session.keys() request.session.values() request.session.items() request.session.iterkeys() request.session.itervalues() request.session.iteritems() # 用户session的随机字符串 request.session.session_key # 将所有Session失效日期小于当前日期的数据删除 request.session.clear_expired() # 检查 用户session的随机字符串 在数据库中是否 request.session.exists("session_key") # 删除当前用户的所有Session数据 request.session.delete() # 将客户端Cookie设置为空 request.session.flush() # 删除掉客户端的Cookie request.session.set_expiry(value) * 如果value是个整数,session会在些秒数后失效。 * 如果value是个datatime或timedelta,session就会在这个时间后失效。 * 如果value是0,用户关闭浏览器session就会失效。 * 如果value是None,session会依赖全局session失效策略。
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其他公用选项
# Session的cookie保存在浏览器上时的key,即:sessionid=随机字符串(默认) SESSION_COOKIE_NAME = "sessionid" # Session的cookie保存的路径(默认) SESSION_COOKIE_PATH = "/" # Session的cookie保存的域名(默认) SESSION_COOKIE_DOMAIN = None # 是否Https传输cookie(默认) SESSION_COOKIE_SECURE = False # 是否Session的cookie只支持http传输(默认) SESSION_COOKIE_HTTPONLY = True # Session的cookie失效日期(2周)(默认) SESSION_COOKIE_AGE = 1209600 # 是否关闭浏览器使得Session过期(默认) SESSION_EXPIRE_AT_BROWSER_CLOSE = False # 是否每次请求都保存Session,默认修改之后才保存(默认) SESSION_SAVE_EVERY_REQUEST = False
CBV中加装饰器
from django.utils.decorators import method_decorator # 装饰器函数 def check_login(func): pass # 要在CBV视图中使用我们上面的check_login装饰器,有以下三种方式: #1. 加在CBV视图的get或post方法上 class HomeView(View): def dispatch(self, request, *args, **kwargs): return super(HomeView, self).dispatch(request, *args, **kwargs) def get(self, request): return render(request, "home.html") @method_decorator(check_login) # 这里 def post(self, request): print("Home View POST method...") return redirect("/index/") # 加在dispatch方法上 # 因为CBV中首先执行的就是dispatch方法,所以这么写相当于给get和post方法都加上了登录校验。 class HomeView(View): @method_decorator(check_login) def dispatch(self, request, *args, **kwargs): return super(HomeView, self).dispatch(request, *args, **kwargs) def get(self, request): return render(request, "home.html") def post(self, request): print("Home View POST method...") return redirect("/index/") # 直接加在视图类上,但method_decorator必须传 name 关键字参数 # 如果get方法和post方法都需要登录校验的话就写两个装饰器。 @method_decorator(check_login, name="get") @method_decorator(check_login, name="post") class HomeView(View): def dispatch(self, request, *args, **kwargs): return super(HomeView, self).dispatch(request, *args, **kwargs) def get(self, request): return render(request, "home.html") def post(self, request): print("Home View POST method...") return redirect("/index/")
补充
CSRF Token相关装饰器在CBV只能加到dispatch方法上
备注:
- csrf_protect,为当前函数强制设置防跨站请求伪造功能,即便settings中没有设置全局中间件。
- csrf_exempt,取消当前函数防跨站请求伪造功能,即便settings中设置了全局中间件。
from django.views.decorators.csrf import csrf_exempt, csrf_protect class HomeView(View): @method_decorator(csrf_exempt) def dispatch(self, request, *args, **kwargs): return super(HomeView, self).dispatch(request, *args, **kwargs) def get(self, request): return render(request, "home.html") def post(self, request): print("Home View POST method...") return redirect("/index/")