Django  分页 、cookie、Session、CSRF

一、分页

分页功能在每个网站都是必要的,下面主要介绍两种分页方式:

1、Django内置分页

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})

views.py

<!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

from django.shortcuts import render
from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger class CustomPaginator(Paginator):
def __init__(self, current_page, max_pager_num, *args, **kwargs):
"""
:param current_page: 当前页
:param max_pager_num:最多显示的页码个数
:param args:
:param kwargs:
:return:
"""
self.current_page = int(current_page)
self.max_pager_num = max_pager_num
super(CustomPaginator, self).__init__(*args, **kwargs) def page_num_range(self):
# 当前页面
# self.current_page
# 总页数
# self.num_pages
# 最多显示的页码个数
# self.max_pager_num
print(1)
if self.num_pages < self.max_pager_num:
return range(1, self.num_pages + 1)
print(2)
part = int(self.max_pager_num / 2)
if self.current_page - part < 1:
return range(1, self.max_pager_num + 1)
print(3)
if self.current_page + part > self.num_pages:
return range(self.num_pages + 1 - self.max_pager_num, self.num_pages + 1)
print(4)
return range(self.current_page - part, self.current_page + part + 1) L = []
for i in range(999):
L.append(i) def index(request):
current_page = request.GET.get('p')
paginator = CustomPaginator(current_page, 11, 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})

扩展内置分页:views.py

<!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 %} {% for i in posts.paginator.page_num_range %}
<a href="?p={{ i }}">{{ i }}</a>
{% endfor %} {% if posts.has_next %}
<a href="?p={{ posts.next_page_number }}">Next</a>
{% endif %}
</span> <span class="current">
Page {{ posts.number }} of {{ posts.paginator.num_pages }}.
</span> </div>
</body>
</html>

扩展内置分页:Html

2、自定义分页

需求:

1、设定每页显示数据条数

2、用户输入页码(第一页、第二页...)

3、设定显示多少页号

4、获取当前数据总条数

5、根据设定显示多少页号和数据总条数计算出,总页数

6、根据设定的每页显示条数和当前页码,计算出需要取数据表的起始位置

7、在数据表中根据起始位置取值,页面上输出数据

8、输出分页html,如:[上一页][1][2][3][4][5][下一页]

2.1、templates下的模板:

<li>{{ item }}</li>

li.html

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title></title>
<style>
.pagination .page{
display: inline-block;
padding: 5px;
background-color: cyan;
margin: 5px;
}
.pagination .page.active{
background-color: brown;
color: white;
}
</style>
</head>
<body>
<ul>
{% for item in li %}
{% include 'li.html' %}
{% endfor %}
</ul> <div class="pagination">
{{ page_str }}
</div>
</body>
</html>

user_list.html

2.2、新增utils目录,创建分页工具pagination.py文件。

from django.utils.safestring import mark_safe  # 导入mark_safe函数

