如何不使用Django的管理员登录视图?

29

我创建了自己的登录视图。但是,如果用户直接访问 /admin,则会将其带到管理员登录页面,而不使用我的自定义视图。如何使其重定向到用于除 /admin 以外的所有内容的登录视图?


4
艾萨克的回答对我完全有效。你应该接受他的答案! :) - Dylan Klomparens
10个回答

60

http://djangosnippets.org/snippets/2127/获得——使用login_required包装管理员登录页面。例如,在urls.py中:

from django.contrib.auth.decorators import login_required
from django.contrib import admin
admin.autodiscover()
admin.site.login = login_required(admin.site.login)

你可能已经有了中间两行甚至第一行;添加第四行将导致任何将要触发admin.site.login函数的内容重定向到你的LOGIN_URL并带上适当的next参数。


14
假设我已经以非员工用户的身份登录。如果我访问 /admin/,我仍会看到默认的登录表单,因为 login_required 没有触发。 - Pēteris Caune
我在我的回答中解决了@PēterisCaune的问题。 - Tatsuyuki Ishi

10

虽然 @Isaac 的解决方案应该可以拒绝大部分恶意机器人,但它并不能保护专业渗透。当已登录用户尝试登录到管理员时,会收到以下消息:

Django re-login prompt

我们应该改用管理员修饰符来拒绝所有非特权用户:

from django.contrib.admin.views.decorators import staff_member_required
from django.contrib import admin
[ ... ]
admin.site.login = staff_member_required(admin.site.login, login_url=settings.LOGIN_URL)

据我所知,这个装饰器是在1.9版本中添加的。


2
完全有可能(很可能)是我在做一些愚蠢的事情,但当我这样做时,我最终会出现一个重定向循环,其中“/admin/login/”重定向到“/admin/login/?next=/admin/login/”,然后重定向到“/admin/login?next=/admin/login/%3Fnext%3D/admin/login/”等。 - Isaac
3
在这里需要传递login_url参数: admin.site.login = staff_member_required(admin.site.login, login_url=settings.LOGIN_URL) - blueyed
楼主,您介意编辑您的答案以包含@blueyed提供的信息以避免重定向吗? - dangel
我想补充一下,我已经花了几个小时寻找解决方案,只有你的答案实现了我想做的事情 - 非常感谢。 - 只是想知道,是否有一种方法可以将非员工或未经身份验证的用户重定向到例如404页面,而不是登录页面? - Daniel

9
我发现上面的答案没有正确地处理“next”查询参数。
解决这个问题的简单方法是使用一个简单的重定向。在您网站的urls文件中,在包含管理员urls之前,立即添加以下代码行:
   url(r'^admin/login$', RedirectView.as_view(pattern_name='my_login_page', permanent=True, query_string=True))

5
更好的做法是,url(r'^admin/login/$', RedirectView.as_view(url=settings.LOGIN_URL, permanent=True, query_string=True)), - Dan
请注意,如果 my_login_page 实现了 ?next 重定向并且重定向已经登录的用户,则会导致重定向循环。因此,my_login_page 处理该情况非常重要,允许已经登录的用户通过。 - Bufke

5

Holá
我找到了一个非常简单的解决方法。
只需要告诉django,管理员登录的url由您自己的登录视图来处理即可。

您只需要修改项目的urls.py文件(请注意,不是应用程序的urls.py文件)

  1. 在PROJECT文件夹中找到urls.py文件。
  2. 在导入部分添加以下行
    from your_app_name import views
  3. 找到这一行
    url(r'^admin/', include(admin.site.urls))
  4. 在该行上方添加以下内容
    url(r'^admin/login/', views.your_login_view),

这是一个示例

    from django.conf.urls import include, url
    from django.contrib import admin

    from your_app import views

    urlpatterns = [
        url(r'^your_app_start/', include('your_app.urls',namespace="your_app_name")),

        url(r'^admin/login/', views.your_app_login),
        url(r'^admin/', include(admin.site.urls)),
    ]

3

我也遇到了同样的问题,试图使用被接受的答案,但是像上面的评论所指出的一样,存在相同的问题。 然后我做了一些不同的事情,如果对某人有帮助,这里把它粘贴出来。

def staff_or_404(u):
    if u.is_active:
        if u.is_staff:
            return True
        raise Http404()
    return False

