中间件介绍和作用:
当请求来的时候需要先经过中间件才能到达Django后端
当响应走的时候也是需要经过中间件才能到达服务网关接口
中间件有点儿类似于小区里面的“保安”
那么可以利用它做什么呢?
它可以用来做网站全局的身份校验,访问频率限制,权限校验等。
#在settings里
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',
]
#首先看下其中三个中间键的源码:
import re
from django.conf import settings
from django.http import HttpResponsePermanentRedirect
from django.utils.deprecation import MiddlewareMixin
class SecurityMiddleware(MiddlewareMixin):
def __init__(self, get_response=None):
self.sts_seconds = settings.SECURE_HSTS_SECONDS
self.sts_include_subdomains = settings.SECURE_HSTS_INCLUDE_SUBDOMAINS
self.sts_preload = settings.SECURE_HSTS_PRELOAD
self.content_type_nosniff = settings.SECURE_CONTENT_TYPE_NOSNIFF
self.xss_filter = settings.SECURE_BROWSER_XSS_FILTER
self.redirect = settings.SECURE_SSL_REDIRECT
self.redirect_host = settings.SECURE_SSL_HOST
self.redirect_exempt = [re.compile(r) for r in settings.SECURE_REDIRECT_EXEMPT]
self.get_response = get_response
def process_request(self, request):
path = request.path.lstrip("/")
if (self.redirect and not request.is_secure() and
not any(pattern.search(path)
for pattern in self.redirect_exempt)):
host = self.redirect_host or request.get_host()
return HttpResponsePermanentRedirect(
"https://%s%s" % (host, request.get_full_path())
)
def process_response(self, request, response):
if (self.sts_seconds and request.is_secure() and
'strict-transport-security' not in response):
sts_header = "max-age=%s" % self.sts_seconds
if self.sts_include_subdomains:
sts_header = sts_header + "; includeSubDomains"
if self.sts_preload:
sts_header = sts_header + "; preload"
response["strict-transport-security"] = sts_header
if self.content_type_nosniff and 'x-content-type-options' not in response:
response["x-content-type-options"] = "nosniff"
if self.xss_filter and 'x-xss-protection' not in response:
response["x-xss-protection"] = "1; mode=block"
return response
#第二个
class CsrfViewMiddleware(MiddlewareMixin):
"""
Middleware that requires a present and correct csrfmiddlewaretoken
for POST requests that have a CSRF cookie, and sets an outgoing
CSRF cookie.
This middleware should be used in conjunction with the csrf_token template
tag.
"""
def _accept(self, request):
request.csrf_processing_done = True
return None
def _reject(self, request, reason):
logger.warning(
'Forbidden (%s): %s', reason, request.path,
extra={
'status_code': 403,
'request': request,
}
)
return _get_failure_view()(request, reason=reason)
def _get_token(self, request):
if settings.CSRF_USE_SESSIONS:
try:
return request.session.get(CSRF_SESSION_KEY)
except AttributeError:
raise ImproperlyConfigured(
'CSRF_USE_SESSIONS is enabled, but request.session is not '
'set. SessionMiddleware must appear before CsrfViewMiddleware '
'in MIDDLEWARE%s.' % ('_CLASSES' if settings.MIDDLEWARE is None else '')
)
else:
try:
cookie_token = request.COOKIES[settings.CSRF_COOKIE_NAME]
except KeyError:
return None
csrf_token = _sanitize_token(cookie_token)
if csrf_token != cookie_token:
request.csrf_cookie_needs_reset = True
return csrf_token
def _set_token(self, request, response):
if settings.CSRF_USE_SESSIONS:
request.session[CSRF_SESSION_KEY] = request.META['CSRF_COOKIE']
else:
response.set_cookie(
settings.CSRF_COOKIE_NAME,
request.META['CSRF_COOKIE'],
max_age=settings.CSRF_COOKIE_AGE,
domain=settings.CSRF_COOKIE_DOMAIN,
path=settings.CSRF_COOKIE_PATH,
secure=settings.CSRF_COOKIE_SECURE,
httponly=settings.CSRF_COOKIE_HTTPONLY,
)
patch_vary_headers(response, ('Cookie',))
def process_request(self, request):
csrf_token = self._get_token(request)
if csrf_token is not None:
request.META['CSRF_COOKIE'] = csrf_token
def process_view(self, request, callback, callback_args, callback_kwargs):
if getattr(request, 'csrf_processing_done', False):
return None
if getattr(callback, 'csrf_exempt', False):
return None
if request.method not in ('GET', 'HEAD', 'OPTIONS', 'TRACE'):
if getattr(request, '_dont_enforce_csrf_checks', False):
return self._accept(request)
if request.is_secure():
referer = force_text(
request.META.get('HTTP_REFERER'),
strings_only=True,
errors='replace'
)
if referer is None:
return self._reject(request, REASON_NO_REFERER)
referer = urlparse(referer)
# Make sure we have a valid URL for Referer.
if '' in (referer.scheme, referer.netloc):
return self._reject(request, REASON_MALFORMED_REFERER)
# Ensure that our Referer is also secure.
if referer.scheme != 'https':
return self._reject(request, REASON_INSECURE_REFERER)
# If there isn't a CSRF_COOKIE_DOMAIN, require an exact match
# match on host:port. If not, obey the cookie rules (or those
# for the session cookie, if CSRF_USE_SESSIONS).
good_referer = (
settings.SESSION_COOKIE_DOMAIN
if settings.CSRF_USE_SESSIONS
else settings.CSRF_COOKIE_DOMAIN
)
if good_referer is not None:
server_port = request.get_port()
if server_port not in ('443', '80'):
good_referer = '%s:%s' % (good_referer, server_port)
else:
# request.get_host() includes the port.
good_referer = request.get_host()
# Here we generate a list of all acceptable HTTP referers,
# including the current host since that has been validated
# upstream.
good_hosts = list(settings.CSRF_TRUSTED_ORIGINS)
good_hosts.append(good_referer)
if not any(is_same_domain(referer.netloc, host) for host in good_hosts):
reason = REASON_BAD_REFERER % referer.geturl()
return self._reject(request, reason)
csrf_token = request.META.get('CSRF_COOKIE')
if csrf_token is None:
# No CSRF cookie. For POST requests, we insist on a CSRF cookie,
# and in this way we can avoid all CSRF attacks, including login
# CSRF.
return self._reject(request, REASON_NO_CSRF_COOKIE)
# Check non-cookie token for match.
request_csrf_token = ""
if request.method == "POST":
try:
request_csrf_token = request.POST.get('csrfmiddlewaretoken', '')
except IOError:
pass
if request_csrf_token == "":
request_csrf_token = request.META.get(settings.CSRF_HEADER_NAME, '')
request_csrf_token = _sanitize_token(request_csrf_token)
if not _compare_salted_tokens(request_csrf_token, csrf_token):
return self._reject(request, REASON_BAD_TOKEN)
return self._accept(request)
def process_response(self, request, response):
if not getattr(request, 'csrf_cookie_needs_reset', False):
if getattr(response, 'csrf_cookie_set', False):
return response
if not request.META.get("CSRF_COOKIE_USED", False):
return response
# Set the CSRF cookie even if it's already set, so we renew
# the expiry timer.
self._set_token(request, response)
response.csrf_cookie_set = True
return response
#第三个
class AuthenticationMiddleware(MiddlewareMixin):
def process_request(self, request):
assert hasattr(request, 'session'), (
"The Django authentication middleware requires session middleware "
"to be installed. Edit your MIDDLEWARE%s setting to insert "
"'django.contrib.sessions.middleware.SessionMiddleware' before "
"'django.contrib.auth.middleware.AuthenticationMiddleware'."
) % ("_CLASSES" if settings.MIDDLEWARE is None else "")
request.user = SimpleLazyObject(lambda: get_user(request))
1.process_request
2.process_response
3.process_view
4.process_exception
5.process_template_response
#首先定义自己的类,模仿源码的构造即可,先测试process_request方法
from django.utils.deprecation import MiddlewareMixin
class Mymidr1(MiddlewareMixin):
def process_request(self, request):
print("我是第1个中间件里的 process_request 方法")
# def process_response(self,request,response):
# print("我是第1个中间件里的 process_response 方法")
# return response
class Mymidr2(MiddlewareMixin):
def process_request(self, request):
print("我是第2个中间件里的 process_request 方法")
# def process_response(self,request,response):
# print("我是第2个中间件里的 process_response 方法")
# return response
class Mymidr3(MiddlewareMixin):
def process_request(self, request):
print("我是第3个中间件里的 process_request 方法")
# def process_response(self,request,response):
# print("我是第3个中间件里的 process_response 方法")
# return response
#在视图函数随便来个
def index(request):
print("经过了视图函数index")
return HttpResponse("index")
#注意写了中间键后,一定要去settings去注册
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',
'mymd.mdd.Mymidr1',#!!!
'mymd.mdd.Mymidr2',#!!!
'mymd.mdd.Mymidr3',#!!!
]
那么经过一波操作后,我们来看看后端打印的结果:
小总结:当请求来的时候,会执行process_request,执行顺序是从上至下。
那么我们再来探讨 process_response 是个什么情况
#基于刚刚的代码稍微改动一下,来测试process_response方法
class Mymidr1(MiddlewareMixin):
def process_request(self, request):
print("我是第1个中间件里的 process_request 方法")
def process_response(self,request,response):
print("我是第1个中间件里的 process_response 方法")
return response
class Mymidr2(MiddlewareMixin):
def process_request(self, request):
print("我是第2个中间件里的 process_request 方法")
def process_response(self,request,response):
print("我是第2个中间件里的 process_response 方法")
return response
class Mymidr3(MiddlewareMixin):
def process_request(self, request):
print("我是第3个中间件里的 process_request 方法")
def process_response(self,request,response):
print("我是第3个中间件里的 process_response 方法")
return response
结果如下:
小总结:当响应回去的时候执行process_response,而且执行顺序是从下往上。
? 还必须将response返回,不然会报错,根本返回不了给前端。
#当我在process_request中直接拦截的话会有怎样的效果呢?
class Mymidr1(MiddlewareMixin):
def process_request(self, request):
print("我是第1个中间件里的 process_request 方法")
return HttpResponse("别去视图层了,已经被我拦截了")
def process_response(self,request,response):
print("我是第1个中间件里的 process_response 方法")
return response
class Mymidr2(MiddlewareMixin):
def process_request(self, request):
print("我是第2个中间件里的 process_request 方法")
def process_response(self,request,response):
print("我是第2个中间件里的 process_response 方法")
return response
class Mymidr3(MiddlewareMixin):
def process_request(self, request):
print("我是第3个中间件里的 process_request 方法")
def process_response(self,request,response):
print("我是第3个中间件里的 process_response 方法")
return response
结果如下:后端和前端
小总结:
当请求在一个 process_request 方法里被返回一个HttpResponse对象时,那么它将不会再继续往下面走其他的中间键,也不走视图函数等等,而是直接返回,并执行process_response方法返回。
那么其实基于这点,我们就可以在这个地方做些相关权限或者安全相关的设置了。
相比较flask框架而言,它如果被拦截,会直接从最后一个中间键走process_response返回,而不是像Django,
同级别拦截,就在同级别返回。所以,Django的安全性还是很高的。
我们再来讨论第三个方法:
# process_view方法
class Mymidr1(MiddlewareMixin):
def process_request(self, request):
print("我是第1个中间件里的 process_request 方法")
# return HttpResponse("别去视图层了,已经被我拦截了")
def process_response(self,request,response):
print("我是第1个中间件里的 process_response 方法")
return response
def process_view(self,request,view_func,view_args,view_kwargs):
print(view_func)
print(view_args)
print(view_kwargs)
print("我是第1个中间件里的 process_view 方法")
class Mymidr2(MiddlewareMixin):
def process_request(self, request):
print("我是第2个中间件里的 process_request 方法")
def process_response(self,request,response):
print("我是第2个中间件里的 process_response 方法")
return response
def process_view(self,request,view_func,view_args,view_kwargs):
print(view_func)
print(view_args)
print(view_kwargs)
print("我是第2个中间件里的 process_view 方法")
class Mymidr3(MiddlewareMixin):
def process_request(self, request):
print("我是第3个中间件里的 process_request 方法")
def process_response(self,request,response):
print("我是第3个中间件里的 process_response 方法")
return response
def process_view(self,request,view_func,view_args,view_kwargs):
print(view_func)
print(view_args)
print(view_kwargs)
print("我是第3个中间件里的 process_view 方法")
那后端打印结果如下:
小总结:基于前面的两种方法下,就来直接阐述第三种方法吧。
? 当请求来的时候,走了process_request方法后,再去执行process_view方法,然后走视图函数
? 最后响应的时候, 执行process_response方法。
? 那么它是在路由匹配成功执行视图函数之前执行的。
由于第四个和第五个方法太过鸡肋,就不加验证了,直接说结果吧!
#process_exception 方法是在视图函数出错的时候,也就是前端飘黄页的时候,会执行。
#那么既然报错了,也就不会走 process_response 方法了
#process_template_response 方法是当你的视图函数那么写的时候会执行:
def index(request):
print('我是index视图函数')
def render():
return HttpResponse('什么鬼玩意')
obj = HttpResponse('index')
obj.render = render
return obj
总而言之,用以下图来诠释是最特切的了:
就拿钓鱼网站来举例,其实它实现的内部原理就是做个很像很像很像正式网站的一个伪造网站,
它仅仅是在前端的页面中改动了输入框,比如说不设置name属性,而是隐藏式的写受益人的用户名
那么,像转账来说,就会被转到非目标用户上。
那么,我们是如何在防这种钓鱼网站的呢?
其实很简单,在正式网站返回给用户form表单时,偷摸着塞一个随机字符串给用户的表单中
一旦当用户再次请求时,那么我这个正式网站会比对那个随机字符串,是否一致。
说了这么多废话,怎么实现呢,那接下来就是代码层面的东西
//先拿前端页面来说
//其实也很简单,就在form表单发送post请求的时候,写一句下面这么个看似简单,功能强大的话。
{% csrf_token %}
其实csrf就是与那七个中间键中的 “django.middleware.csrf.CsrfViewMiddleware”这个有关。
它是用来做全局校验的,提高安全性。
当你网站全局不校验csrf的时候,但是又有几个需要校验的,应该这么做:
from django.utils.decorators import method_decorator
from django.views.decorators.csrf import csrf_exempt,csrf_protect
# 第一种方式
# @method_decorator(csrf_protect,name='post')
class MyView(View):
# 第三种方式
# @method_decorator(csrf_protect)
def dispatch(self, request, *args, **kwargs):
res = super().dispatch(request, *args, **kwargs)
return res
def get(self,request):
return HttpResponse('get')
# 第二种方式
# @method_decorator(csrf_protect)
def post(self,request):
return HttpResponse('post')
当你网站全局都需要校验csrf的时候,但又有几个不需要校验的,应该这么做:
@method_decorator(csrf_exempt,name='dispatch') # 第二种可以不校验的方式
class MyView(View):
# @method_decorator(csrf_exempt) # 第一种可以不校验的方式
def dispatch(self, request, *args, **kwargs):
res = super().dispatch(request, *args, **kwargs)
return res
def get(self,request):
return HttpResponse('get')
def post(self,request):
return HttpResponse('post')
这个模块是跟用户相关的,auth模块功能如下:
1.查询用户
2.记录用户状态
3.判断用户是否登陆
4.验证密码是否正确
5.修改密码
6.注销用户
7.注册用户
....
#首先用命令行创建个用户...
...
#查询用户
from django.contrib import auth
def login(request):
if request.method == "POST":
username = request.POST.get("username")
password = request.POST.get("password")
user_obj = auth.authenticate(username=username,password=password)
print(user_obj.username) #获取对象中的username
print(user_obj.password) #获取对象中的password,是个密文
print(user_obj) #是个对象
if user_obj:
return HttpResponse("ok")
return render(request,"login.html")
#记录用户状态
from django.contrib import auth
def login(request):
if request.method == "POST":
username = request.POST.get("username")
password = request.POST.get("password")
user_obj = auth.authenticate(username=username,password=password)
if user_obj:
auth.login(request,user_obj)
#只要用户状态保存了,那么在后端任意位置都可以通过 request.user 拿到当前用户对象
return HttpResponse("ok")
return render(request,"login.html")
#判断用户是否登陆
#一定要在settings里添加一个配置:LOGIN_URL = "/login/"
from django.contrib.auth.decorators import login_required
@login_required
def update_pwd(request):
if request.method == "POST":
old_password = request.POST.get("old_password")
n_password = request.POST.get("n_password")
#判断原密码是否正确
is_right= request.user.check_password(old_password)
if is_right:
#替换密码
request.user.set_password(n_password)
#修改密码后保存
request.user.save()
return render(request,"update_pwd.html")
#修改密码
def update_pwd(request):
if request.method == "POST":
old_password = request.POST.get("old_password")
n_password = request.POST.get("n_password")
#判断原密码是否正确
is_right= request.user.check_password(old_password)
if is_right:
#替换密码
request.user.set_password(n_password)
#修改密码后保存
request.user.save()
return render(request,"update_pwd.html")
#验证密码
request.user.check_password(old_password)
#注销用户
@login_required
def logout(request):
auth.logout(request)#清楚session
return HttpResponse("注销成功")
#注册用户
from django.contrib.auth.models import User
def register(request):
if request.method == "POST":
username = request.POST.get("username")
password = request.POST.get("password")
user_obj = User.objects.filter(username=username)
if not user_obj:
#创建普通用户
User.objects.create_user(username=username,password=password)
return render(request,"register.html")
总结:可见这个模块的强大之处,提升很高的效率
原文:https://www.cnblogs.com/cody-wu/p/11588158.html