class Page:
def __init__(self, current_page, data_count, per_page_count=10, pager_num=7):
self.current_page = current_page # 当前页
self.data_count = data_count # 总页数
self.per_page_count = per_page_count # 每页显示数据
self.pager_num = pager_num # 前端展示的分页数 @property # 转换成静态属性
def start(self):
"""
定义每页展示数据的开始下标
:return:
"""
return (self.current_page - 1) * self.per_page_count @property
def end(self):
"""
定义每页展示数据的结束下标
:return:
"""
return self.current_page * self.per_page_count @property
def total_count(self):
"""
定义总页数
:return:
"""
v, y = divmod(self.data_count, self.per_page_count) # 内置函数divmod 能够整除返回True,不能整除返回False
if y:
v += 1
return v def page_str(self, base_url):
"""
定义分页规则
:param base_url:
:return:
"""
page_list = [] if self.total_count < self.pager_num:
start_index = 1
end_index = self.total_count + 1
else:
if self.current_page <= (self.pager_num + 1) / 2:
start_index = 1
end_index = self.pager_num + 1
else:
start_index = self.current_page - (self.pager_num - 1) / 2
end_index = self.current_page + (self.pager_num + 1) / 2
if (self.current_page + (self.pager_num - 1) / 2) > self.total_count:
end_index = self.total_count + 1
start_index = self.total_count - self.pager_num + 1 if self.current_page == 1:
prev = '<a class="page" href="javascript:void(0);">上一页</a>'
else:
prev = '<a class="page" href="%s?p=%s">上一页</a>' % (base_url, self.current_page - 1,)
page_list.append(prev) for i in range(int(start_index), int(end_index)):
if i == self.current_page:
temp = '<a class="page active" href="%s?p=%s">%s</a>' % (base_url, i, i)
else:
temp = '<a class="page" href="%s?p=%s">%s</a>' % (base_url, i, i)
page_list.append(temp) if self.current_page == self.total_count:
nex = '<a class="page" href="javascript:void(0);">下一页</a>'
else:
nex = '<a class="page" href="%s?p=%s">下一页</a>' % (base_url, self.current_page + 1,)
page_list.append(nex) jump = """
<input type='text' /><a onclick='jumpTo(this, "%s?p=");'>GO</a>
<script>
function jumpTo(ths,base){
var val = ths.previousSibling.value;
location.href = base + val;
}
</script>
""" % (base_url,) page_list.append(jump)
# django从view向template传递HTML字符串的时候,django默认不渲染此HTML,原因是为了防止这段字符串里面有恶意攻击的代码(XSS)
# mark_safe这个函数就是确认这段HTML字符串是安全的,Django会才会渲染此HTML
page_str = mark_safe("".join(page_list)) return page_str

pagination.py

2.3、app下的views.py:

from django.shortcuts import render
from utils import pagination LIST = []
for i in range(500):
LIST.append(i) def user_list(request):
current_page = request.GET.get('p', 1)
current_page = int(current_page) # val = request.COOKIES.get('per_page_count', 10)
# val = int(val)
page_obj = pagination.Page(current_page, len(LIST)) data = LIST[page_obj.start:page_obj.end] page_str = page_obj.page_str("/app1/user_list/") return render(request, 'user_list.html', {'li': data, 'page_str': page_str})

views.py

二、Cookie

Cookie 是在 HTTP 协议下,服务器或脚本可以维护客户工作站上信息的一种方式。Cookie 是由 Web 服务器保存在用户浏览器(客户端)上的小文本文件,它可以包含有关用户的信息。无论何时用户链接到服务器,Web 站点都可以访问 Cookie 信息,因此用户不用每次登陆都需要输入账号密码。

1、获取Cookie:

request.COOKIES['key']
request.get_signed_cookie(key, default=RAISE_ERROR, salt='', max_age=None)
"""
  参数:
default: 默认值
salt: 加密盐
max_age: 后台控制过期时间
"""

2、设置Cookie:

rep = HttpResponse(...) 或 rep = render(request, ...) #创建对象

rep.set_cookie(key,value,...) #明文Cookie设置方式
rep.set_signed_cookie(key,value,salt='加密盐',...) #加密加盐Cookie设置方式
"""
参数:
key, 键
value='', 值
max_age=None, 超时时间
expires=None, 超时时间(IE requires expires, so set it if hasn't been already.)
max_age与expires的区别:max_age用秒设置超时时间,N秒后失效,expires用datetime设置超时时间,到截至时间后失效
path='/', Cookie生效的路径,/ 表示根路径,特殊的:根路径的cookie可以被任何url的页面访问
domain=None, Cookie生效的域名
secure=False, https传输
httponly=False 只能http协议传输,当httponly=True时,无法被JavaScript获取(不是绝对,底层抓包可以获取到也可以被覆盖)
"""

3、前端页面操作Cookie

由于Cookie保存在客户端的电脑上,所以,JavaScript和jquery也可以操作Cookie。

JavaScript操作Cookie:
#获取cookie并赋值给变量
var strCookie=document.cookie
#修改cookie中的id值
document.cookie="id=88";
#设置终止时间
###获取当前时间
var date=new Date();
var expireDays=10;
###将date设置为10天以后的时间
date.setTime(date.getTime()+expireDays*60*1000);
###将id和name两个cookie设置为10天后过期
document.cookie="id=77; name=bill; expires="+date.toUTCString(); Jquery操作Cookie:
相对于JS操作cookie,Jquery操作Cookie就简单很多
<script src='/static/js/jquery.cookie.js'></script> #导入jquery.cookie.js文件,此文件在官网下载
$.cookie("list_pager_num", 30,{ path: '/' });

