Python/Django - HTTP认证

1 投票
2 回答
1387 浏览
提问于 2025-04-18 07:55

我想在我的代码中加入基本的HTTP认证,前提是满足某个条件。这个条件是一个请求参数。为了简单起见,我们假设我想在Django的视图中实现这个功能。

我知道在PHP中怎么做。下面这段代码正好实现了我想要的功能:

if (isset($_REQUEST['key']) && $_REQUEST['key'] === 'value') {
    if (!isset($_SERVER['PHP_AUTH_USER'])) {
        header('WWW-Authenticate: Basic realm="My Realm"');
        header('HTTP/1.0 401 Unauthorized');
        echo 'Text to send if user hits Cancel button';
        exit;
    } else {
        echo "<p>Hello {$_SERVER['PHP_AUTH_USER']}.</p>";
        echo "<p>You entered {$_SERVER['PHP_AUTH_PW']} as your password.</p>";
    }
}

我可以把这段代码放在PHP脚本的任何地方,当执行到这些行时,就会要求进行认证。如果认证失败,脚本执行会停止,并且会发送一个401 Unauthorized的响应,带上指定的消息。这就是我在Python/Django中想要的。下面是一段代码,帮我填充一下:

def some_view(request):
    if request.REQUEST.get('key') == 'value':
        # FILL ME IN

我想要的功能:

  • 能够把这段代码放在代码的任何地方,并要求用户进行认证(假设我可以使用请求变量,以及一个django.http.response.HttpResponse实例作为响应)
  • 能够指定在什么条件下会要求进行认证(比如请求中是否有某个参数)
  • 如果用户提供了无效的凭证,能够停止进一步的执行,并返回401 Unauthorized的响应
  • 这个解决方案要便于移植(可以复制/粘贴这段代码到其他地方,一旦执行到这里就会触发认证)

我不想要的功能:

  • 在调用一个函数时硬编码认证要求(使用装饰器)
  • 更改我的web应用的任何全局设置
  • 更改我希望触发认证的代码以外的任何内容
  • 需要安装任何额外的包来实现这个功能
  • 与应用中已经设置的CMS访问的标准认证共享凭证

2 个回答

1

我会使用Django的类视图中的一个混合类来实现这个功能。

class AuthenticationMixin(object):
    def dispatch(self, request, *args, **kwargs):
        if not request.user.is_authenticated:
            return HttpResponse('text, test, text', status=401)
        return super(AuthenticationMixin, self).dispatch(request, *args, **kwargs)

然后你可以在视图中这样使用它:

class MyView(AuthenticationMixin, DetailView):
.
.
.

如果有一个视图你不想使用认证,只需要在类声明中不继承认证的混合类就可以了。

class MyUnauthenticatedView(DetailView):
.
.
.
2
from django.http import HttpResponse

def some_view(request):
    if request.REQUEST.get('key') == 'value':
        if not request.user.is_authenticated():
            response = HttpResponse('Text to send if user hits Cancel button', status=401)
            response['WWW-Authenticate'] = 'Basic realm="My Realm'
            return response
        ...

这会返回一个 401 状态码,但你需要自己提供内容或模板。如果你可以接受返回 403 状态码的话,可以使用内置的 PermissionDenied 异常,这样会根据你的 '403.html' 模板返回一个403页面:

from django.core.exceptions import PermissionDenied

def some_view(request):
    if request.REQUEST.get('key') == 'value':
        if not request.user.is_authenticated():
            raise PermissionDenied
        ...

编辑:

要实现HTTP基本认证,你需要设置你的网页服务器(比如Apache、nginx,或者你用的其他服务器)来处理这个功能。你应该查看你网页服务器的文档,了解如何进行设置。

Django提供了一个 RemoteUserBackend,允许用户使用基本认证登录,但这和普通的 ModelBackend 使用的是同一个用户模型,所以它们并不是分开的。幸运的是,中间件后端 都相对简单。网页服务器会拒绝任何凭证无效的请求,所以每当 'REMOTE_USER' 这个头(或者你使用的其他头)被设置时,用户就会被正确认证。这个头会在 request.META 中可用,因此要检查用户是否通过基本认证登录,你可以使用以下代码:

from django.http import HttpResponse

def some_view(request):
    if request.REQUEST.get('key') == 'value':
        if request.META.get('REMOTE_USER', None):
            do_something()
        else:
            response = HttpResponse('Text to send if user hits Cancel button', status=401)
            response['WWW-Authenticate'] = 'Basic realm="My Realm'
            return response
        ...

撰写回答