[python] Django의 login_required를 기본값으로 만드는 가장 좋은 방법
저는 큰 Django 앱을 작업 중이며 대부분의 경우 액세스하려면 로그인이 필요합니다. 즉, 앱 전체에 다음과 같이 뿌렸습니다.
@login_required
def view(...):
괜찮 습니다. 모든 곳에 추가하는 것을 기억하는 한 훌륭하게 작동합니다 ! 슬프게도 때때로 우리는 잊어 버리고 실패는 종종 그다지 분명하지 않습니다. 보기에 대한 유일한 링크가 @login_required 페이지에있는 경우 로그인하지 않고도 해당보기에 실제로 도달 할 수 있다는 사실을 눈치 채지 못할 것입니다.하지만 악의적 인 사용자는 이것이 문제임을 알아 차릴 수 있습니다.
내 생각은 시스템을 뒤집는 것이었다. 모든 곳에 @login_required를 입력하는 대신 다음과 같은 내용이 있습니다.
@public
def public_view(...):
공공 장소를 위해서. 나는 이것을 미들웨어로 구현하려고 시도했지만 작동하지 않는 것 같습니다. 내가 시도한 모든 것이 우리가 사용하는 다른 미들웨어와 나쁘게 상호 작용했다고 생각합니다. 다음으로 URL 패턴을 탐색하여 @public이 아닌 모든 항목이 @login_required로 표시되었는지 확인하기 위해 무언가를 작성하려고했습니다. 적어도 무언가를 잊으면 빠른 오류가 발생합니다. 하지만 @login_required가 뷰에 적용되었는지 확인하는 방법을 알 수 없었습니다 …
그렇다면이를 수행하는 올바른 방법은 무엇입니까? 도와 주셔서 감사합니다!
답변
미들웨어가 최선의 선택 일 수 있습니다. 나는 과거에 다른 곳에서 발견 된 스 니펫에서 수정 된이 코드를 사용했다.
import re
from django.conf import settings
from django.contrib.auth.decorators import login_required
class RequireLoginMiddleware(object):
"""
Middleware component that wraps the login_required decorator around
matching URL patterns. To use, add the class to MIDDLEWARE_CLASSES and
define LOGIN_REQUIRED_URLS and LOGIN_REQUIRED_URLS_EXCEPTIONS in your
settings.py. For example:
------
LOGIN_REQUIRED_URLS = (
r'/topsecret/(.*)$',
)
LOGIN_REQUIRED_URLS_EXCEPTIONS = (
r'/topsecret/login(.*)$',
r'/topsecret/logout(.*)$',
)
------
LOGIN_REQUIRED_URLS is where you define URL patterns; each pattern must
be a valid regex.
LOGIN_REQUIRED_URLS_EXCEPTIONS is, conversely, where you explicitly
define any exceptions (like login and logout URLs).
"""
def __init__(self):
self.required = tuple(re.compile(url) for url in settings.LOGIN_REQUIRED_URLS)
self.exceptions = tuple(re.compile(url) for url in settings.LOGIN_REQUIRED_URLS_EXCEPTIONS)
def process_view(self, request, view_func, view_args, view_kwargs):
# No need to process URLs if user already logged in
if request.user.is_authenticated():
return None
# An exception match should immediately return None
for url in self.exceptions:
if url.match(request.path):
return None
# Requests matching a restricted URL pattern are returned
# wrapped with the login_required decorator
for url in self.required:
if url.match(request.path):
return login_required(view_func)(request, *view_args, **view_kwargs)
# Explicitly return None for all non-matching requests
return None
그런 다음 settings.py에서 보호 할 기본 URL을 나열합니다.
LOGIN_REQUIRED_URLS = (
r'/private_stuff/(.*)$',
r'/login_required/(.*)$',
)
사이트가 인증이 필요한 페이지에 대한 URL 규칙을 따르는 한이 모델이 작동합니다. 일대일 적합하지 않은 경우 상황에 더 가깝게 미들웨어를 수정하도록 선택할 수 있습니다.
이 접근 방식에 대해 내가 좋아하는 점은-코드베이스를 @login_required
데코레이터 로 흩뿌 릴 필요성을 제거하는 것 외에도 -인증 체계가 변경되면 한 곳에서 전역 변경을 수행 할 수 있다는 것입니다.
답변
각 뷰 기능에 데코레이터를 배치하는 대안이 있습니다. login_required()
데코레이터를 urls.py
파일 에 넣을 수도 있습니다. 이 작업은 여전히 수동 작업이지만 최소한 모든 작업을 한곳에 모아서 감사하기가 더 쉽습니다.
예 :
my_views에서 home_view 가져 오기 urlpatterns = 패턴 ( '', # "집": (r '^ $', login_required (home_view), dict (template_name = 'my_site / home.html', items_per_page = 20)), )
뷰 함수는 이름이 지정되고 문자열이 아니라 직접 가져옵니다.
또한 이것은 클래스를 포함하여 호출 가능한 모든 뷰 객체에서 작동합니다.
답변
Django 2.1에서는 다음 과 같이 클래스의 모든 메서드를 장식 할 수 있습니다 .
from django.contrib.auth.decorators import login_required
from django.utils.decorators import method_decorator
from django.views.generic import TemplateView
@method_decorator(login_required, name='dispatch')
class ProtectedView(TemplateView):
template_name = 'secret.html'
업데이트 :
나는 또한 작동하기 위해 다음을 발견했다 :
from django.contrib.auth.mixins import LoginRequiredMixin
from django.views.generic import TemplateView
class ProtectedView(LoginRequiredMixin, TemplateView):
template_name = 'secret.html'
설정하고 LOGIN_URL = '/accounts/login/'
당신에 settings.py
답변
URL이 함수를보기 위해 전달되는 방식을 재 작업하지 않고 장고의 내장 가정을 변경하는 것은 어렵습니다.
Django 내부를 비웃는 대신 사용할 수있는 감사가 있습니다. 각보기 기능을 확인하기 만하면됩니다.
import os
import re
def view_modules( root ):
for path, dirs, files in os.walk( root ):
for d in dirs[:]:
if d.startswith("."):
dirs.remove(d)
for f in files:
name, ext = os.path.splitext(f)
if ext == ".py":
if name == "views":
yield os.path.join( path, f )
def def_lines( root ):
def_pat= re.compile( "\n(\S.*)\n+(^def\s+.*:$)", re.MULTILINE )
for v in view_modules( root ):
with open(v,"r") as source:
text= source.read()
for p in def_pat.findall( text ):
yield p
def report( root ):
for decorator, definition in def_lines( root ):
print decorator, definition
이것을 실행하고 def
적절한 데코레이터없이 s에 대한 출력을 검사하십시오 .
답변
다음은 django 1.10+ 용 미들웨어 솔루션입니다.
의 미들웨어는 django 1.10+에서 새로운 방식으로 작성되어야합니다 .
암호
import re
from django.conf import settings
from django.contrib.auth.decorators import login_required
class RequireLoginMiddleware(object):
def __init__(self, get_response):
# One-time configuration and initialization.
self.get_response = get_response
self.required = tuple(re.compile(url)
for url in settings.LOGIN_REQUIRED_URLS)
self.exceptions = tuple(re.compile(url)
for url in settings.LOGIN_REQUIRED_URLS_EXCEPTIONS)
def __call__(self, request):
response = self.get_response(request)
return response
def process_view(self, request, view_func, view_args, view_kwargs):
# No need to process URLs if user already logged in
if request.user.is_authenticated:
return None
# An exception match should immediately return None
for url in self.exceptions:
if url.match(request.path):
return None
# Requests matching a restricted URL pattern are returned
# wrapped with the login_required decorator
for url in self.required:
if url.match(request.path):
return login_required(view_func)(request, *view_args, **view_kwargs)
# Explicitly return None for all non-matching requests
return None
설치
- 코드를 프로젝트 폴더에 복사하고 middleware.py로 저장하십시오.
-
MIDDLEWARE에 추가
MIDDLEWARE = [… ‘.middleware.RequireLoginMiddleware’, # 로그인 필요]
- settings.py에 추가하십시오.
LOGIN_REQUIRED_URLS = (
r'(.*)',
)
LOGIN_REQUIRED_URLS_EXCEPTIONS = (
r'/admin(.*)$',
)
LOGIN_URL = '/admin'
출처 :
-
Daniel Naab의 답변
-
Max Goodridge의 Django 미들웨어 튜토리얼
답변
Ber의 답변에 영감을 받아 patterns
모든 URL 콜백을 login_required
데코레이터 로 래핑 하여 함수 를 대체하는 작은 스 니펫을 작성했습니다 . 이것은 Django 1.6에서 작동합니다.
def login_required_patterns(*args, **kw):
for pattern in patterns(*args, **kw):
# This is a property that should return a callable, even if a string view name is given.
callback = pattern.callback
# No property setter is provided, so this will have to do.
pattern._callback = login_required(callback)
yield pattern
그것을 사용하면 다음과 같이 작동합니다 (때문에 호출 list
이 필요합니다 yield
).
urlpatterns = list(login_required_patterns('', url(r'^$', home_view)))
답변
당신은 이길 수 없습니다. 당신은 단순히 해야한다 인증 요구 사항의 선언을합니다. 뷰 함수에 의한 권리를 제외하고이 선언을 어디에 두겠습니까?
뷰 함수를 호출 가능한 객체로 바꾸는 것을 고려하십시오.
class LoginViewFunction( object ):
def __call__( self, request, *args, **kw ):
p1 = self.login( request, *args, **kw )
if p1 is not None:
return p1
return self.view( request, *args, **kw )
def login( self, request )
if not request.user.is_authenticated():
return HttpResponseRedirect('/login/?next=%s' % request.path)
def view( self, request, *args, **kw ):
raise NotImplementedError
그런 다음 뷰 함수 하위 클래스를 LoginViewFunction
.
class MyRealView( LoginViewFunction ):
def view( self, request, *args, **kw ):
.... the real work ...
my_real_view = MyRealView()
코드 줄을 저장하지 않습니다. 그리고 그것은 “우리가 잊었다”문제에 도움이되지 않습니다. 할 수있는 일은 코드를 검사하여 뷰 함수가 객체인지 확인하는 것입니다. 올바른 클래스.
그러나 그럼에도 불구 하고 단위 테스트 스위트가 없으면 모든 뷰 기능이 정확 하다는 것을 결코 알 수 없습니다 .