4、根据cookie值显示定制条数示例:

在上面自定义分页示例的基础上,根据cookie值进行定制条数数据显示。

4.1、templates下的模板:

<li>{{ item }}</li>

li.html

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title></title>
<style>
.pagination .page{
display: inline-block;
padding: 5px;
background-color: cyan;
margin: 5px;
}
.pagination .page.active{
background-color: brown;
color: white;
}
</style>
</head>
<body>
<ul>
{% for item in li %}
{% include 'li.html' %}
{% endfor %}
</ul> <div>
<select id="ps" onchange="changePageSize(this)">
<option value="10">10</option>
<option value="30">30</option>
<option value="50">50</option>
<option value="100">100</option>
</select>
</div> <div class="pagination">
{{ page_str }}
</div>
<script src="/static/jquery-1.12.4.js"></script>
<script src="/static/jquery.cookie.js"></script>
<script>
//将选择框的值设置为cookie,在Views中就可以接收cookie
function changePageSize(ths){
var v = $(ths).val();
$.cookie('per_page_count',v);
location.reload(); //页面重新加载
}
//将多选框的值更改为最新的值
$(function(){
var v = $.cookie('per_page_count');
$('#ps').val(v);
}); </script>
</body>
</html>

user_list.html

4.2、utils目录下pagination.py文件

from django.utils.safestring import mark_safe  # 导入mark_safe函数

class Page:
def __init__(self, current_page, data_count, per_page_count=10, pager_num=7):
self.current_page = current_page # 当前页
self.data_count = data_count # 总页数
self.per_page_count = per_page_count # 每页显示数据
self.pager_num = pager_num # 前端展示的分页数 @property # 转换成静态属性
def start(self):
"""
定义每页展示数据的开始下标
:return:
"""
return (self.current_page - 1) * self.per_page_count @property
def end(self):
"""
定义每页展示数据的结束下标
:return:
"""
return self.current_page * self.per_page_count @property
def total_count(self):
"""
定义总页数
:return:
"""
v, y = divmod(self.data_count, self.per_page_count) # 内置函数divmod 能够整除返回True,不能整除返回False
if y:
v += 1
return v def page_str(self, base_url):
"""
定义分页规则
:param base_url:
:return:
"""
page_list = [] if self.total_count < self.pager_num:
start_index = 1
end_index = self.total_count + 1
else:
if self.current_page <= (self.pager_num + 1) / 2:
start_index = 1
end_index = self.pager_num + 1
else:
start_index = self.current_page - (self.pager_num - 1) / 2
end_index = self.current_page + (self.pager_num + 1) / 2
if (self.current_page + (self.pager_num - 1) / 2) > self.total_count:
end_index = self.total_count + 1
start_index = self.total_count - self.pager_num + 1 if self.current_page == 1:
prev = '<a class="page" href="javascript:void(0);">上一页</a>'
else:
prev = '<a class="page" href="%s?p=%s">上一页</a>' % (base_url, self.current_page - 1,)
page_list.append(prev) for i in range(int(start_index), int(end_index)):
if i == self.current_page:
temp = '<a class="page active" href="%s?p=%s">%s</a>' % (base_url, i, i)
else:
temp = '<a class="page" href="%s?p=%s">%s</a>' % (base_url, i, i)
page_list.append(temp) if self.current_page == self.total_count:
nex = '<a class="page" href="javascript:void(0);">下一页</a>'
else:
nex = '<a class="page" href="%s?p=%s">下一页</a>' % (base_url, self.current_page + 1,)
page_list.append(nex) jump = """
<input type='text' /><a onclick='jumpTo(this, "%s?p=");'>GO</a>
<script>
function jumpTo(ths,base){
var val = ths.previousSibling.value;
location.href = base + val;
}
</script>
""" % (base_url,) page_list.append(jump)
# django从view向template传递HTML字符串的时候,django默认不渲染此HTML,原因是为了防止这段字符串里面有恶意攻击的代码(XSS)
# mark_safe这个函数就是确认这段HTML字符串是安全的,Django会才会渲染此HTML
page_str = mark_safe("".join(page_list)) return page_str

