Django操作session
session简介
1.cookie是保存在浏览器上面,它的数据不安全
1.怎么优化?
用session,把用户的信息保存在服务端
2.session它是基于cookie工作的,它的原理是:
1.先做用户信息的认证
2.生成一个随机字符串
3.用这个随机字符和用户信息做个映射,保存起来,django默认是保存在MySql中
"""
key data
随机字符串 用户信息
"""
4.服务端会把随机字符返回给浏览器,浏览器把随机字符串保存起来,下次访问的时候,一块带着随机字符串传给服务端,服务端拿着这个随机字符串去数据库中查询,如果查到了,说明已经登录过了。
3.cookie与session区别
1.cookie把数据保存在浏览器;session把数据保存在服务端
2.session保存的数据更加安全
3.cookie设置数据是有大小限制的(最大支持4096字节);session没有数据限制大小的
4.session由来:
Cookie弥补了HTTP无状态的不足,让服务器知道来的人是“谁”;但是Cookie以文本的形式保存在本地,自身安全性较差;所以我们就通过Cookie识别不同的用户,对应的在Session里保存私密的信息以及超过4096字节的文本。
另外,上述所说的Cookie和Session其实是共通性的东西,不限于语言和框架。
设置session
request.session['key']='value'
1.如果你在哎Django中默认使用session,前提是必须要django_session表
"""
django_session表字段:
session_key:随机字符串
session_data:加密之后的数据
expire_data:过期时间(session的默认过期时间14天)
"""
2.在cookie中,session保存的key默认是:sessionid
3.当session设置多个值的时候,django_Session表中也还是有一条记录(一台计算机的一个浏览器) ---->session_data值变了,session_key没有变
"""
request.session['username'] = 'nana'
request.session['username1'] = 'nana1'
request.session['username2'] = 'nana2'
request.session['username3'] = 'nana3'
"""
4. 设置session发生的事情
1.生成了随机字符串
2.把数据保存到了django_session表中
2.1 这个操作实在中间件中操作的
"""
MIDDLEWARE = [...,'django.contrib.sessions.middleware.SessionMiddleware',...]
ps: 看字符串的源码:用from ...import... eg: from django.contrib.sessions.middleware import SessionMiddleware
"""
2.2 请求来的时候,是把数据准备好,此时在缓存中(内存)
2.3 响应走的时候,才真正执行了create,insert方法
3.把随机字符串返回给浏览器
def set_session(request):
request.session['username'] = 'nana'
return HttpResponse('set_session')
获取session
request.session.get('key') or request.session['key']
1.获取session发生的事情
1.获取cookie值,名为sessionid的cookie值
2.拿着这个cookie值去数据库中查询
3.如果查询到了,把查询到的数据封装到request.session中
def get_session(request):
print(request.session.get('username')) # nana
return HttpResponse('get_session')
删除session
request.session.delete() or request.session.flush()
1.request.session.delete() # 只删除数据库
2.request.session.flush() # 数据库和cookie都删
def del_session(request):
# request.session.delete()
request.session.flush()
return HttpResponse('del session')
Django中session中常用方法
1.获取、设置、删除Session中数据
request.session['k1']
request.session.get('k1',None)
request.session['k1'] = 123
request.session.setdefault('k1',123) # 存在则不设置
del request.session['k1']
2.所有 键、值、键值对
request.session.keys()
request.session.values()
request.session.items()
request.session.iterkeys()
request.session.itervalues()
request.session.iteritems()
3.会话session的key
request.session.session_key
4.将所有Session失效日期小于当前日期的数据删除
request.session.clear_expired()
5.检查会话session的key在数据库中是否存在
request.session.exists("session_key")
6.删除当前会话的所有Session数据(只删数据库)
request.session.delete()
7.删除当前的会话数据并删除会话的Cookie(数据库和cookie都删)。
request.session.flush()
ps:这用于确保前面的会话数据不可以再次被用户的浏览器访问
eg:django.contrib.auth.logout() 函数中就会调用它。
8.设置会话Session和Cookie的超时时间
request.session.set_expiry(value)
* 如果value是个整数,session会在些秒数后失效。
* 如果value是个datatime或timedelta,session就会在这个时间后失效。
* 如果value是0,用户关闭浏览器session就会失效。
* 如果value是None,session会依赖全局session失效策略。
Django中的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,默认修改之后才保存(默认)
CBV中加装饰器
以登录认证装饰器为例
def login_auth(fun_name):
def inner(request,*args,**kwargs):
if request.COOKIES.get('username'):
res = fun_name(request,*args,**kwargs)
return res
else:
return redirect('/login/')
return inner
def login(request):
# 1.获取数据
if request.method == 'POST':
username = request.POST.get('username')
password = request.POST.get('password')
# 2.从数据库中比对数据
user_obj = models.UserInfo.objects.filter(username=username).first()
if not user_obj:
return HttpResponse('用户名不存在')
if user_obj.password == password:
print('登陆成功')
# 使用cookie保存用户信息
obj = redirect('/home/')
obj.set_cookie('username',username,max_age=3000000,)
return obj
return render(request, 'login.html')
@login_auth # home = login_auth(home)
def home(request):
return HttpResponse('这是home页面')
提示:post请求 ,可借助postman/apizz
CBV中加装饰器-方式一
在请求方式方法上添加语法糖@method_decorator(login_auth)
from django.views import View
from django.utils.decorators import method_decorator
class cbv_login(View):
@method_decorator(login_auth)
def get(self,request):
print('get')
return HttpResponse('get')
def post(self,request):
print('post')
return HttpResponse('post')
CBV中加装饰器-方式二
在类上添加语法糖@method_decorator(login_auth,name='请求方式方法')
from django.views import View
from django.utils.decorators import method_decorator
@method_decorator(login_auth,name='get')
@method_decorator(login_auth,name='post')
class cbv_login(View):
def get(self,request):
print('get')
return HttpResponse('get')
def post(self,request):
print('post')
return HttpResponse('post')
CBV中加装饰器-方式三
重写dispatch(在dispatch上添加语法糖@method_decorator(login_auth))
from django.views import View
from django.utils.decorators import method_decorator
class cbv_login(View):
@method_decorator(login_auth) # 类里面所有的方法都加了装饰器
def dispatch(self, request, *args, **kwargs):
return super(cbv_login, self).dispatch(request, *args, **kwargs)
def get(self,request):
print('get')
return HttpResponse('get')
def post(self,request):
print('post')
return HttpResponse('post')
Django的中间件
django的请求生命周期流程图
1.django的请求生命周期流程图
1.请求来了,先经过中间件
2.响应走了,也要经过中间件
2.看中间件源码
"""
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',
]
"""
ps: 1.查看中间件的源码:里面是字符串,使用from ... import ...
eg: from django.contrib.sessions.middleware import SessionMiddleware
from django.middleware.security import SecurityMiddleware
2.django自带的有七个中间件
3.django的中间件给我们暴露了一些方法
"""
class SessionMiddleware(MiddlewareMixin):
def process_request(self, request):
session_key = request.COOKIES.get(settings.SESSION_COOKIE_NAME)
request.session = self.SessionStore(session_key)
def process_response(self, request, response):
return response
"""
3.提供的几个方法
1.了解的方法:
1.process_view:是与视图函数相关的
2.process_exception:跟异常相关的
3.process_template_response:跟模版相关的
2.掌握的方法
1.process_request:请求来的时候触发
2.process_response:响应走的时候触发
ps:在一个中间件中,暴露的这几个方法不是每个类都必须有的,而是需要什么就写什么
4.应用场景:只要是与全局相关的功能都可以想到中间件
eg:
1.限制频率
2.认证登陆
3.权限校验(RBAC)
自定义中间件
1.django默认提供的有七个中间件,它还支持程序员自己定义中间件
2.自定中间件的步骤
1.在项目名下或者应用名下新建一个任意名称的文件夹
2.在这个文件夹下面新建一个py文件
3.在这个py文件中,新建一个类,必须继承MiddlewareMixin
4.在这个新建的类下面写两个方法:
1.process_reqeust
2.process_response
5.一定要在配置文件的中间件里面注册你的中间件路径
3.需求:创建自定义的中间件
针对process_request
1.执行顺序是按照配置文件中注册的顺序,从上往下依次执行
2.视图函数在中间件的process_reqeust函数之后执行
3.如果在process_request里面直接返回HttpRespponse,之后的中间件yilv不再走了,包括视图函数
from django.utils.deprecation import MiddlewareMixin
class mymiddleware1(MiddlewareMixin):
def process_request(self,request):
print('我是第一个中间件的process_request')
class mymiddleware2(MiddlewareMixin):
def process_request(self, request):
print('我是第二个中间件的process_request')
"""
MIDDLEWARE = [...,'app01.mymidddleware.mymiddle.mymiddleware1',
'app01.mymidddleware.mymiddle.mymiddleware2']
"""
>>>打印结果:
我是第一个中间件的process_request
我是第二个中间件的process_request
nana # 视图函数返回结果
from django.utils.deprecation import MiddlewareMixin
from django.shortcuts import render, HttpResponse
class mymiddleware1(MiddlewareMixin):
def process_request(self,request):
# 可以写ip访问逻辑
print('我是第一个中间件的process_request')
return HttpResponse('我是第一个中间件的process_request') # 拦截
class mymiddleware2(MiddlewareMixin):
def process_request(self, request):
print('我是第二个中间件的process_request')
>>>打印结果:
我是第一个中间件的process_request
针对process_response
1.必须返回HttpResponse
2.执行顺序:是按照配置文件的注册顺序,从下往上一次执行
from django.utils.deprecation import MiddlewareMixin
from django.shortcuts import render, HttpResponse
class mymiddleware1(MiddlewareMixin):
def process_request(self,request):
print('我是第一个中间件的process_request')
# return HttpResponse('我是第一个中间件的process_request')
def process_response(self, request, response):
print('我是第一个中间件的process_response')
return response # 本质返回的也是HttpResponse
class mymiddleware2(MiddlewareMixin):
def process_request(self, request):
print('我是第二个中间件的process_request')
def process_response(self, request, response):
print('我是第二个中间件的process_response')
return response
"""
MIDDLEWARE = [...,'app01.mymidddleware.mymiddle.mymiddleware1',
'app01.mymidddleware.mymiddle.mymiddleware2']
"""
>>>打印结果:
我是第一个中间件的process_request
我是第二个中间件的process_request
1
我是第二个中间件的process_response
我是第一个中间件的process_response
提问-结论
研究在第一个中间件的process_reqeust方法中,直接返回HttpResponse,然后,观察所有中间件的process_response的执行顺序?
from django.utils.deprecation import MiddlewareMixin
from django.shortcuts import render, HttpResponse
class mymiddleware1(MiddlewareMixin):
def process_request(self,request):
print('我是第一个中间件的process_request')
return HttpResponse('我是第一个中间件的process_request')
def process_response(self, request, response):
print('我是第一个中间件的process_response')
return response
class mymiddleware2(MiddlewareMixin):
def process_request(self, request):
print('我是第二个中间件的process_request')
def process_response(self, request, response):
print('我是第二个中间件的process_response')
return response
>>>打印结果:
我是第一个中间件的process_request
我是第一个中间件的process_response
# 页面返回:我是第一个中间件的process_request
结论:在第一个中间件中得process_reqeust中直接拦截,只走第一个中间件的process_reqeust和第一个中间件的process_response,直接原路返回。
但是,在flask框架中,如果在第一个拦截了,后面的类似于process_response都会走一遍(flask框架中实际上没有没有真正的中间件,它是需要我们看源码自己做成类似于django的这种中间件)
csrf跨站请求伪造
钓鱼网站
1.最典型的例子是钓鱼网站,所谓钓鱼网站,就是它的页面与真实网站界面基本一致,欺骗消费者或者窃取访问者提交的账号和密码信息
2.举例:
现在有一个正规的中国银行的网站,诈骗A写一个页面与中国银行的页面完全一模一样的网站,有个客户B去中国银行转钱,不小心来到了这个钓鱼网站,开始在这个钓鱼网站转账,转账完毕之后,钱确实少了,这个钱也到了中国银行,但是,它想转账的这个对方账户不是它自己想要的哪个账户.
3.针对上述情况,它是怎么做到的?
其内部本质是:在钓鱼网站上的form表单中,它是写一个没有name属性的input框,它在这个input框下面,在隐藏一个input框,这个隐藏的input框,我给他提前写好name属性和value值,但是用户不知道
4.如何规避上述问题?
在正规网站中给form表单一个唯一标识,然后,表单每次提交的时候,后端都做一个验证没如果正确就可以提交,如果不正确,就直接403(forbidden)
ps:面试的时候关于面试相关的:sql注入,xss攻击,csrf跨站请求,密码加盐
模拟钓鱼网站
正规网站
# views.py
def home(request):
if request.method =='POST':
username = request.POST.get('username')
target_name = request.POST.get('target_name')
money = request.POST.get('money')
print(f'用户{username}给{target_name}转账了{money}')
return render(request,'home.html')
# home.html
<body>
<h1>中国银行官方网站</h1>
<form action="" method="post">
username: <input type="text" name ='username'>
target_name: <input type="text" name = 'target_name'>
money: <input type="text" name = 'money'>
<input type="submit">
</form>
</body>
钓鱼网站
# views.py
def home(request):
return render(request,'home.html')
# home.html
<body>
<h1>伪造:中国银行官方网站</h1>
<form action="http://127.0.0.1:8000/home/" method="post">
username: <input type="text" name ='username'>
target_name: <input type="text" >
<input type="text" name="target_name" style="display: none" value="xiao">
money: <input type="text" name = 'money'>
<input type="submit">
</form>
</body>
解决问题
在正规网站中,给form表单一个唯一标识,然后,表单每次提交的时候,后端做一个验证,如果正确就可以提交,如果不正确,就直接403(forbidden)
<body>
<h1>中国银行官方网站</h1>
<form action="" method="post">
{% csrf_token %}
username: <input type="text" name ='username'>
target_name: <input type="text" name = 'target_name'>
money: <input type="text" name = 'money'>
<input type="submit">
</form>
</body>
ps:1.在form表单中加入{% csrf_token %},在浏览器端就多了一个标签:<input type="hidden" name="csrfmiddlewaretoken" value="ytTLI7TrhKTHW8rpLMOfqQhoKJBCyPQebbWAQpD0NzYNb1RsSE9bzSTtZ3c8REEr">;其value值是随机的(标识),form表单提交的时候,中间件的csrf进行验证,如果正确就可以提交,如果不正确,就直接403(forbidden)
ajax如何规避csrf的验证
方式一
data:{'csrfmiddlewaretoken':$('[name="csrfmiddlewaretoken"]'}
<body>
<h1>中国银行官方网站</h1>
<button class="btn">确认</button>
<script>
$('.btn').click(function (){
$.ajax({
url:'',
type:'post',
data:{'username':'nana','target_name':'cx','money':10000,'csrfmiddlewaretoken':$('[name="csrfmiddlewaretoken"]').val()}, //方式一
success:function (){
}
})
})
</script>
</body>
方式二
data:{'csrfmiddlewaretoken':'{{ csrf_token }}'}
<body>
<h1>中国银行官方网站</h1>
<button class="btn">确认</button>
<script>
$('.btn').click(function (){
$.ajax({
url:'',
type:'post',
data:{'username':'nana','target_name':'cx','money':10000,'csrfmiddlewaretoken':'{{ csrf_token }}'},
success:function (){
}
})
})
</script>
方式三
使用django官方提供的js文件
function getCookie(name) {
var cookieValue = null;
if (document.cookie && document.cookie !== '') {
var cookies = document.cookie.split(';');
for (var i = 0; i < cookies.length; i++) {
var cookie = jQuery.trim(cookies[i]);
// Does this cookie string begin with the name we want?
if (cookie.substring(0, name.length + 1) === (name + '=')) {
cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
break;
}
}
}
return cookieValue;
}
var csrftoken = getCookie('csrftoken');
// 每一次都这么写太麻烦了,可以使用$.ajaxSetup()方法为ajax请求统一设置。
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);
}
}
});
将上面的文件配置到你的Django项目的静态文件中,在html页面上通过导入该文件即可自动帮我们解决ajax提交post数据时校验csrf_token的问题,(导入该配置文件之前,需要先导入jQuery,因为这个配置文件内的内容是基于jQuery来实现的);更多细节详见:docs.djangoproject.com/en/1.11/ref…
<body>
<h1>中国银行官方网站</h1>
<button class="btn">确认</button>
<script src="/static/js/myjs.js"> //导入django官方提供的js文件
</script>
<script>
$('.btn').click(function (){
$.ajax({
url:'',
type:'post',
{#data:{'username':'nana','target_name':'cx','money':10000,'csrfmiddlewaretoken':$('[name="csrfmiddlewaretoken"]').val()},#}
{#data:{'username':'nana','target_name':'cx','money':10000,'csrfmiddlewaretoken':'{{ csrf_token }}'},#}
data:{'username':'nana','target_name':'cx','money':10000},
success:function (){
}
})
})
</script>
</body>