网站在对第一次访问的请求中会返回set-cookie,浏览器会将set-cookie进行存储,以后再进行访问时就会带上cookie值。so~第一次访问请求头是没有cookie的,如果你的有,那就是你之前访问过,cookie值一直保留在浏览器内。
cookie基于服务端生成的,在客户端的头信息中,在第一次把请求发到服务端,服务端生成cookie,存放到客户端,下次再请求的时候会带上cookie,redis存session,有缓存时间,过了一段时间就会过期。客户端访问的时候,会带上身份标识,这个标识存在cookie中,当web浏览器禁用cookie的时候,session也失效了。
大家都知道HTTP协议是无状态的。
无状态的意思是每次请求都是独立的,它的执行情况和结果与前面的请求和之后的请求都无直接关系,它不会受前面的请求响应情况直接影响,也不会直接影响后面的请求响应情况。
一句有意思的话来描述就是人生只如初见,对服务器来说,每次的请求都是全新的。状态可以理解为客户端和服务器在某次会话中产生的数据,那无状态的就以为这些数据不会被保留。会话中产生的数据又是我们需要保存的,也就是说要“保持状态”。因此Cookie就是在这样一个场景下诞生。
正统的cookie分发是通过扩展HTTP协议来实现的,服务器通过在HTTP的响应头中加上一行特殊的指示以提示浏览器按照指示生成相应的cookie。然而纯粹的客户端脚本如JavaScript或者VBScript也可以生成cookie。而cookie的使用是由浏览器按照一定的原则在后台自动发送给服务器的。浏览器检查所有存储的cookie,如果某个cookie所声明的作用范围大于等于将要请求的资源所在的位置,则把该cookie附在请求资源的HTTP请求头上发送给服务器。
具体来说cookie机制采用的是在客户端保持状态的方案,而session机制采用的是在服务器端保持状态的方案。同时我们也看到,由于采用服务器端保持状态的方案在客户端也需要保存一个标识,所以session机制可能需要借助于cookie机制来达到保存标识的目的,但实际上它还有其他选择。
Cookie具体指的是一段小信息,它是服务器发送出来存储在浏览器上的一组组键值对,下次访问服务器时浏览器会自动携带这些键值对,以便服务器提取有用信息。
cookie的内容主要包括:名字,值,过期时间,路径和域。路径与域一起构成cookie的作用范围。若不设置过期时间,则表示这个cookie的生命期为浏览器会话期间,关闭浏览器窗口,cookie就消失。这种生命期为浏览器会话期的cookie被称为会话cookie。
cookie可以被人为的禁止,则必须有其他机制以便在cookie被禁止时仍然能够把session id传递回服务器。
经常被使用的一种技术叫做URL重写,就是把session id直接附加在URL路径的后面。
还有一种技术叫做表单隐藏字段。就是服务器会自动修改表单,添加一个隐藏字段,以便在表单提交时能够把session id传递回服务器。比如:
<form name="testform" action="/xxx"> <input type="hidden" name="jsessionid" value="ByOK3vjFD75aPnrF7C2HmdnV6QZcEbzWoWiBYEnLerjQ99zWpBng!-145788764"> <input type="text"> </form>实际上这种技术可以简单的用对action应用URL重写来代替。
cookie的工作原理是:由服务器产生内容(通过response响应头的set-Cookie字段进行设置),浏览器收到请求后保存在本地;当浏览器再次访问时,浏览器会自动带上Cookie,这样服务器就能通过Cookie的内容来判断这个是“谁”了。下面是一个示例:
[HTTP/1.1 200 OK] Server:[bfe/1.0.8.18] Etag:["58860415-98b"] Cache-Control:[private, no-cache, no-store, proxy-revalidate, no-transform] Connection:[Keep-Alive] Set-Cookie:[BDORZ=27315; max-age=86400; domain=.baidu.com; path=/] Pragma:[no-cache] Last-Modified:[Mon, 23 Jan 2017 13:24:37 GMT] Content-Length:[2443] Date:[Mon, 09 Apr 2018 09:59:06 GMT] Content-Type:[text/html]Cookie包含什么信息?
它可以记录你的用户ID、密码、浏览过的网页、停留的时间等信息。当你再次来到该网站时,网站通过读取Cookies,得知你的相关信息,就可以做出相应的动作,如在页面显示欢迎你的标语,或者让你不用输入ID、密码就直接登录等等。一个网站只能读取它自己放置的信息,不能读取其他网站的Cookie文件。因此,Cookie文件还保存了host属性,即网站的域名或ip。 这些属性以名值对的方式进行保存,为了安全,它的内容大多进行了加密处理。Cookie文件的命名格式是:用户名@网站地址[数字].txt
Cookie的优点:
给用户更人性化的使用体验,如记住“密码功能”、老用户登录欢迎语弥补了HTTP无连接特性站点统计访问人数的一个依据Cookie的缺点:
它无法解决多人共用一台电脑的问题,带来了不安全因素Cookie文件容易被误删除一人使用多台电脑Cookies欺骗。修改host文件,可以非法访问目标站点的Cookie
我们使用Chrome浏览器,打开开发者工具。
参数:
default: 默认值salt: 加密盐max_age: 后台控制过期时间参数:
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版登陆校验
def check_login(func): @wraps(func) def inner(request, *args, **kwargs): next_url = request.get_full_path() if request.get_signed_cookie("login", salt="SSS", default=None) == "yes": # 已经登录的用户... return func(request, *args, **kwargs) else: # 没有登录的用户,跳转刚到登录页面 return redirect("/login/?next={}".format(next_url)) return inner def login(request): if request.method == "POST": username = request.POST.get("username") passwd = request.POST.get("password") if username == "xxx" and passwd == "dashabi": next_url = request.GET.get("next") if next_url and next_url != "/logout/": response = redirect(next_url) else: response = redirect("/class_list/") response.set_signed_cookie("login", "yes", salt="SSS") return response return render(request, "login.html") cookie版登录相关HTML代码
#login <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>login</title> </head> <body> {#action="",不写是当前地址#} <form action="" method="post"> {% csrf_token %} <p>用户名: <input type="text" name="username"> </p> <p>密码: <input type="password" name="password"> </p> <p> <button type="submit">提交</button> <span>{{ msg }}</span> </p> </form> </body> </html> #vip <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>vip</title> </head> <body> <hi>登录才能看到</hi> <a href="/logout/">注销</a> </body> </html>Cookie虽然在一定程度上解决了“保持状态”的需求,但是由于Cookie本身最大支持4096字节,以及Cookie本身保存在客户端,可能被拦截或窃取,因此就需要有一种新的东西,它能支持更多的字节,并且他保存在服务器,有较高的安全性。这就是Session。
问题来了,基于HTTP协议的无状态特征,服务器根本就不知道访问者是“谁”。那么上述的Cookie就起到桥接的作用。
我们可以给每个客户端的Cookie分配一个唯一的id,这样用户在访问时,通过Cookie,服务器就知道来的人是“谁”。然后我们再根据不同的Cookie的id,在服务器上保存一段时间的私密资料,如“账号密码”等等。
当程序需要为某个客户端的请求创建一个session时,服务器首先检查这个客户端的请求里是否已包含了一个session标识(称为session id),如果已包含则说明以前已经为此客户端创建过session,服务器就按照session id把这个session检索出来使用(检索不到,会新建一个),如果客户端请求不包含session id,则为此客户端创建一个session并且生成一个与此session相关联的session id,session id的值应该是一个既不会重复,又不容易被找到规律以仿造的字符串,这个session id将被在本次响应中返回给客户端保存。保存这个session id的方式可以采用cookie,这样在交互过程中浏览器可以自动的按照规则把这个标识发送给服务器。一般这个cookie的名字都是类似于SEEESIONID。
总结而言:Cookie弥补了HTTP无状态的不足,让服务器知道来的人是“谁”;但是Cookie以文本的形式保存在本地,自身安全性较差;所以我们就通过Cookie识别不同的用户,对应的在Session里保存私密的信息以及超过4096字节的文本。
另外,上述所说的Cookie和Session其实是共通性的东西,不限于语言和框架。
Django中默认支持Session,其内部提供了5种类型的Session供开发者使用。
1. 数据库Session SESSION_ENGINE = 'django.contrib.sessions.backends.db' # 引擎(默认) 2. 缓存Session SESSION_ENGINE = 'django.contrib.sessions.backends.cache' # 引擎 SESSION_CACHE_ALIAS = 'default' # 使用的缓存别名(默认内存缓存,也可以是memcache),此处别名依赖缓存的设置 3. 文件Session SESSION_ENGINE = 'django.contrib.sessions.backends.file' # 引擎 SESSION_FILE_PATH = None # 缓存文件路径,如果为None,则使用tempfile模块获取一个临时地址tempfile.gettempdir() 4. 缓存+数据库 SESSION_ENGINE = 'django.contrib.sessions.backends.cached_db' # 引擎 5. 加密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,默认修改之后才保存(默认) Django中Session相关设置CBV实现的登录视图
class LoginView(View): def get(self, request): """ 处理GET请求 """ return render(request, 'login.html') def post(self, request): """ 处理POST请求 """ user = request.POST.get('user') pwd = request.POST.get('pwd') if user == 'aaa' and pwd == "aaa1234": next_url = request.GET.get("next") # 生成随机字符串 # 写浏览器cookie -> session_id: 随机字符串 # 写到服务端session: # { # "随机字符串": {'user':'alex'} # } request.session['user'] = user if next_url: return redirect(next_url) else: return redirect('/index/') return render(request, 'login.html')要在CBV视图中使用我们上面的check_login装饰器,有以下三种方式:
from django.utils.decorators import method_decorator
1. 加在CBV视图的get或post方法上
from django.utils.decorators import method_decorator 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/")2. 加在dispatch方法上
from django.utils.decorators import method_decorator 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/")因为CBV中首先执行的就是dispatch方法,所以这么写相当于给get和post方法都加上了登录校验。
3. 直接加在视图类上,但method_decorator必须传 name 关键字参数
如果get方法和post方法都需要登录校验的话就写两个装饰器。
from django.utils.decorators import method_decorator @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方法上,或者加在视图类上然后name参数指定为dispatch方法。
备注:
csrf_protect,为当前函数强制设置防跨站请求伪造功能,即便settings中没有设置全局中间件。csrf_exempt,取消当前函数防跨站请求伪造功能,即便settings中设置了全局中间件。 from django.views.decorators.csrf import csrf_exempt, csrf_protect from django.utils.decorators import method_decorator 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/")或者
from django.views.decorators.csrf import csrf_exempt, csrf_protect from django.utils.decorators import method_decorator @method_decorator(csrf_exempt, name='dispatch') 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/")html
{#login#} <body> {#action="",不写是当前地址#} <form action="" method="post"> {% csrf_token %} <p>用户名: <input type="text" name="username"> </p> <p>密码: <input type="password" name="password"> </p> <p> <button type="submit">提交</button> <span>{{ msg }}</span> </p> </form> </body> #vip#} <body> <hi>登录才能看到</hi> <a href="/logout/">注销</a> </body> #csrf_test <body> <form action="/csrf_test/" method="post"> <input type="text" name="xx"> <button type="submit">提交</button> </form>稳扎稳打版
data = [] for i in range(1, 302): tmp = {"id": i, "name": "alex-{}".format(i)} data.append(tmp) print(data) def user_list(request): # user_list = data[0:10] # user_list = data[10:20] try: current_page = int(request.GET.get("page")) except Exception as e: current_page = 1 per_page = 10 # 数据总条数 total_count = len(data) # 总页码 total_page, more = divmod(total_count, per_page) if more: total_page += 1 # 页面最多显示多少个页码 max_show = 11 half_show = int((max_show-1)/2) if current_page <= half_show: show_start = 1 show_end = max_show else: if current_page + half_show >= total_page: show_start = total_page - max_show show_end = total_page else: show_start = current_page - half_show show_end = current_page + half_show # 数据库中获取数据 data_start = (current_page - 1) * per_page data_end = current_page * per_page user_list = data[data_start:data_end] # 生成页面上显示的页码 page_html_list = [] # 加首页 first_li = '<li><a href="/user_list/?page=1">首页</a></li>' page_html_list.append(first_li) # 加上一页 if current_page == 1: prev_li = '<li><a href="#">上一页</a></li>' else: prev_li = '<li><a href="/user_list/?page={}">上一页</a></li>'.format(current_page - 1) page_html_list.append(prev_li) for i in range(show_start, show_end+1): if i == current_page: li_tag = '<li class="active"><a href="/user_list/?page={0}">{0}</a></li>'.format(i) else: li_tag = '<li><a href="/user_list/?page={0}">{0}</a></li>'.format(i) page_html_list.append(li_tag) # 加下一页 if current_page == total_page: next_li = '<li><a href="#">下一页</a></li>' else: next_li = '<li><a href="/user_list/?page={}">下一页</a></li>'.format(current_page+1) page_html_list.append(next_li) # 加尾页 page_end_li = '<li><a href="/user_list/?page={}">尾页</a></li>'.format(total_page) page_html_list.append(page_end_li) page_html = "".join(page_html_list) return render(request, "user_list.html", {"user_list": user_list, "page_html": page_html}) 稳扎稳打版封装保存版
class Pagination(object): def __init__(self, current_page, total_count, base_url, per_page=10, max_show=11): """ :param current_page: 当前页 :param total_count: 数据库中数据总数 :param per_page: 每页显示多少条数据 :param max_show: 最多显示多少页 """ try: current_page = int(current_page) except Exception as e: current_page = 1 self.current_page = current_page self.total_count = total_count self.base_url = base_url self.per_page = per_page self.max_show = max_show # 总页码 total_page, more = divmod(total_count, per_page) if more: total_page += 1 half_show = int((max_show - 1) / 2) self.half_show = half_show self.total_page = total_page @property def start(self): return (self.current_page - 1) * self.per_page @property def end(self): return self.current_page * self.per_page def page_html(self): if self.current_page <= self.half_show: show_start = 1 show_end = self.max_show else: if self.current_page + self.half_show >= self.total_page: show_start = self.total_page - self.max_show show_end = self.total_page else: show_start = self.current_page - self.half_show show_end = self.current_page + self.half_show # 生成页面上显示的页码 page_html_list = [] # 加首页 first_li = '<li><a href="{}?page=1">首页</a></li>'.format(self.base_url) page_html_list.append(first_li) # 加上一页 if self.current_page == 1: prev_li = '<li><a href="#">上一页</a></li>' else: prev_li = '<li><a href="{0}?page={1}">上一页</a></li>'.format(self.base_url, self.current_page - 1) page_html_list.append(prev_li) for i in range(show_start, show_end + 1): if i == self.current_page: li_tag = '<li class="active"><a href="{0}?page={1}">{1}</a></li>'.format(self.base_url, i) else: li_tag = '<li><a href="{0}?page={1}">{1}</a></li>'.format(self.base_url, i) page_html_list.append(li_tag) # 加下一页 if self.current_page == self.total_page: next_li = '<li><a href="#">下一页</a></li>' else: next_li = '<li><a href="{0}?page={1}">下一页</a></li>'.format(self.base_url, self.current_page + 1) page_html_list.append(next_li) # 加尾页 page_end_li = '<li><a href="{0}?page={1}">尾页</a></li>'.format(self.base_url, self.total_page) page_html_list.append(page_end_li) return "".join(page_html_list) 封装保存版封装保存版使用指南
def user_list(request): pager = Pagination(request.GET.get("page"), len(data), request.path_info) user_list = data[pager.start:pager.end] page_html = pager.page_html() return render(request, "user_list.html", {"user_list": user_list, "page_html": page_html})内置分页view部分
from django.shortcuts import render from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger L = [] for i in range(999): L.append(i) def index(request): current_page = request.GET.get('p') paginator = Paginator(L, 10) # per_page: 每页显示条目数量 # count: 数据总个数 # num_pages:总页数 # page_range:总页数的索引范围,如: (1,10),(1,200) # page: page对象 try: posts = paginator.page(current_page) # has_next 是否有下一页 # next_page_number 下一页页码 # has_previous 是否有上一页 # previous_page_number 上一页页码 # object_list 分页之后的数据列表 # number 当前页 # paginator paginator对象 except PageNotAnInteger: posts = paginator.page(1) except EmptyPage: posts = paginator.page(paginator.num_pages) return render(request, 'index.html', {'posts': posts}) 内置分页view部分内置分页HTML部分
<!DOCTYPE html> <html> <head lang="en"> <meta charset="UTF-8"> <title></title> </head> <body> <ul> {% for item in posts %} <li>{{ item }}</li> {% endfor %} </ul> <div class="pagination"> <span class="step-links"> {% if posts.has_previous %} <a href="?p={{ posts.previous_page_number }}">Previous</a> {% endif %} <span class="current"> Page {{ posts.number }} of {{ posts.paginator.num_pages }}. </span> {% if posts.has_next %} <a href="?p={{ posts.next_page_number }}">Next</a> {% endif %} </span> </div> </body> </html> 内置分页HTML部分
123
转载于:https://www.cnblogs.com/daofaziran/p/9430443.html
相关资源:各显卡算力对照表!