Django (二)
一. cookie
a. 简单应用
def login(request): if request.method=="GET": return render(request,"login.html") else: name = request.POST.get("name") password = request.POST.get("password") if name == "alex" and password == "123": obj = redirect("/classes/") obj.set_cookie("ticket","123456",max_age=10) return obj else: return redirect("/login/") def classes(request): sk = request.COOKIES print(sk) if not sk: return redirect("/login/")
b. 过期时间的两种格式
方式一: obj.set_cookie("ticket","123456",max_age=10) 方式二: import datetime from datetime import timedelta #时间的加减 ct = datetime.datetime.utcnow() #获取当前日期 v= timedelta(seconds=10) #10秒 value = ct + v obj.set_cookie("ticket","123456",expires=value)
c. 限制路径
obj.set_cookie("ticket","123456",max_age=10,path="/") #所有路径都可以访问 obj.set_cookie("ticket","123456",max_age=10,path="/class") #只有class访问
d. cookie签名
#加 obj.set_signed_cookie("ticket","123456",salt="abc") #解 sk = request.get_signed_cookie("ticket",salt="abc")
二. session
流程:客户登录网址,验证成功后,服务端生成一个随机字符串和随机字符串对应的键值,然后把随机字符串通过cookie发送给客户端 客户端拿着随机字符串通过cookir再次登陆,服务端拿着随机字符串和保存在本地的数据对应,以确定用户的登录状态
Cookie是什么?
保存在客户端浏览器上的键值对
Session是什么?
保存在服务端的数据(本质是键值对)
{
“aaaaa":{'id':1,'name':'于浩',email='xxxx'}
"bbbbb":{'id':2,'name':'陈涛',email='0000'}
}
应用:依赖cookie
作用:保持会话(Web网站)
好处:敏感信息不会直接给客户端
梳理:
1. 保存在服务端的数据(本质是键值对)
2. 配置文件中:
- 存储位置
- 超时时间、每次刷新更新时间
a. 简单示例
用户访问http://127.0.0.1:8000/login/
urlpatterns = [
url(r'^index/', views.index),
url(r'^login/', views.login),
]
def login(request):
if request.method == 'GET':
return render(request,'login.html')
else:
u = request.POST.get('user')
p = request.POST.get('pwd')
obj = models.UserAdmin.objects.filter(username=u,password=p).first()
if obj:
# 1. 生成随机字符串
# 2. 通过cookie发送给客户端
# 3. 服务端保存
# {
# 随机字符串1: {'username':'alex','email':x''...}
# }
request.session['username'] = obj.username
return redirect('/index/')
else:
return render(request,'login.html',{'msg':'用户名或密码错误'})
def index(request):
# 1. 获取客户端端cookie中的随机字符串
# 2. 去session中查找有没有随机字符
# 3. 去session对应key的value中查看是否有 username
v = request.session.get('username')
if v:
return HttpResponse('登录成功:%s' %v)
else:
return redirect('/login/')
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <form action="/login/" method="POST"> <input type="text" name="user"> <input type="text" name="pwd"> <input type="submit" value="提交">{{ msg }} </form> </body> </html>
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <h1>index page</h1> </body> </html>
三. cbv
cbv(class-base-view) 基于类的视图 fbv(func-base-view) 基于函数的视图
a. 基本演示
urlpatterns = [ url(r'^login.html$', views.Login.as_view()), ]
from django.views import View class Login(View): def dispatch(self, request, *args, **kwargs): print('before') obj = super(Login,self).dispatch(request,*args,**kwargs) print("after") return obj def get(self,request): return render(request,"login.html") def post(self,request): print(request.POST) return HttpResponse("Login.post")
四. 分页
a. django分页
#浏览器访问 http://127.0.0.1:8000/index.html/?page=9
urlpatterns = [ #django分页 url(r'^index', views.index), ]
#django 分页 from django.core.paginator import Paginator,Page,PageNotAnInteger,EmptyPage def index(request): current_page = request.GET.get("page") user_list = models.User_info.objects.all() paginator = Paginator(user_list,10) #每页显示10条 """ # count: 数据总个数 # num_pages:总页数 # page_range:总页数的索引范围,如: (1,10),(1,200) # page: page对象 """ try: posts = paginator.page(current_page) #当前页 except PageNotAnInteger as e: #http://127.0.0.1:8000/index.html/?page=qqq 处理这种异常 posts = paginator.page(1) except EmptyPage as e: #http://127.0.0.1:8000/index.html/?page=-10 捕获这种异常 posts = paginator.page(1) """ # has_next 是否有下一页 # next_page_number 下一页页码 # has_previous 是否有上一页 # previous_page_number 上一页页码 # object_list 分页之后的数据列表 # number 当前页 # paginator paginator对象 """ return render(request,"index.html",{"posts":posts})
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <h1>用户列表</h1> <ul> {% for row in posts.object_list %} <li>{{ row.name }}</li> {% endfor %} </ul> <div> {% if posts.has_previous %} <a href="/index.html/?page={{ posts.previous_page_number }}">上一页</a> {% endif %} {% for num in posts.paginator.page_range %} <a href="/index.html/?page={{ num }}">{{ num }}</a> {% endfor %} {% if posts.has_next %} <a href="/index.html/?page={{ posts.next_page_number }}">下一页</a> {% endif %} </div> </body> </html>
b. 自定义分页
#浏览器访问 http://127.0.0.1:8000/custom/?page=6
urlpatterns = [ #自定义分页 url(r'^custom/', views.custom), ]
from utils.pager import PageInfo #自定义分页 def custom(request): #总页数 all_count = models.User_info.objects.all().count() #用户当前想要访问的页码 current_page = request.GET.get("page") page_info = PageInfo(current_page,all_count,10,"/custom",11) user_list = models.User_info.objects.all()[page_info.start():page_info.end()] return render(request,"custom.html",{"user_list":user_list,"page_info":page_info})
class PageInfo(object): def __init__(self,current_page,all_count,per_page,base_url,show_page=11): #如果传值错误进入第一页 try: self.current_page = int(current_page) except Exception as e: self.current_page = 1 self.per_page = per_page #每页显示的个数 a,b = divmod(all_count,per_page) #页数 余数 if b: a = a + 1 self.all_page = a #总页码 self.show_page = show_page self.base_url = base_url def start(self): # 1 0: 10 # 2 10:20 # 3 20:30 return (self.current_page-1) * self.per_page def end(self): return self.current_page * self.per_page def pager(self): page_list = [] half = int((self.show_page-1)/2) if self.all_page < self.show_page: begin = 1 stop = self.all_page + 1 else: if self.current_page < half: begin = 1 stop = self.show_page + 1 else: if self.current_page + half > self.all_page: begin = self.all_page - 10 +1 stop = self.all_page + 1 else: begin = self.current_page - half stop = self.current_page + half +1 if self.current_page <=1: prev = "<li><a href='#'>上一页</a></li>" else: prev = "<li><a href='%s/?page=%s'>上一页</a></li>"%(self.base_url,self.current_page - 1) page_list.append(prev) for i in range(begin,stop): if i == self.current_page: temp = "<li class='active'><a href='/custom/?page=%s'>%s</a></li>"%(i,i) else: temp = "<li><a href='%s/?page=%s'>%s</a></li>"%(self.base_url,i,i) page_list.append(temp) if self.current_page >= self.all_page: nex = "<li><a href='#'>下一页</a></li>" else: nex = "<li><a href='%s/?page=%s'>下一页</a></li>" % (self.base_url,self.current_page + 1) page_list.append(nex) return "".join(page_list)
from django.db import models # Create your models here. class User_type(models.Model): uid = models.BigAutoField(primary_key=True) title = models.CharField(max_length=32) class User_info(models.Model): name = models.CharField(max_length=32) age = models.CharField(max_length=32) ut = models.ForeignKey("User_type")
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <link rel="stylesheet" href="/static/plugins/bootstrap-3.3.7-dist/css/bootstrap.css" /> </head> <body> {% for row in user_list %} <li>{{ row.name }}</li> {% endfor %} <nav aria-label="Page navigation"> <ul class="pagination"> {{ page_info.pager |safe }} </ul> </nav> </body> </html>
class PageInfo(object): def __init__(self,current_page,all_count,per_page,base_url,show_page=11): #如果传值错误进入第一页 try: self.current_page = int(current_page) except Exception as e: self.current_page = 1 self.per_page = per_page #每页显示的个数 a,b = divmod(all_count,per_page) #页数 余数 if b: a = a + 1 self.all_page = a #总页码 self.show_page = show_page self.base_url = base_url def start(self): # 1 0: 10 # 2 10:20 # 3 20:30 return (self.current_page-1) * self.per_page def end(self): return self.current_page * self.per_page def pager(self): page_list = [] half = int((self.show_page-1)/2) if self.all_page < self.show_page: begin = 1 stop = self.all_page + 1 else: if self.current_page < half: begin = 1 stop = self.show_page + 1 else: if self.current_page + half > self.all_page: begin = self.all_page - 10 +1 stop = self.all_page + 1 else: begin = self.current_page - half stop = self.current_page + half +1 if self.current_page <=1: prev = "<li><a href='#'>上一页</a></li>" else: prev = "<li><a href='%s?page=%s'>上一页</a></li>"%(self.base_url,self.current_page - 1) page_list.append(prev) for i in range(begin,stop): if i == self.current_page: temp = "<li class='active'><a href='%s?page=%s'>%s</a></li>"%(self.base_url,i,i) else: temp = "<li><a href='%s?page=%s'>%s</a></li>"%(self.base_url,i,i) page_list.append(temp) if self.current_page >= self.all_page: nex = "<li><a href='#'>下一页</a></li>" else: nex = "<li><a href='%s?page=%s'>下一页</a></li>" % (self.base_url,self.current_page + 1) page_list.append(nex) return "".join(page_list)
五. XSS
对用户提交的数据验证 1.以安全的字符串显示出来,没有样式 2.加safe,以安全的方式显示,过滤关键字
六. CSRF
CSRF(Cross-site request forgery)跨站请求伪造,通常缩写为CSRF或者XSRF,是一种对网站的恶意利用。尽管听起来像跨站脚本(XSS) 但它与XSS非常不同,XSS利用站点内的信任用户,而CSRF则通过伪装来自受信任用户的请求来利用受信任的网站。 自己理解:csrf_token防止从别的网站向自己网站发post请求, 客户来访问网站,网站会向客户发送随机字符串,然后客户带随机字符串发送post请求 只有带随机字符串来,网站才认,一般是post请求才要求带随机字符串,其它网站第一次来不会带随机字符串。
a. django开启csrf
MIDDLEWARE = [ 'django.middleware.csrf.CsrfViewMiddleware', ]
b.HTML中使用
{% csrf_token %} #只要写上{% csrf_token %} 会有一个隐藏的input随机字符串,在cookie也有一个随机的字符串,form表单提交数据时,一般会使用 {{ csrf_token }} #生成随机的字符串
c.django中设置防跨站请求伪造功能有分为全局和局部
#局部 from django.views.decorators.csrf import csrf_exempt,csrf_protect @csrf_protect settings中没有设置全局中间件,为当前函数强制设置防跨站请求伪造功能。 @csrf_exempt settings中设置了全局中间件,取消当前函数防跨站请求伪造功能。
#fbv @csrf_protect def func(object): pass #cbv from django.views import View from django.utils.decorators import method_decorator @method_decorator(csrf_exempt,name="dispatch") class foo(View) pass
#方式一 类上加装饰器: def wrapper(func): def inner(*args,**kwargs): return func(*args,**kwargs) return inner @method_decorator(wrapper,name="get") @method_decorator(wrapper,name="post") class foo(View): def get(self,request): pass def post(self,request): pass #方式二 类上“dispatch”加装饰器: def wrapper(func): def inner(*args,**kwargs): return func(*args,**kwargs) return inner @method_decorator(wrapper,name="dispatch") class foo(View): def dispatch(self,request,*args,**kwargs): return xxx def get(self,request): pass def post(self,request): pass #方式三 方法上加装饰器: def wrapper(func): def inner(*args,**kwargs): return func(*args,**kwargs) return inner class foo(View): @method_decorator(wrapper) def get(self,request): pass def post(self,request): pass
d. Ajax提交数据 携带CSRF
1. 通过获取隐藏的input标签中的字符串 放置在data中发送 js代码可以和html在不同文件中(推荐使用)
{% csrf_token %} $.ajax({ data:{"csrfmiddlewaretoken":$("[name='csrfmiddlewaretoken']").val() });
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <form method="POST" action="/csrf1.html"> {% csrf_token %} <input id="user" type="text" name="user"/> <a onclick="submitForm();">Ajax提交</a> </form> <script src="/static/jquery-1.12.4.js"></script> <script> function submitForm() { var csrf = $('input[name="csrfmiddlewaretoken"]').val(); var user = $("#user").val(); $.ajax({ url:'/csrf1.html', type:'POST', data:{"user":user,"csrfmiddlewaretoken":csrf}, success:function (arg) { console.log(arg); } }) } </script> </body> </html>
urlpatterns = [ url(r'^csrf1.html', views.csrf1), ]
def csrf1(request): if request.method == 'GET': return render(request,'csrf1.html') else: return HttpResponse('ok')
2. 通过获取返回的cookie中的字符串 放置在请求头中发送
通过在console中 document.cookie可以获取csrftoken=JPv1gIdrBiAlK2RCrgFs0OKwsncPXvwPfMhEWIVzMdMFymIayiuGu2GkBAu57moL 但需要切割字符串,通过引入jquery.cookie.js对cookie操作,使用$.cookie("csrftoken") <script src="/static/jquery.cookie.js"></script> $.ajax({ headers:{"X-CSRFToken":$.cookie('csrftoken')}, })
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <form method="POST" action="/csrf1.html"> {% csrf_token %} <input id="user" type="text" name="user"/> <a onclick="submitForm();">Ajax提交</a> </form> <script src="/static/jquery-1.12.4.js"></script> <script src="/static/jquery.cookie.js"></script> <script> function submitForm() { var token = $.cookie("csrftoken"); var user = $("#user").val(); $.ajax({ url:'/csrf1.html', type:'POST', headers:{"X-CSRFToken":token}, data:{"user":user}, success:function (arg) { console.log(arg); } }) } </script> </body> </html>
3. 直接在ajax里发送
模版页面第一次render渲染时生成,js代码需要和html在一个文件中。如果js代码在其它文件中,是不会渲染的 $.ajaxSetup({ data: {csrfmiddlewaretoken: '{{ csrf_token }}' }, });
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <form method="POST" action="/csrf1.html"> <input id="user" type="text" name="user"/> <a onclick="submitForm();">Ajax提交</a> </form> <script src="/static/jquery-3.2.1.js"></script> <script> function submitForm() { var user = $("#user").val(); $.ajax({ url:'/index/', type:'POST', data:{"user":user,"csrfmiddlewaretoken":"{{ csrf_token }}"}, success:function (arg) { console.log(arg); } }) } </script> </body> </html>
七. 中间件
#django 中的中间件(middleware),在django中,中间件其实就是一个类,在请求到来和结束后,django会根据自己的规则在合适的时机执行中间件中相应的方法。
a. 注册中间件
在django项目的settings模块中,有一个 MIDDLEWARE_CLASSES 变量,其中每一个元素就是一个中间件,如下图。 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', "md.M1", "md.M2", ]