pagination.py

4.3、app下的views.py:

def cookie(request):
#
# request.COOKIES
# request.COOKIES['username111']
request.COOKIES.get('username111') response = render(request,'index.html')
response = redirect('/index/')
# 设置cookie,关闭浏览器失效
response.set_cookie('key',"value")
# 设置cookie, N秒只有失效
response.set_cookie('username111',"value",max_age=10)
# 设置cookie, 截止时间失效
import datetime
current_date = datetime.datetime.utcnow()
current_date = current_date + datetime.timedelta(seconds=5)
response.set_cookie('username111',"value",expires=current_date)
response.set_cookie('username111',"value",max_age=10) # request.COOKIES.get('...')
# response.set_cookie(...)
obj = HttpResponse('s') obj.set_signed_cookie('username',"kangbazi",salt="asdfasdf")
request.get_signed_cookie('username',salt="asdfasdf") return response

views.py

5、cookie模拟登陆示例:

5.1、templates下模板:

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title></title>
</head>
<body>
<form action="/login/" method="POST">
<input type="text" name="username" placeholder="用户名" />
<input type="password" name="pwd" placeholder="密码" />
<input type="submit" />
</form>
</body>
</html>

login.html

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title></title>
</head>
<body>
<h1>欢迎登录:{{ current_user }}</h1>
</body>
</html>

home

5.2、views.py:

user_info = {
'dachengzi': {'pwd': ""},
'kanbazi': {'pwd': "kkkkkkk"},
}
def login(request):
if request.method == "GET":
return render(request,'login.html')
if request.method == "POST":
u = request.POST.get('username')
p = request.POST.get('pwd')
dic = user_info.get(u)
if not dic:
return render(request,'login.html')
if dic['pwd'] == p:
res = redirect('/home/')
# res.set_cookie('username111',u,max_age=10) #设置cookie N秒后失效
# import datetime
# current_date = datetime.datetime.utcnow()
# current_date = current_date + datetime.timedelta(seconds=5)
# res.set_cookie('username111',u,expires=current_date) #设置cookie 到截至时间后失效
res.set_cookie('username111',u)
res.set_cookie('user_type',"asdfjalskdjf",httponly=True) # 只能http协议传输,当httponly=True时,无法被JavaScript获取(不是绝对,底层抓包可以获取到也可以被覆盖)
return res
else:
return render(request,'login.html')

views.py

6、CBV和FBV用户认证装饰器

6.1、FBV (Function Base VIew)在view(视图)中装饰器编写逻辑:

def auth(func):
def inner(reqeust,*args,**kwargs):
v = reqeust.COOKIES.get('username111')
if not v:
return redirect('/login/')
return func(reqeust, *args,**kwargs)
return inner @auth
def index(reqeust):
# 获取当前已经登录的用户
v = reqeust.COOKIES.get('username111')
return render(reqeust,'index.html',{'current_user': v})

6.2、CBV(Class Base Viev)在view(视图)中装饰器编写逻辑:

from django import views
from django.utils.decorators import method_decorator #导入method_decorator函数,此函数时Django工具包中用于视图中类的装饰器函数 def auth(func):
def inner(reqeust,*args,**kwargs):
v = reqeust.COOKIES.get('username111')
if not v:
return redirect('/login/')
return func(reqeust, *args,**kwargs)
return inner @method_decorator(auth,name='dispatch') #第三种装饰方法,在类上添加上装饰器
class Order(views.View): # @method_decorator(auth) #第二种装饰方法,在dispatch函数上添加装饰器,因为dispatch函数会在类中所有函数执行前先行执行一遍
# def dispatch(self, request, *args, **kwargs):
# return super(Order,self).dispatch(request, *args, **kwargs) # @method_decorator(auth) #第一种装饰方法,在需要装饰的上添加装饰器
def get(self,reqeust):
v = reqeust.COOKIES.get('username111')
return render(reqeust,'index.html',{'current_user': v}) def post(self,reqeust):
v = reqeust.COOKIES.get('username111')
return render(reqeust,'index.html',{'current_user': v})

  

三、session