admin.site.login = user_passes_test(
        staff_or_404,
    )(admin.site.login)

这个想法是,如果用户已经登录并尝试访问管理员页面,则会显示404错误。否则,它会强制您跳转到普通的登录页面(除非您已经登录)。


3

http://blog.montylounge.com/2009/07/5/customizing-django-admin-branding/ (web archive)

我正在尝试解决这个问题,我在这位博主的博客上找到了解决方案。基本上,覆盖管理模板并使用自己的模板即可。简而言之,在/path-to-project/templates/admin/中创建一个名为login.html的文件,它将替换管理员登录页面。您可以复制原始文件(django/contrib/admin/templates/login.html)并修改一两行。如果您想完全放弃默认的登录页面,可以像这样操作:

{% extends "my-login-page.html" %}

就是这样。一个文件中只有一行代码。Django真是太棒了。


2
在您的ROOT_URLCONF文件中(默认情况下,它在项目的根文件夹中的urls.py文件中),是否有这样一行代码:
urlpatterns = patterns('',
...
    (r'^admin/', include(admin.site.urls)),
...
)

如果是这样,你需要用你创建的自定义视图替换include(admin.site.urls):
(r'^admin/', 'myapp.views.myloginview'),

或者如果您的应用程序有自己的urls.py,您可以像这样包含它:
(r'^admin/', include(myapp.urls)),

为了澄清,我想继续使用管理员,只是不想使用登录管理员视图。通过替换include(admin.site.urls)),我无法再访问/admin。 - Bufke
所以你想通过与登录系统的其他部分相同的登录视图登录到管理员系统?是什么机制让视图知道用户登录时想要常规登录还是管理员登录?显然,如果用户没有管理员凭据,他们无法进行管理员登录,但是如果他们有管理员凭据,他们想要将站点显示为已登录用户还是已登录管理员呢? - sandinmyjoints
在我的情况下,管理员用户永远不需要访问站点的非管理员部分。因此,我的视图根据用户类型重定向他们。所以,是的,我希望他们看到与非管理员用户相同的登录界面。如果他们去到 /,那么这已经发生了,但是如果他们在 /admin 中收藏了某些内容,它会将他们发送到管理员的登录界面,这就是我不想发生的事情。 - Bufke
是的,我希望所有的登录代码都由同一个视图处理,因为我有一些自定义代码处理Google应用程序身份验证。有时它需要显示除登录成功或失败之外的特殊消息。 - Bufke
好的,现在我理解了你的情况,我可以说这有点超出了我的知识水平。我可以看到admin.site.urls是在django/trunk/django/contribu/admin/sites.py中创建的,我想知道你是否能够在那里查找并找出更改与管理视图相关联的登录页面的方法。希望我能提供更多帮助! - sandinmyjoints
显示剩余2条评论

1

这是我的解决方案,使用了自定义的AdminSite类

class AdminSite(admin.AdminSite):

    def _is_login_redirect(self, response):
        if isinstance(response, HttpResponseRedirect):
            login_url = reverse('admin:login', current_app=self.name)
            response_url = urllib.parse.urlparse(response.url).path
            return login_url == response_url
        else:
            return False

    def admin_view(self, view, cacheable=False):
        inner = super().admin_view(view, cacheable)

        def wrapper(request, *args, **kwargs):
            response = inner(request, *args, **kwargs)
            if self._is_login_redirect(response):
                if request.user.is_authenticated():
                    return HttpResponseRedirect(settings.LOGIN_REDIRECT_URL)
                else:
                    return redirect_to_login(request.get_full_path(), reverse('accounts_login'))
            else:
                return response

        return wrapper

0
您可以将管理员登录网址重定向到授权登录视图:
from django.contrib import admin
from django.urls import path, include

urlpatterns = [
    path('', include('your_app.urls')),
    path('accounts/', include('django.contrib.auth.urls')),
    path('admin/login/', RedirectView.as_view(url='/accounts/login/?next=/admin/', permanent=True)),
    path('admin/', admin.site.urls),
]

0
截至2020年8月,django.contrib.admin.sites.AdminSite有一个login_template属性。因此,您可以只是子类化AdminSite并指定自定义模板,例如:
class MyAdminSite(AdminSite):
    login_template = 'my_login_template.html'

my_admin_site = MyAdminSite()

然后只需在所有地方使用my_admin_site,而不是admin.site


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接