文章目录
1,cookie
1、cookie简介
- cookie实质就是客户端硬盘中存放的键值对,利用这个特性可以用来做用户验证
- 比如:{“username”: “dachengzi”} #再次访问url就会携带这些信息过来
2、前端操作cookie
说明: 使用下面方法操cookie必须先引入jquery.cookie.js
- 前端获取cookie值: var v = $.cookie(‘per_page_count’);
- 前端设置cookie值: $.cookie(‘per_page_count’,v);
3、后端操作cookie
说明: response = HttpResponse(…) 或 response = render(request, …)
- 后端设置cookie值: response.set_cookie(‘username’,“zhangsan”)
- 后端后去cookie值: request.COOKIES.get(‘username’)
4、使用cookie实现用户登录、注销
- urls.py
from django.contrib import admin
from django.urls import path,re_path
from app01 import views
urlpatterns = [
path('admin/', admin.site.urls),
re_path(r'login/$',views.login),
re_path(r'index/$',views.index),
re_path(r'logout/$',views.logout),
]
- views.py
from django.shortcuts import render,HttpResponse,redirect
def index(request):
username = request.COOKIES.get('username') # 获取cookie
if not username:
return redirect('/login/')
return HttpResponse(username)
def login(request):
if request.method == "GET":
return render(request,'login.html',{'msg':''})
if request.method == "POST":
u = request.POST.get('username')
p = request.POST.get('pwd')
print(u,p)
if u == 'tom' and p == '123':
res = redirect('/index/')
res.set_cookie('username',u ,max_age=10) # 设置500s免登陆
return res
else:
return render(request,'login.html', {'msg':'用户名或密码错误'})
def logout(req):
response = redirect('/login/')
#清理cookie里保存username
response.delete_cookie('username')
return response
- login.html
<form action="/login/" method="POST">
<input type="text" name="username" placeholder="用户名">
<input type="text" name="pwd" placeholder="密码">
<input type="submit" value="提交">
<p>{{ msg }}</p>
</form>
**5、设置cookie时常用参数 **
def cookie(request):
#1 获取cookie中username111得值
request.COOKIES.get('username111')
#2 设置cookie的值,关闭浏览器失效
response.set_cookie('key',"value")
# 设置cookie, N秒只后失效
response.set_cookie('username111',"value",max_age=10)
#3 设置cookie, 截止时间失效(expires后面指定那个时间点失效)
import datetime
current_date = datetime.datetime.utcnow()
exp_date = current_date + datetime.timedelta(seconds=5) #seconds指定再过多少秒过期
response.set_cookie('username111',"value",expires=exp_date)
#4 设置cookie是可以使用关键字salt对cookie加密(加密解密的salt中值必须相同)
obj = HttpResponse('s')
obj.set_signed_cookie('username',"kangbazi",salt="asdfasdf")
request.get_signed_cookie('username',salt="asdfasdf")
#5 设置cookie生效路径
path = '/'
#6 删除cookie中is_login的值
response.delete_cookie('is_login')
return response
6、前端设置、获取cookie
- urls.py
from app01 import views
urlpatterns = [
url(r'^admin/', admin.site.urls),
url(r'^index/', views.index),
url(r'^get_ck/', views.get_ck),
]
- /app01/views.py
from django.shortcuts import render,HttpResponse
def index(request):
return render(request, 'index.html', )
def get_ck(request):
val = request.COOKIES.get('per_page_count')
print('get_ck',val)
return HttpResponse(val)
- index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
{# 选择每页显示多少条的select单选框 #}
<div>
<select id="ps" onchange="changePageSize(this)">
<option value="10">10</option>
<option value="20">20</option>
<option value="50">50</option>
<option value="100">100</option>
</select>
</div>
<script src="/static/jquery-1.12.4.js"></script>
<script src="/static/jquery.cookie.js"></script>
<script>
//当框架加载完成后获取cookie的值,并设置到select中
$(function(){
var v = $.cookie('per_page_count'); //前端获取cookie值
console.log(v);
$('#ps').val(v);
});
function changePageSize(ths){
//获取select单选框选择的值(10,20,50,100)这些选项
var v = $(ths).val();
//使用cookie将v的值传递到后台
$.cookie('per_page_count',v, { expires: 7 }); //前端设置cookie值
$.cookie('per_page_count',v, {'path':'/'}); // 将这个路径设置为网站的根目录
}
</script>
</body>
</html>
2,session
1、Session作用 & 原理(session操作依赖cookie)
- 基于Cookie做用户验证时:敏感信息不适合放在cookie中
- 用户成功登陆后服务端会生成一个随机字符串并将这个字符串作为字典key,将用户登录信息作为value
- 当用户再次登陆时就会带着这个随机字符串过来,就不必再输入用户名和密码了
- 用户使用cookie将这个随机字符串保存到客户端本地,当用户再来时携带这个随机字符串,服务端根据
这个随机字符串查找对应的session中的值,这样就避免敏感信息泄露
2、Cookie和Session对比
1、Cookie是保存在用户浏览器端的键值对
2、Session是保存在服务器端的键值对
3、Django中支持下面五种session,可以在settings.py中配置
- 5种session在settings.py中公用配置参数
# 1、SESSION_COOKIE_NAME = "sessionid" # Session的cookie保存在浏览器上时的key,即:sessionid=随机字符串(默认)
# 2、SESSION_COOKIE_PATH = "/" # Session的cookie保存的路径(默认)
# 3、SESSION_COOKIE_DOMAIN = None # Session的cookie保存的域名(默认)
# 4、SESSION_COOKIE_SECURE = False # 是否Https传输cookie(默认)
# 5、SESSION_COOKIE_HTTPONLY = True # 是否Session的cookie只支持http传输(默认)
# 6、SESSION_COOKIE_AGE = 1209600 # Session的cookie失效日期(2周)(默认)
# 7、SESSION_EXPIRE_AT_BROWSER_CLOSE = False # 是否关闭浏览器使得Session过期(默认)
# 8、SESSION_SAVE_EVERY_REQUEST = False # 是否每次请求都保存Session,默认修改之后才保存(默认)
# 10s 免登陆时,这里必须配置成True
- settings.py中配置使用session五种方法
#1 数据库(默认) #将session数据保存到数据库中
SESSION_ENGINE = 'django.contrib.sessions.backends.db'
#2 缓存 #将session数据保存到缓存中
# 使用的缓存别名(默认内存缓存,也可以是memcache),此处别名依赖缓存的设置
# 'default'是下面链接的缓存别名,也可以是另一缓存名'db1'
SESSION_ENGINE = 'django.contrib.sessions.backends.cache'
SESSION_CACHE_ALIAS = 'default'
CACHES = {
'default': {
'BACKEND': 'django.core.cache.backends.memcached.MemcachedCache',
'LOCATION': [
'172.19.26.240:11211',
'172.19.26.242:11211',
]
},
'db1': {
'BACKEND': 'django.core.cache.backends.memcached.MemcachedCache',
'LOCATION': [
'172.19.26.240:11211',
'172.19.26.242:11211',
]
},
}
#3 文件Session
# 缓存文件路径,如果为None,则使用tempfile模块获取一个临时地址tempfile.gettempdir()
# 如:/var/folders/d3/j9tj0gz93dg06bmwxmhh6_xm0000gn/T把他当做session目录
SESSION_ENGINE = 'django.contrib.sessions.backends.file'
SESSION_FILE_PATH = os.path.join(BASE_DIR,'cache') #保存session的文件夹目录
#4 缓存+数据库Session #默认到缓存中拿session数据,没有再到数据库中取
SESSION_ENGINE = 'django.contrib.sessions.backends.cached_db'
#5 加密cookie Session #其实质使用的是cookie,将数据放到了客户端,但是数据经过了加密
SESSION_ENGINE = 'django.contrib.sessions.backends.signed_cookies'
4、操作session:设置、获取等
def index11(request): #request.session中存放了所有用户信息
#1 获取 Session中数据
request.session['k1']
request.session.get('k1',None)
#2 设置
request.session['k1'] = 123
request.session.setdefault('k1',123) # 存在则不设置
#3 删除
del request.session['k1'] #删除某个键值对
#删除某用户登录时在数据库生成的随机字符串的整条数据全部删除
request.session.delete("session_key")
#4 注销 当用户注销时使用request.session.clear()
request.session.clear() #这个短句相当于下面这个长句
request.session.delete(request.session.session_key)
#5 设置超时时间(默认超时时间两周)
request.session.set_expiry("value")
# 如果value是个整数,session会在些秒数后失效。
# 如果value是个datatime或timedelta,session就会在这个时间后失效。
# 如果value是0,用户关闭浏览器session就会失效。
# 如果value是None,session会依赖全局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")
5、session实现用户十秒免登陆,以及注销功能
- session默认使用数据库session,使用前必须先执行下面命令
python manage.py makemigrations
python manage.py migrate- settings.py中配置每次用户访问都会推辞更新时间
SESSION_SAVE_EVERY_REQUEST = True- 实现10s免登陆关键步骤
1) 设置session : request.session[‘is_login’] = True
2) 设置10s超时: request.session.set_expiry(10)
3) 获取session : request.session.get(‘is_login’)
- views.py
from django.shortcuts import render,HttpResponse,redirect
def index(request):
if request.session.get('is_login'):
return render(request,'index.html',{'username':request.session.get('username')})
else:
return 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 == 'tom' and pwd == '123':
#1 生成随机字符串
#2 写到用户浏览器cookie
#3 保存到session中
#4 在随机字符串对应的字典中设置相关内容
#5 有等号设置session键值对,没有等号获取键的值
request.session['username']=user #1 用户登录成功设置用户名
request.session['is_login'] = True #2 登陆成功时可以设置一个标志
if request.POST.get('rmb') == '1': #3 当勾选checkbox框后设置10秒超时
request.session.set_expiry(10) #4 设置10s后超时,需要重新登录
return redirect('/index')
else:
return render(request,'login.html')
def logout(request):
request.session.clear() #5 访问'/logout/'时注销登陆
return redirect('/login/')
- login.html
<!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="checkbox" name="rmb" value="1">十秒免登陆
<input type="submit" value="提交">
</form>
</body>
</html>
- index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>欢饮登陆:{{ username }},{{ request.session.username }}</h1>
<a href="/logout/">注销</a>
</body>
</html>
- settings.py
SESSION_SAVE_EVERY_REQUEST = True
3,Django序列化操作
1、为什么需要需要序列化操作
- python中很多格式的数据类型不能通过简单的json直接序列化转换成字符串格式传递到前端
- 比如:form提交出现错误,返回的是Django的errors.dict对象,不能直接用python的json序列化(必须先as_json)
2、Django序列化(法1:前端两次序列化得到错误信息)
- 首先后端现用 as_json 将errors.dict对象转换成字符串,然后通过json传递到前端
- 前端第一次序列化:将整个错误信息大字典变成对象,提取到其中的error小字典
- 前端第二次序列化:由于获取的小字典是字符串,还需要将error序列化成对象,才能获取到错误信息
- views.py
from django.shortcuts import render,HttpResponse
from django import forms
from django.forms import fields #fields字段专门用于验证
from django.forms import widgets #widgets专门用于生成html标签
import json
#这里为了方便没有将创建form放到forms.py中
class LoginForm(forms.Form):
username = fields.CharField()
password = fields.CharField(
max_length=12,
min_length=4,
error_messages={
'required':'密码不能为空',
'min_length':'密码长度不能小于4',
'max_length':'密码长度不能大于12'
}
)
def login(request):
if request.method == 'GET':
return render(request,'login.html')
elif request.method == 'POST':
ret = {'status':True,'error':None,'data':None}
obj = LoginForm(request.POST)
if obj.is_valid():
print(obj.cleaned_data)
#正确信息:{'password': '1234567343434343434', 'username': 'tom'}
else:
# obj.errors中封装的数据必须变成字符串才能json操作
ret['error'] = obj.errors.as_json() # as_json() 返回的是字符串
# print(obj.errors.as_data())
return HttpResponse(json.dumps(ret))
- login.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<form id="fm">
{% csrf_token %}
<p><input type="text" name="username"></p>
<p><input type="password" name="password">
<span id="pwd-err" style="color: red"></span></p>
<a id="submit">ajax提交</a>
</form>
<script src="/static/jquery-1.12.4.js"></script>
<script>
/* #### 当不填密码提交后返回的错误信息时这样的 ####
{'status': True,
'error': '{"password": [{"code": "required", "message": "密码不能为空"}]}',
'data': None}
*/
$(function(){
$('#submit').click(function(){
$.ajax({
url: '/login/',
type:'POST',
data:$('#fm').serialize(),
success:function(arg){ // 服务端返回的arg是字符串格式
arg = JSON.parse(arg); // 第一次json转换:将arg字符串转换成对象
error = arg.error; // 获取到arg对象中的error(error此时是字符串)
error = JSON.parse(error); // 第二次json转换:将error字符串转换成对象
pwd_err = error.password[0].message; // 转换成对象后才能获取到最终的错误信息
console.log(pwd_err); //pwd_err打印结果:密码不能为空
$('#pwd-err').text(pwd_err);
},
error:function(){
}
})
})
})
</script>
</body>
</html>
3、Django序列化(法2:前端一次序列化得到错误信息)
- 首先后端 as_data() 将获取到错误数据
- 然后将数据放到JsonCustomEncoder类中转换成指定格式的字典,前端一次即可的到错误提示信息
- views.py
from django.shortcuts import render,HttpResponse
from django import forms
from django.forms import fields #fields字段专门用于验证
from django.forms import widgets #widgets专门用于生成html标签
import json
from django.core.exceptions import ValidationError
class JsonCustomEncoder(json.JSONEncoder):
def default(self, field):
if isinstance(field, ValidationError):
return {'code':field.code,'messages':field.messages}
else:
return json.JSONEncoder.default(self, field)
#这里为了方便没有将创建form放到forms.py中
class LoginForm(forms.Form):
username = fields.CharField()
password = fields.CharField(
max_length=12,
min_length=4,
error_messages={
'required':'密码不能为空',
'min_length':'密码长度不能小于4',
'max_length':'密码长度不能大于12'
}
)
def login(request):
if request.method == 'GET':
return render(request,'login.html')
elif request.method == 'POST':
ret = {'status':True,'error':None,'data':None}
obj = LoginForm(request.POST)
if obj.is_valid():
print(obj.cleaned_data)
#正确信息:{'password': '1234567343434343434', 'username': 'tom'}
else:
# obj.errors中封装的数据必须变成字符串才能json操作
ret['error'] = obj.errors.as_data() # as_json() 返回的是字符串
# print(obj.errors.as_data()) # {'password': [ValidationError(['密码不能为空'])]}
print(ret)
request = json.dumps(ret,cls=JsonCustomEncoder)
print(request)
return HttpResponse(request)
- index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<form id="fm">
{% csrf_token %}
<p><input type="text" name="username"></p>
<p><input type="password" name="password">
<span id="pwd-err" style="color: red"></span></p>
<a id="submit">ajax提交</a>
</form>
<script src="/static/jquery-1.12.4.js"></script>
<script>
$(function(){
$('#submit').click(function(){
$.ajax({
url: '/login/',
type:'POST',
data:$('#fm').serialize(),
success:function(arg){ //服务端返回的是字符串格式
arg = JSON.parse(arg); //这里只需要一次序列化就可以得到error字典格式
pwd_err = arg.error.password[0].messages;
console.log(pwd_err); //pwd_err打印结果:密码不能为空
$('#pwd-err').text(pwd_err);
},
error:function(){
}
})
})
})
</script>
</body>
</html>
4,CSRF跨站请求伪造
1、CSRF原理
1、当用户第一次发送get请求时,服务端不仅给客户端返回get内容,而且中间包含一个随机字符串
2、这个字符串是加密的,只有服务端自己可以反解
3、当客户端发送POST请求提交数据时,服务端会验证客户端是否携带这个随机字符串, 没有就会引发csrf错误
4、如果没有csrf,那么黑客可以通过任意表单向我们的后台提交数据,不安全
2、form和ajax提交数据 解决CSRF方法
1、$.cookie(‘csrftoken’)可以获取到那个随机字符串
2、headers{} 可以将指定信息添加到请求头部发送给服务端
3、通过cookie和通过{{ csrf_token }}获取的随机字符串不同
- from和ajax提交数据 解决CSRF方法
<form action="/login/" method="POST">
{#1 form方式提交仅需要在form表单中添加csrf_token生成随机字符串即可 #}
{% csrf_token %}
<input type="submit" name="提交">
<input id="btn" type="button" value="按钮">
</form>
<script src="/static/jquery-1.12.4.js"></script>
<script src="/static/jquery.cookie.js"></script>
{#2 使用ajax提交CSRF字符串的两种方法 #}
<script>
$(function(){
{# 方法一: 仅用定义一个ajaxSetup实现对所有页面ajax提交数据自动发送CSRF字符串 #}
//xhr是ajax内部封装的一个方法
$.ajaxSetup({
beforeSend:function(xhr,settings){
xhr.setRequestHeader('X-CSRFtoken',$.cookie('csrftoken'))
}
});
{# 方法二: 必须在每个ajax提交的函数中都指定要发送CSRF字符串 #}
$('#btn').click(function(){
$.ajax({
url: '/login/',
type: 'POST',
data: {'user':'root','pwd':'123'},
headers:{'X-CSRFtoken':$.cookie('csrftoken')},
success:function(data){
}
})
})
})
</script>
3、ajax提交过滤
说明:对以GET,HEAD,OPTIONS,TRACE这四种提交数据方法不提交CSRF字符串
<script>
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);
}
}
});
</script>
ajax提交过滤
4、指定那些函数启用CSRF验证
- 法1:启用CSRF验证指,仅指定某些页面不需要验证
#1 启用settings文件中的CSRF
MIDDLEWARE = [
'django.middleware.csrf.CsrfViewMiddleware',
]
#2 对不需要验证的函数添加@csrf_exempt
from django.views.decorators.csrf import csrf_exempt,csrf_protect
@csrf_exempt
def login(request):
'处理函数内容'
- 法2:关闭CSRF验证指,仅指定某些页面需要验证
#1 关闭settings文件中的CSRF
MIDDLEWARE = [
# 'django.middleware.csrf.CsrfViewMiddleware',
]
#2 对需要验证的函数添加@csrf_protect
from django.views.decorators.csrf import csrf_exempt,csrf_protect
@csrf_protect
def login(request):
'处理函数内容'
5,Django中的缓存
1、Django缓存作用
- 由于Django是动态网站,所有每次请求均会去数据进行相应的操作,当程序访问量大时,耗时必然会更加明显
- 缓存将一个某个views的返回值保存至内存或者memcache中,5分钟内再有人来访问时,则不再去执行view中的操作
- 而是直接从内存或者Redis中之前缓存的内容拿到,并返回
2、Django中提供了6种缓存方式
- 1:开发调试缓存
# 开发调试缓存(虽然配置上,但实际没有缓存,还是到数据库取)
CACHES = {
'default': {
'BACKEND': 'django.core.cache.backends.dummy.DummyCache', # 引擎
#注: 下面这些参数时公用的,五种缓存都可以使用
'TIMEOUT': 300, # 缓存超时时间(默认300,None表示永不过期,0表示立即过期)
'OPTIONS':{
'MAX_ENTRIES': 300, # 最大缓存个数(默认300)
'CULL_FREQUENCY': 3, # 缓存到达最大个数之后,剔除缓存个数的比例(3就是1/3)
},
}
}
- 2:内存缓存
# 注:内存缓存本质上就是在内存中维护一个字典,所存储的形式就是字典的键值对组合
CACHES = {
'default': {
'BACKEND': 'django.core.cache.backends.locmem.LocMemCache',
'LOCATION': 'unique-snowflake', #这个参数指定变量名必须唯一
}
}
- 3:文件缓存
CACHES = {
'default': {
'BACKEND': 'django.core.cache.backends.filebased.FileBasedCache',
'LOCATION': os.path.join(BASE_DIR,'cache'), #缓存内容存放的文件夹路径
}
}
- 4:数据库缓存
# 注:执行创建表命令 python manage.py createcachetable
CACHES = {
'default': {
'BACKEND': 'django.core.cache.backends.db.DatabaseCache',
'LOCATION': 'my_cache_table', # 数据库表名(名字是自己取的)
}
}
- 5:Memcache缓存 两种
# 注:Memcache缓存有两个模块:python-memcached模块、 pylibmc模块
CACHES = {
'default': {
'BACKEND': 'django.core.cache.backends.memcached.MemcachedCache',
'LOCATION': '127.0.0.1:11211', #使用ip加端口连接memcached
}
}
CACHES = {
'default': {
'BACKEND': 'django.core.cache.backends.memcached.MemcachedCache',
'LOCATION': 'unix:/tmp/memcached.sock', #以文件的形式连本地memcached
}
}
CACHES = {
'default': {
'BACKEND': 'django.core.cache.backends.memcached.MemcachedCache',
'LOCATION': [ # memcached天生支持集群
#1 均衡分配
'172.19.26.240:11211',
'172.19.26.242:11211',
#2 调整权重(权重和请求比例成正比)
('172.19.26.240:11211',1),
('172.19.26.242:11211',10),
]
}
}
# 注: pylibmc模块只改变上面'BACKEND'配置为下面样式即可
# 'BACKEND': 'django.core.cache.backends.memcached.PyLibMCCache',
3、Django中缓存3种应用
- 页面级别缓存(需要在views.py文件中引入cache_page)
# 1、views.py文件中的处理函数
from django.views.decorators.cache import cache_page
@cache_page(6) #6秒后缓存失效
def cache(request):
import time
ctime = time.time()
return render(request,'cache.html',{'ctime':ctime})
# 2、cache.html文件中内容都会被缓存,5s后才会失效
<body>
<h1>{{ ctime }}</h1> #页面刷新时时间不会时刻变化,5s过后才会变一次
</body>
- 模板级别缓存(直接在cache.html模板文件中指定某个值放入缓存)
{#1 在文件最顶部引入TemplateTag#}
{% load cache %}
<body>
{#2 使用缓存 c1是缓存的key值 #}
{% cache 5 c1 %} {# 将数据缓存5秒 #}
{{ ctime }}
{% endcache %}
</body>
- 全局缓存(只需要在settings.py中间件配置的首尾各加一条)
注:设置全局缓存后所有页面的模板数据都会应用
MIDDLEWARE = [
'django.middleware.cache.UpdateCacheMiddleware',
# 其他中间件...
'django.middleware.cache.FetchFromCacheMiddleware',
]