Session存储在服务器端,一般放置在服务器的内存中(为了高速存取),Sessinon在用户访问第一次访问服务器时创建,需要注意只有访问JSP、Servlet等程序时才会创建Session,只访问HTML、IMAGE等静态资源并不会创建Session,可调用request.getSession(true)强制生成Session。

Django中默认支持Session,其内部提供了5种类型的Session供开发者使用:

  • 数据库(默认)
  • 缓存
  • 文件
  • 缓存+数据库
  • 加密cookie
 Django默认支持Session,并且默认是将Session数据存储在数据库中,即:django_session 表中。

 a. 配置 settings.py

     SESSION_ENGINE = 'django.contrib.sessions.backends.db'   # 引擎(默认)

     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,默认修改之后才保存(默认) b. 使用 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'] # 所有 键、值、键值对
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("session_key") request.session.set_expiry(value)
* 如果value是个整数,session会在些秒数后失效。
* 如果value是个datatime或timedelta,session就会在这个时间后失效。
* 如果value是0,用户关闭浏览器session就会失效。
* 如果value是None,session会依赖全局session失效策略。

数据库Session

 a. 配置 settings.py

     SESSION_ENGINE = 'django.contrib.sessions.backends.cache'  # 引擎
SESSION_CACHE_ALIAS = 'default' # 使用的缓存别名(默认内存缓存,也可以是memcache),此处别名依赖缓存的设置 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,默认修改之后才保存 b. 使用 同上

缓存Session

 a. 配置 settings.py

     SESSION_ENGINE = 'django.contrib.sessions.backends.file'    # 引擎
SESSION_FILE_PATH = None # 缓存文件路径,如果为None,则使用tempfile模块获取一个临时地址tempfile.gettempdir() # 如:/var/folders/d3/j9tj0gz93dg06bmwxmhh6_xm0000gn/T 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,默认修改之后才保存 b. 使用 同上

文件Session

 数据库用于做持久化,缓存用于提高效率

 a. 配置 settings.py

     SESSION_ENGINE = 'django.contrib.sessions.backends.cached_db'        # 引擎

 b. 使用

     同上

缓存+数据库Session

 a. 配置 settings.py

     SESSION_ENGINE = 'django.contrib.sessions.backends.signed_cookies'   # 引擎

 b. 使用

     同上

加密cookie Session

更多session参考:参考一参考二

1、session免密登录+10秒超时示例:

1.1、templates 下的模板文件:

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title></title>
</head>
<body>
<form action="/login/" method="POST">
<input type="text" name="user" />
<input type="text" name="pwd" />
<input type="checkbox" name="rmb" value="" /> 10秒免登录
<input type="submit" value="提交" />
<input id="btn1" type="button" value="按钮" />
<input id="btn2" type="button" value="按钮" />
</form> <script src="/static/jquery-1.12.4.js"></script>
<script src="/static/jquery.cookie.js"></script>
</body>
</html>

login.html

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title></title>
</head>
<body>
<h1>欢迎登录:{{ username }}, {{ request.session.username }}</h1>
<a href="/logout/">注销</a>
</body>
</html>

index.html

1.2、views.py:

from django.shortcuts import render,redirect,HttpResponse

def login(request):
if request.method == "GET":
return render(request,'login.html')
elif request.method == "POST":
user = request.POST.get('user')
pwd = request.POST.get('pwd')
if user == 'root' and pwd == "":
# session中设置值 Django自动产生4步操作(1、生成随机字符串 2、写到用户浏览器cookie 3、保存在session中 4、在随机字符串对应字典中设置相应值)
request.session['username'] = user
request.session['is_login'] = True
if request.POST.get('rmb', None) == '':
# 超时时间
request.session.set_expiry(10)
return redirect('/index/')
else:
return render(request,'login.html') def index(request):
# session中获取值
if request.session.get('is_login', None):
# {'username': request.session['username']}这个字典数据其实可以不用传递,因为request本身就包含了session值,
# 在HTML中可以直接通过request.session.username获取用户名
return render(request, 'index.html', {'username': request.session['username']})
else:
return HttpResponse('gun') # 注销
def logout(request):
# del request.session['username'] # 删除一个指定session用户
request.session.clear() # 清楚全部session用户
return redirect('/login/')

views.py

