cookie
浏览器客户端的一个文件,
通常是以字典的形式存放的,
通常我们登录网站后会显示当前登录的用户名,实现的例子的views:
user_info = {
"alex":{"pwd":"123"},
"jack":{"pwd":"456"}
}
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("/index/")
res.set_cookie("user",u)
return res
else:
return render(request,"login.html")
def index(request):
#获取当前登录的用户名
v = request.COOKIES.get("user")
if not v:
return render(request,"login.html")
return render(request,"index.html",{"v":v})
index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>当前用户{{ v }}</h1>
</body>
</html>
login.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<form action="/login/" method="POST" enctype="multipart/form-data">
<p>
<input type="text" name="username" placeholder="用户名" />
</p>
<p>
<input type="password" name="pwd" placeholder="密码" />
</p>
<p>
<input type="submit" value="提交" />
</p>
</form>
</body>
</html>
这种情况下用户在没有登录的时候是无法访问index页面的,用户登录成功后就能显示初恋当前的用户了,也就实现了用户的认证
设置Cookie:
res.set_cookie(key,value)
参数:
key 键
value=‘’ 值
max_age=None 超时时间,以秒作为单位。默认是关闭浏览器失效
expires=None 超时时间,这个是可以设置datatime
path="/" Cookie生效的路径
domain=None Cookie生效的域名
secure=False https传输
httponly=False 只能http协议传输,无法被JavaScript获取
salt=“jiami” 通过salt这个参数实现加密,同样的获取cookie的时候也需要加上salt参数才能进行解密
基于cookie的实现分页定制显示每页数据条数
user_list.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<style>
.q .page{
display: inline-block;
background-color: aqua;
margin: 5px;
padding: 5px;
}
.q .page.active{
background-color: red;
color: white;
}
.q #i1{
padding-left: 5px;
color: red;
}
</style>
</head>
<body>
<ul>
{% for item in list %}
{% include "tag.html" %}
{% endfor %}
</ul>
<div>
<select id="ll" οnchange="changePageSize(this)">
<option value="5">5</option>
<option value="10">10</option>
<option value="20">20</option>
<option value="50">50</option>
<option value="100">100</option>
</select>
</div>
<div class="q">
{{ temp|safe }}
<input type="text" />
<a οnclick="Go(this,'/user_list/?p=');" id="i1">GO</a>
</div>
<script src="/static/jquery-1.12.4.js"></script>
<script src="/static/jquery.cookie.js"></script>
<script>
function Go(th,base) {
var val = th.previousElementSibling.value;
location.href = base + val
}
$(function () {
var v =$.cookie("per_page_num");
$("#ll").val(v);
});
function changePageSize(th) {
var v = $(th).val();
console.log(v);
$.cookie("per_page_num",v);
location.reload();
}
</script>
</body>
</html>
views.py
def user_list(request):
current_page = request.GET.get("p",1)
current_page = int(current_page) #当前页
res = request.COOKIES.get("per_page_num")
res = int(res)
page_obj = pagination.Page(current_page,len(list),res)
data=list[page_obj.start:page_obj.end]
page_str = page_obj.page_str("/user_list/")
return render(request,"user_list.html",{"list":data,"temp":page_str})
显示效果如下:其中利用了jquery和jquery.cookie
用户认证装饰器
FBV的装饰器用法
我们前面的代码中有用户登录的:
def index(request):
#获取当前登录的用户名
v = request.COOKIES.get("username1")
if not v:
return redirect("/login/")
return render(request,"index.html",{"current_user":v})
当我们有很多页面都需要用户登录验证的时候,每个都这样写的话,就出现了代码重复,所以这里我们可以用装饰器实现,上面代码修改一下就可以。
修改后:
def auth(func):
def inner(request,*args,**kwargs):
v = request.COOKIES.get("username1")
if not v:
return redirect("/login/")
return func(request,*args,**kwargs)
return inner
@auth
def index(request):
#获取当前登录的用户名
v = request.COOKIES.get("username1")
return render(request,"index.html",{"current_user":v})
CBV的装饰器用法
下面是一个普通的CBV的例子:
from django import views
class Order(views.View):
def get(self,request):
v = request.COOKIES.get("username1")
if not v:
return redirect("/login/")
return render(request, "index.html", {"current_user": v})
def post(self,request):
v = request.COOKIES.get("username1")
return render(request, "index.html", {"current_user": v})
我们只对get请求做认证,当我们访问Order时候就加上了认证
def auth(func):
def inner(request,*args,**kwargs):
v = request.COOKIES.get("username1")
if not v:
return redirect("/login/")
return func(request,*args,**kwargs)
return inner
from django import views
from django.utils.decorators import method_decorator
class Order(views.View):
@method_decorator(auth)
def get(self,request):
v = request.COOKIES.get("username1")
return render(request, "index.html", {"current_user": v})
def post(self,request):
v = request.COOKIES.get("username1")
return render(request, "index.html", {"current_user": v})
这种方法只针对于get,如果我们想给更多的方法加上认证,还可以修改成这样的:
def auth(func):
def inner(request,*args,**kwargs):
v = request.COOKIES.get("username1")
if not v:
return redirect("/login/")
return func(request,*args,**kwargs)
return inner
from django import views
from django.utils.decorators import method_decorator
class Order(views.View):
@method_decorator(auth)
def dispatch(self, request, *args, **kwargs):
return super(Order,self).dispatch(request, *args, **kwargs)
def get(self,request):
v = request.COOKIES.get("username1")
return render(request, "index.html", {"current_user": v})
def post(self,request):
v = request.COOKIES.get("username1")
return render(request, "index.html", {"current_user": v})
每次执行CBV是必须先执行一个dispatch方法,我们在dispatch加上认证,就相当于在所有上面加上认证,
我们还可以简化成:
@method_decorator(auth,name="dispatch")
class Order(views.View):
def get(self,request):
v = request.COOKIES.get("username1")
return render(request, "index.html", {"current_user": v})
def post(self,request):
v = request.COOKIES.get("username1")
return render(request, "index.html", {"current_user": v})
session
session的原理
基于cookie做用户验证的时候,敏感信息不适合放在cookie中
session依赖cookie
cookie是保存在用户浏览器端的键值对
session是保存在服务器端的键值对
session在服务器端的数据存放为:
session={
随机字符串1:{用户1的相关信息},
随机字符串2:{用户2的相关信息},
随机字符串3:{用户3的相关信息},
}
session的客户端就是浏览器的客户端中cookie中存放的数据是当前用户对应的随机字符串。
session的工作过程
过程:
1、生成随机字符串
2、写到用户浏览器的cookie中
3、保存到session中
4、在随机字符串对应的字典中设置相关内容
而上述过程在Django中的体现为: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("username")
pwd = request.POST.get("pwd")
if user == "root" and pwd == "123":
request.session["username"] = user
request.session["is_login"] = True
return redirect("/index/")
else:
return render(request, "login.html")
def index(request):
if request.session["is_login"]:
return HttpResponse(request.session["username"])
else:
return HttpResponse("REEOR")
request.session["username"]=user
这里的username为通过request.POST.get("username")从前端html页面中获取到的用户名信息
注意:
在Django中要用session中一定要先执行:
python manage.py makemigrations
python manage.py migrate
因为当用户登录的时候的就会在数据库的django_session表中记录session信息
同样的通过request.session["username"]也可以获取相应的值
在这个过程中:
1、首先获取当前用户的随机字符串
2、根据随机字符串获取对应的内容
session的操作
1 request.session["k1"] 如果不存在则会报错 2 3 request.session.get["k1"],如果不存在则会报错,为了防止出错可以request.session.get('k1',none) 4 5 6 7 request.session['k1'] = 123 设置session值 8 9 request.session.setdefault('k1',123) 存在则不设置 10 11 del request.session['k1'] 删除 12 13 request.session.clear() 删除 14 15 16 17 所有 键、值、键值对 18 19 request.session.keys() 20 21 request.session.values() 22 23 request.session.items() 24 25 request.session.iterkeys() 26 27 request.session.itervalues() 28 29 request.session.iteritems() 30 31 32 33 用户session的随机字符串 34 35 request.session.session_key 36 37 38 39 将所有Session失效日期小于当前日期的数据删除 40 41 request.session.clear_expired() 42 43 44 45 检查 用户session的随机字符串 在数据库中是否 46 47 request.session.exists("session_key") 48 49 50 51 删除当前用户的所有Session数据 52 53 request.session.delete("session_key") 54 55 56 57 request.session.set_expiry(value) 58 59 默认的过期时间是两周,如果自己设置了过期时间,这样自己设定的优先级就会高于默认的 60 61 如果value是个整数,session会在些秒数后失效。 62 63 如果value是个datatime或timedelta,session就会在这个时间后失效。 64 65 如果value是0,用户关闭浏览器session就会失效。 66 67 如果value是None,session会依赖全局session失效策略。 68 69 70 71 配置setting.py 72 73 SESSION_COOKIE_NAME = "sessionid" # Session的cookie保存在浏览器上时的key,即:sessionid=随机字符串(默认) 74 75 SESSION_COOKIE_PATH = "/" # Session的cookie保存的路径(默认) 76 77 SESSION_COOKIE_DOMAIN = None # Session的cookie保存的域名(默认) 78 79 SESSION_COOKIE_SECURE = False # 是否Https传输cookie(默认) 80 81 SESSION_COOKIE_HTTPONLY = True # 是否Session的cookie只支持http传输(默认) 82 83 SESSION_COOKIE_AGE = 1209600 # Session的cookie失效日期(2周)(默认) 84 85 SESSION_EXPIRE_AT_BROWSER_CLOSE = False # 是否关闭浏览器使得Session过期(默认) 86 87 SESSION_SAVE_EVERY_REQUEST = False # 是否每次请求都保存Session,默认修改之后才保存(默认)
Django中对于session的存储方式
1 Django默认支持Session,并且默认是将Session数据存储在数据库中,即:django_session 表中。 2 3 a. 配置 settings.py 4 5 SESSION_ENGINE = 'django.contrib.sessions.backends.db' # 引擎(默认) 6 7 SESSION_COOKIE_NAME = "sessionid" # Session的cookie保存在浏览器上时的key,即:sessionid=随机字符串(默认) 8 SESSION_COOKIE_PATH = "/" # Session的cookie保存的路径(默认) 9 SESSION_COOKIE_DOMAIN = None # Session的cookie保存的域名(默认) 10 SESSION_COOKIE_SECURE = False # 是否Https传输cookie(默认) 11 SESSION_COOKIE_HTTPONLY = True # 是否Session的cookie只支持http传输(默认) 12 SESSION_COOKIE_AGE = 1209600 # Session的cookie失效日期(2周)(默认) 13 SESSION_EXPIRE_AT_BROWSER_CLOSE = False # 是否关闭浏览器使得Session过期(默认) 14 SESSION_SAVE_EVERY_REQUEST = False # 是否每次请求都保存Session,默认修改之后才保存(默认) 15 16 17 18 b. 使用 19 20 def index(request): 21 # 获取、设置、删除Session中数据 22 request.session['k1'] 23 request.session.get('k1',None) 24 request.session['k1'] = 123 25 request.session.setdefault('k1',123) # 存在则不设置 26 del request.session['k1'] 27 28 # 所有 键、值、键值对 29 request.session.keys() 30 request.session.values() 31 request.session.items() 32 request.session.iterkeys() 33 request.session.itervalues() 34 request.session.iteritems() 35 36 37 # 用户session的随机字符串 38 request.session.session_key 39 40 # 将所有Session失效日期小于当前日期的数据删除 41 request.session.clear_expired() 42 43 # 检查 用户session的随机字符串 在数据库中是否 44 request.session.exists("session_key") 45 46 # 删除当前用户的所有Session数据 47 request.session.delete("session_key") 48 49 request.session.set_expiry(value) 50 * 如果value是个整数,session会在些秒数后失效。 51 * 如果value是个datatime或timedelta,session就会在这个时间后失效。 52 * 如果value是0,用户关闭浏览器session就会失效。 53 * 如果value是None,session会依赖全局session失效策略。
1 a. 配置 settings.py 2 3 SESSION_ENGINE = 'django.contrib.sessions.backends.cache' # 引擎 4 SESSION_CACHE_ALIAS = 'default' # 使用的缓存别名(默认内存缓存,也可以是memcache),此处别名依赖缓存的设置 5 6 7 SESSION_COOKIE_NAME = "sessionid" # Session的cookie保存在浏览器上时的key,即:sessionid=随机字符串 8 SESSION_COOKIE_PATH = "/" # Session的cookie保存的路径 9 SESSION_COOKIE_DOMAIN = None # Session的cookie保存的域名 10 SESSION_COOKIE_SECURE = False # 是否Https传输cookie 11 SESSION_COOKIE_HTTPONLY = True # 是否Session的cookie只支持http传输 12 SESSION_COOKIE_AGE = 1209600 # Session的cookie失效日期(2周) 13 SESSION_EXPIRE_AT_BROWSER_CLOSE = False # 是否关闭浏览器使得Session过期 14 SESSION_SAVE_EVERY_REQUEST = False # 是否每次请求都保存Session,默认修改之后才保存 15 16 17 18 b. 使用 19 20 同上
1 a. 配置 settings.py 2 3 SESSION_ENGINE = 'django.contrib.sessions.backends.file' # 引擎 4 SESSION_FILE_PATH = None # 缓存文件路径,如果为None,则使用tempfile模块获取一个临时地址tempfile.gettempdir() # 如:/var/folders/d3/j9tj0gz93dg06bmwxmhh6_xm0000gn/T 5 6 7 SESSION_COOKIE_NAME = "sessionid" # Session的cookie保存在浏览器上时的key,即:sessionid=随机字符串 8 SESSION_COOKIE_PATH = "/" # Session的cookie保存的路径 9 SESSION_COOKIE_DOMAIN = None # Session的cookie保存的域名 10 SESSION_COOKIE_SECURE = False # 是否Https传输cookie 11 SESSION_COOKIE_HTTPONLY = True # 是否Session的cookie只支持http传输 12 SESSION_COOKIE_AGE = 1209600 # Session的cookie失效日期(2周) 13 SESSION_EXPIRE_AT_BROWSER_CLOSE = False # 是否关闭浏览器使得Session过期 14 SESSION_SAVE_EVERY_REQUEST = False # 是否每次请求都保存Session,默认修改之后才保存 15 16 b. 使用 17 18 同上
1 数据库用于做持久化,缓存用于提高效率 2 3 a. 配置 settings.py 4 5 SESSION_ENGINE = 'django.contrib.sessions.backends.cached_db' # 引擎 6 7 b. 使用 8 9 同上
1 a. 配置 settings.py 2 3 SESSION_ENGINE = 'django.contrib.sessions.backends.signed_cookies' # 引擎 4 5 b. 使用 6 7 同上
扩展:Session用户验证
def login(func):
def wrap(request, *args, **kwargs):
# 如果未登陆,跳转到指定页面
if request.path == '/test/':
return redirect('http://www.baidu.com')
return func(request, *args, **kwargs)
return wrap
CSRF
django为用户实现防止跨站请求伪造的功能,通过中间件 django.middleware.csrf.CsrfViewMiddleware 来完成。而对于django中设置防跨站请求伪造功能有分为全局和局部。
全局:
中间件 django.middleware.csrf.CsrfViewMiddleware
局部:
@csrf_protect,为当前函数强制设置防跨站请求伪造功能,即便settings中没有设置全局中间件。
@csrf_exempt,取消当前函数防跨站请求伪造功能,即便settings中设置了全局中间件。
注意:from django.views.decorators.csrf import csrf_exempt,csrf_protect
原理
当用post提交数据的时候,django会去检查是否有一个csrf的随机字符串,如果没有就会报错,这也是之前我们一直将其注释的原因,错误如下:
在django内部支持生成这个随机字符串
通过form提交
在form表单里面需要添加{%csrf_token%}
这样当你查看页面源码的时候,可以看到form中有一个input是隐藏的
总结原理:当用户访问login页面的时候,会生成一个csrf的随机字符串,并且cookie中也存放了这个随机字符串,当用户再次提交数据的时候会带着这个随机字符串提交,如果没有这个随机字符串则无法提交成功
cookie中存放的csrftoken如下图
通过ajax提交
因为cookie中同样存在csrftoken,所以可以在js中通过:
$.cooke("cstftoken")获取
如果通过ajax进行提交数据,这里提交的csrftoken是通过请求头中存放,需要提交一个字典类型的数据,即这个时候需要一个key。
在views中的login函数中:from django.conf import settings,然后打印print(settings.CSRF_HEADER_NAME)
这里需要注意一个问题,这里导入的settings并不是我们在项目文件下看到的settings.py文件,这里是是一个全局的settings配置,而当我们在项目目录下的settings.py中配置的时候,我们添加的配置则会覆盖全局settings中的配置
print(settings.CSRF_HEADER_NAME)打印的内容为:HTTP_X_CSRFTOKEN
这里的HTTP_X_CSRFTOKEN是django在X_CSRF的前面添加了HTTP_,所以实际传递的是就是X_CSRFtoken,而在前端页面的ajax传递的时候由于不能使用下划线所以传递的是X_CSRFtoken
下面是在前端ajax中写的具体内容:
1 $("#btn1").click(function () { 2 $.ajax({ 3 url:"/login/", 4 type:"POST", 5 data:{"usr":"root","pwd":"123"}, 6 headers:{ "X-CSRFtoken":$.cookie("csrftoken")}, 7 success:function (arg) { 8 9 } 10 }) 11 })
但是如果页面中有多个ajax请求的话就在每个ajax中添加headers信息,所以可以通过下面方式在所有的ajax中都添加
1 $.ajaxSetup({ 2 beforeSend:function (xhr,settings) { 3 xhr.setRequestHeader("X-CSRFtoken",$.cookie("csrftoken")) 4 } 5 });
这样就会在提交ajax之前执行这个方法,从而在所有的ajax里都加上这个csrftoken
这里的xhr是XMLHttpRequest的简写,ajax调用的就是这个方法
如果想要实现在当get方式的时候不需要提交csrftoken,当post的时候需要,实现这种效果的代码如下:
1 function csrfSafeMethod(method) { 2 // these HTTP methods do not require CSRF protection 3 return (/^(GET|HEAD|OPTIONS|TRACE)$/.test(method)); 4 } 5 $.ajaxSetup({ 6 beforeSend: function(xhr, settings) { 7 if (!csrfSafeMethod(settings.type) && !this.crossDomain) { 8 xhr.setRequestHeader("X-CSRFToken", csrftoken); 9 } 10 } 11 });
这样就实现了当GET|HEAD|OPTIONS|TRACE这些方式请求的时候不需要提交csrftoken
总结
1、csrf在ajax提交的时候通过请求头传递的给后台的
2、csrf在前端的key为:X-CSRFtoken,到后端的时候django会自动添加HTTP_,并且最后为HTTP_X_CSRFtoken
3、csrf在form中提交的时需要在前端form中添加{%csrftoken%}