一、什麼是Django中間件?
1、中間件是Django處理請求/響應的鉤子框架。這是一個輕,低層次的“插件”系統,用於全局改變Django和客戶端的輸入和輸出。
2、我的理解,給Django加一個裝飾器,客戶端向Django發送請求時,先由中間件處理一下,再決定是否交給Views處理
3、一般處理哪些任務?請求的日誌、用戶登錄認證、請求地址跳轉、IP地址過濾,等等
二、中間件在哪裏設置?
project下,project包下的settings.py文件,MIDDLEWARE列表,添加或刪除列表項。
三、多箇中間件的運行方式?
中間件列表運行從前到後,遇到錯誤停止,返回數據從後到前。類似列表的先進後出。
四、中間件在一次請求中的運行時間點?
1、Django服務啓動時,中件間初始化
2、客戶端請求時,中件間調用
五、自已怎麼做一箇中間件?
1、新建一個xxxxxx.py的python文件,可以放在Project任意位置,比如我這裏新建midtest.py存到project根目錄。
2、在midtest.py裏創建中間件函數或類
from django.utils.deprecation import MiddlewareMixin # 導入中間件類 class MyMiddleware(MiddlewareMixin): # 自定義類繼承中間件類 def process_request(self,request): # 重寫中間件方法 print("---process_request---")
3、把創建的中間件添加到settings.py的MIDDLEWARE列表
MIDDLEWARE=["midtest.MYMiddleware", ]
六、中間件進階:
1、自定義中間件的五個方法和執行順序:
process_request():客戶端請求時執行,然後請求urls.py
process_view():urls.py之後,views.py之前
process_template_response():views.py之後
process_exception():捕獲views.py的錯誤,所以是在views.py之後
process_response():在exception之後運行
2、代碼實例
from django.utils.deprecation import MiddlewareMixin class MyMiddleware(MiddlewareMixin): # 例子中列舉了所有方法,但使用的時候可以只用一個 # 當然也可以通過class MyMid: __init__和__call__來改寫,參考繼承的MiddelwareMixin類的寫法 def process_request(self, request): # request參數,客戶端的請求 # 可以設置驗證登陸、IP阻止列表等功能 # 1、-----通過session驗證登陸----- if request.path_info == '/login/': # 請求login正常執行 return elif not request.session.get('k1', None): # 請求其它頁面判斷未登陸跳轉到login return redirect('/login/') # 2、-----設置拒絕訪問的客戶端IP----- refuse_ip=["172.0.0.1"] if request.META.get('HTTP_X_FORWARDED_FOR', None): # 'HTTP_X_FORWARDED_FOR'使用反向代理時,尋找用戶真實IP ip = request.META['HTTP_X_FORWARDED_FOR'] else: ip = request.META['REMOTE_ADDR'] # 如果'HTTP_X_FORWARDED_FOR'不存在則獲取REMOTE_ADDR if ip in refuse_ip: # 判斷IP是否在拒絕列表裏 return HttpResponse("幹啥虧心事了,不讓你登陸!") def process_view(self, response, view_func, *args, **kwargs): # request客戶端請求 # view_func 請求的視圖函數 # *args,**kwargs 請求時附帶的參數 def process_exception(self, request, response): # 如果views.py函數有報錯,執行 # request客戶端請求
七、CSRF中間件
跨站請求僞造(英語:Cross-site request forgery),也被稱爲 one-click attack 或者 session riding,通常縮寫爲 CSRF 或者 XSRF, 是一種挾制用戶在當前已登錄的Web應用程序上執行非本意的操作的***方法。跟跨網站腳本(XSS)相比,XSS 利用的是用戶對指定網站的信任,CSRF 利用的是網站對用戶網頁瀏覽器的信任。
解決方法:
1、檢查HTTP的Referer字段
2、添加校驗Token
Django的CSRF校驗:
1、django使用的是添加校驗的Token,
2、方法一:使用的是中間件'django.middleware.csrf.CsrfViewMiddleware'
方法二:服務器向客戶端發送一個加密的Token,來進行客戶認證
3、HTML的From中添加標籤{% csrf_token %}
4、CSRF源碼:
class CsrfViewMiddleware(MiddlewareMixin): 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: # Cookie token needed to be replaced; # the cookie needs to be reset. 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: # Use same token next time. 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) if '' in (referer.scheme, referer.netloc): return self._reject(request, REASON_MALFORMED_REFERER) if referer.scheme != 'https': return self._reject(request, REASON_INSECURE_REFERER) 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: good_referer = request.get_host() 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: return self._reject(request, REASON_NO_CSRF_COOKIE) 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 self._set_token(request, response) response.csrf_cookie_set = True return response