注、在运行Django工程示例前需要运行如下命令,生成表结构,session会默认保存在db.sqlite3数据库文件中。

python3 manage.py  makemigrations #相当于在该app的migrations目录,记录下该app下modes.py所有表结构类型的改动(普通增删改查不记录)
python3 manage.py migrate #将刚刚对于表结构的改动作用至数据库

四、CSRF

CSRF(Cross-site request forgery)跨站请求伪造,也被称为“One Click Attack”或者Session Riding,通常缩写为CSRF或者XSRF,是一种对网站的恶意利用。尽管听起来像跨站脚本(XSS),但它与XSS非常不同,XSS利用站点内的信任用户,而CSRF则通过伪装来自受信任用户的请求来利用受信任的网站。与XSS攻击相比,CSRF攻击往往不大流行(因此对其进行防范的资源也相当稀少)和难以防范,所以被认为比XSS更具危险性。

之前的Django博文,都是基于在settings.py文件中,注释掉CSRF中间件进行的。

MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
#'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
]

注:现在开始之后的博文都是去掉CSRF中间件注释进行演示。

django中设置防跨站请求伪造功能有分为全局和局部:

全局:
  中间件 django.middleware.csrf.CsrfViewMiddleware 局部:
  from django.views.decorators.csrf import csrf_exempt,csrf_protect
  @csrf_protect,为当前函数强制设置防跨站请求伪造功能,即便settings中没有设置全局中间件。
  @csrf_exempt,取消当前函数防跨站请求伪造功能,即便settings中设置了全局中间件。

1、生成csrf_token:

在Django中可以通过{% csrf_token %} 生成csrftoken来防范CSRF攻击,但是要在后续的request请求中需要带上csrftoken参数才能通过CSRF中间件防御机制,进入view视图中进行处理。

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title></title>
</head>
<body>
<form action="/login/" method="POST">
<!--在Django中可通过%配合csrf_token字段可生成csrf_token字符串,会在form表单中生成一个隐藏的input框如下:
<input type="hidden" name="csrfmiddlewaretoken" value="6CAO0tE8oqIsehJFR9VojmS307XRfK91GkxRHutKvwj5nSQgI6KDZZL4hLkAorNo">
还会在cookie中生成{csrftoken:gQgbSGTiXtnqTeKD0jRbq6ZGsrdiWiJZQydezHIU4zY32PReRgGq6JSHJ5A15Znm}-->
{% csrf_token %}
<input type="text" name="user" />
<input type="text" name="pwd" />
<input type="checkbox" name="rmb" value="1" /> 10秒免登录
<input type="submit" value="提交" />
</form>
</script>
</body>
</html>

生成csrftoken

2、csrf_token 提交给后台的三种方式:

 <!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title></title>
</head>
<body>
<form action="/login/" method="POST">
<!--在Django中可通过%配合csrf_token字段可生成csrf_token字符串,会在form表单中生成一个隐藏的input框如下:
<input type="hidden" name="csrfmiddlewaretoken" value="6CAO0tE8oqIsehJFR9VojmS307XRfK91GkxRHutKvwj5nSQgI6KDZZL4hLkAorNo">
还会在cookie中生成{csrftoken:gQgbSGTiXtnqTeKD0jRbq6ZGsrdiWiJZQydezHIU4zY32PReRgGq6JSHJ5A15Znm}-->
{% csrf_token %}
<input type="text" name="user" />
<input type="text" name="pwd" />
<input type="checkbox" name="rmb" value="1" /> 10秒免登录
<input type="submit" value="提交" /> <!-- 第一种提交csrf_token的方式 通过from表单的形式-->
<input id="btn1" type="button" value="按钮" />
<input id="btn2" type="button" value="按钮" />
</form> <script src="/static/jquery-1.12.4.js"></script>
<script src="/static/jquery.cookie.js"></script>
<script>
// 第二种提交csrf_token的方式 在每个ajax请求之前,将cookie中的csrftoken值添加至请求头中,ajaxSetup类似于unittest框架中的setup,在每个case运行之前运行一遍
$(function(){
$.ajaxSetup({
beforeSend: function(xhr,settings){ // xhr 是XMLHttpRequest()对象,settings是Django的配置文件,settings.py每次运行都会将settings加载在内存之中 xhr.setRequestHeader('X-CSRFtoken', $.cookie('csrftoken'));
}
}); // <!-- 第三种提交csrf_token的方式 通过ajax形式将cookie中的csrftoken值添加至请求头中-->
// <!-- 但是第三种提交方式不好的地方就是需要不断的添加ajax请求,有一个提交按钮,就要新增一个ajax请求-->
$('#btn1').click(function () {
$.ajax({
url: '/login/',
type:"GET",
data: {'user': 'root', 'pwd': '123'},
// headers: {'X-CSRFtoken': $.cookie('csrftoken')}, //X-CSRFtoken 这key是固定的不能改变
success:function(arg){ }
})
});
})
</script>
</body>
</html>

