最好的方法使Django的login_required默认

我正在开发一个大型的Django应用程序,其中绝大多数需要login才能访问。 这意味着,我们所有的应用程序,我们洒了:

@login_required def view(...): 

这很好, 只要我们记得把它添加到任何地方 ,它就会很好 ! 可悲的是,有时我们会忘记,而失败往往不是非常明显。 如果唯一链接到一个视图是在@login_required页面上,那么你不可能注意到你可以在没有login的情况下实际访问该视图。但是坏人可能会注意到,这是一个问题。

我的想法是扭转系统。 而不是必须在任何地方input@login_required,而应该是这样的:

 @public def public_view(...): 

只是为了公众的东西。 我试图用一些中间件实现这一点,我似乎无法得到它的工作。 我想,我尝试过的所有事情都与我们使用的其他中间件有着很大的不良影响。 接下来,我尝试编写一些东西来遍历URL模式,以检查不是@public的所有内容是否标记为@login_required – 至less在我们忘记了某些内容的情况下会出现一个错误。 但后来我无法弄清楚如何判断@login_required是否已经应用到视图中…

那么,怎样才能做到这一点呢? 谢谢您的帮助!

中间件可能是你最好的select。 过去我使用过这段代码,通过其他地方的代码片段进行修改:

 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/(.*)$', ) 

只要你的网站遵循需要authentication的网页的URL约定,这个模型就可以工作。 如果这不是一对一的合适的,你可以select修改中间件来更加贴近你的情况。

我喜欢这种方法 – 除了用@login_required装饰器去除代码库的必要性 – 就是如果authenticationscheme改变了,你就有一个地方去做全局的改变。

在每个视图函数上都有一个装饰器的替代方法。 您还可以将login_required()装饰器放在urls.py文件中。 虽然这仍然是一项手动任务,但至less您可以将它全部集中在一个地方,这样可以更轻松地进行审计。

例如,

    从my_views导入home_view

     urlpatterns = patterns('',
         #“家”:
         (r'^ $',login_required(home_view),dict(template_name ='my_site / home.html',items_per_page = 20)),
     )

请注意,视图函数是直接命名和导入的,而不是string。

另外请注意,这适用于任何可调用的视图对象,包括类。

在Django中修改内置的假设是很困难的,而不会改变url被移交到查看函数的方式。

而不是在Django内部喋喋不休,这里是一个可以使用的审计。 只需检查每个视图function。

 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没有适当的装饰器。

受Ber的回答的启发,我写了一个代码来replacepatterns函数,通过使用login_required装饰器来包装所有的URLcallback函数。 这在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 

使用它是这样工作的(由于yield要求list的调用)。

 urlpatterns = list(login_required_patterns('', url(r'^$', home_view))) 

你不能真正赢得这个。 您只需要声明授权要求即可。 除了视图函数之外,你还会在哪里放置这个声明?

考虑用可调用的对象replace你的视图函数。

 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() 

它不保存任何代码行。 这对“我们忘记”的问题没有帮助。 你所能做的就是检查代码,确保视图函数是对象。 正确的课程。

但即使如此,如果没有unit testing套件,您永远都不会知道每个视图函数都是正确的。

这是一个django 1.10+的中间件解决scheme

中间件必须以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 

安装

  1. 将代码复制到您的项目文件夹中,并保存为middleware.py
  2. 添加到MIDDLEWARE

    MIDDLEWARE = […'.middleware.RequireLoginMiddleware',#需要login]

  3. 添加到你的settings.py:

    LOGIN_REQUIRED_URLS =(r'(。 )',)LOGIN_REQUIRED_URLS_EXCEPTIONS =(r'/ admin /(。)$',)LOGIN_URL ='/ admin /'

资料来源:

  1. Daniel Naab 回答了这个问题

  2. Dj Goodo中间件教程 Max Goodridge

  3. Django中间件文档

将有可能有一个单一的起点,包括所有的urls和装饰它使用这个软件包https://github.com/vorujack/decorate_url