三种方式

注:解释 ajax 将csrftoken值添加至请求头中,为什么对应的key是 X-CSRFtoken:

from django.conf import settings
print(settings.CSRF_HEADER_NAME) #输出打印结果 HTTP_X_CSRFTOKEN

通过打印结果发现值是 HTTP_X_CSRFTOKEN,这个就是csrftoken请求头对应的key,HTTP是因为Django会默认将收到的每个请求都会加上HTTP的缘故。因此对应的真实key是X_CSRFTOKEN,但是如果真的就把请求头中的key写成X_CSRFTOKEN的话,后台是接收不到的,因为无法识别下划线_,因此需要将X_CSRFTOKEN写成X-CSRFTOKEN。(X-CSRFTOKEN这个作为key是可以请求的,但是为了命名规范会写成X-CSRFtoken)

3、根据请求方式决定是否提交csrftoken值

<!DOCTYPE html>
<html>
<head lang="en">
<meta charset="UTF-8">
<title></title>
</head>
<body>
{% csrf_token %} <input type="button" onclick="Do();" value="Do it"/> <script src="/static/plugin/jquery/jquery-1.8.0.js"></script>
<script src="/static/plugin/jquery/jquery.cookie.js"></script>
<script type="text/javascript">
var csrftoken = $.cookie('csrftoken'); function csrfSafeMethod(method) {
// these HTTP methods do not require CSRF protection
return (/^(GET|HEAD|OPTIONS|TRACE)$/.test(method));
}
$.ajaxSetup({
beforeSend: function(xhr, settings) {
if (!csrfSafeMethod(settings.type) && !this.crossDomain) {
xhr.setRequestHeader("X-CSRFToken", csrftoken);
}
}
});
function Do(){ $.ajax({
url:"/app01/test/",
data:{id:1},
type:'POST',
success:function(data){
console.log(data);
}
}); }
</script>
</body>
</html>

更多CSRF参考:点击

最新文章

  1. VXLAN 概念(Part II)- 每天5分钟玩转 OpenStack(109)
  2. HTTP 协议详解
  3. 超高性能的json序列化之MVC中使用Json.Net
  4. iPad应用开发者的建议
  5. Hibernate不调用update却自动更新
  6. JAVA字符串转日期或日期转字
  7. 通达OA 同步中控考勤机 增强版
  8. Linux iptables重启后失效
  9. css 所有选择器
  10. 获取json对象长度的问题
  11. JS 计算日期天数差
  12. eclipse导入包的快捷键
  13. jquery 根据网站url给导航nav添加active效果
  14. 《Android内核剖析》读书笔记 第13章 View工作原理【View重绘过程】
  15. 【转】vector中erase()的使用注意事项
  16. Shiro中Realm
  17. IE9 添加事件DOMContentLoaded,方法addEventListener
  18. ethjs-1-了解
  19. 进阶之路(基础篇) - 021 arduino基础知识
  20. linux上定时运行scrapy

热门文章

  1. leetcode 解题报告 Word Ladder II
  2. linux归档压缩命令
  3. PHP-php-fpm配置优化
  4. &lt;转&gt;程序员的心理疾病
  5. ibatis 动态查询
  6. android之ScrollView里嵌套ListView(转)
  7. HDU 1009:FatMouse&amp;#39; Trade(简单贪心)
  8. 牛散NO.2:MACD西施说风情,柳下惠高位勿迷情
  9. Git使用笔记2
  10. Redis源码之String操作