在Django中通过IP地址进行身份验证。

时间:2022-09-16 13:25:32

I have a small Django application with a view that I want to restrict to certain users. Anyone from a specific network should be able to see that view without any further authentication, based on IP address alone. Anyone else from outside this IP range should be asked for a password and authenticated against the default Django user management.

我有一个小的Django应用程序,它的视图要限制特定的用户。任何来自特定网络的人都应该能够看到这个视图,而不需要任何进一步的身份验证,仅基于IP地址。应该向这个IP范围之外的任何人询问密码,并对默认的Django用户管理进行身份验证。

I assume I have to write a custom authentication backend for that, but the documentation confuses me as the authenticate() function seems to expect a username/password combination or a token. It is not clear to me how to authenticate using IP addresses here.

我假设我必须为此编写一个自定义身份验证后端,但是文档使我感到困惑,因为authenticate()函数似乎期望用户名/密码组合或令牌。我不清楚如何使用IP地址进行身份验证。

What would be the proper way to implement IP address-based authentication in Django? I'd prefer to use as much existing library functions as possible for security-related code instead of writing all of it myself.

在Django中实现基于IP地址的身份验证的正确方法是什么?我宁愿使用尽可能多的现有库函数来编写与安全性相关的代码,而不是自己编写所有的代码。

6 个解决方案

#1


9  

There are two suitable approaches for that kind of authentication:

这种认证有两种合适的方法:

  • As Decorator: if some of views (but not many of them) requires this check, then it is better to write a decorator for that (something like @Jingo had written)
  • 作为Decorator:如果一些视图(但不是很多)需要这个检查,那么最好为此编写一个Decorator(类似于@Jingo))
  • As Middleware: if that check needed to be done by all (or many) views, instead of using a decorator, writing a middleware is a better solution.
  • 作为中间件:如果需要由所有(或多个)视图执行检查,而不是使用decorator,那么编写中间件是更好的解决方案。

A sample middleware can be something like:

一个示例中间件可以是这样的:

ALLOWED_IP_BLOCKS = [......]

class NeedToLoginMiddleware(object):
    def process_request(self, request):
        ip = request.META['REMOTE_ADDR']
        if not ip in ALLOWED_IP_BLOCKS: #ip check
            if not request.user.is_authenticated(): #if ip check failed, make authentication check
                return HttpResponseRedirect(...)
    return None
  • You can make ip check using a list, or a regex as @Jingo mentioned.
  • 您可以使用列表或@Jingo提到的regex进行ip检查。
  • If you are using django authentication and REMOTE_ADDR is not in ALLOWED_IP_BLOCKS list, then you can use is_authenticated to check if related user had logged in or not. But for using is_autheticated in a custom middleware, your custom middleware must be placed after AuthenticationMiddleware, because request.user is set on that level.

    如果您正在使用django身份验证,而REMOTE_ADDR不位于ALLOWED_IP_BLOCKS列表中,那么您可以使用is_authenticated来检查相关用户是否已登录。但是对于在自定义中间件中使用is_autheticated,您的自定义中间件必须放在AuthenticationMiddleware之后,因为请求。user设置在该级别上。

    MIDDLEWARE_CLASSES = (
        ...
        'django.contrib.auth.middleware.AuthenticationMiddleware',
        'path.to.my.NeedToLoginMiddleware',
        ...
    )
    
    • If a few views do not require this authentication, then you can make a list of exceptional urls and get the request url from request.path and check if the request url requires ip check/authentication.
    • 如果一些视图不需要此身份验证,那么您可以创建一个异常url列表,并从请求中获取请求url。路径并检查请求url是否需要ip检查/身份验证。

More info about custom middleware classes

更多关于自定义中间件类的信息

#2


7  

You can also write a small decorator for this purpose:

您还可以为此编写一个小的decorator:

def login_by_ip(view_func):
    def authorize(request, *args, **kwargs):
        user_ip = request.META['REMOTE_ADDR']
        for ip in allowedIps.allowedIps:
            authenticated_by_ip = re.compile(ip).match(user_ip)
            if authenticated_by_ip:
                return view_func(request, authenticated_by_ip, *args, **kwargs)
        return HttpResponseRedirect('/redirect/path/')
    return authorize

allowedIps is in my case a file (allowedIps.py) which stores the regexes for allowed IPs in a tuple like this:

allowedIps在我的例子中是一个文件(allowedIps.py),它将允许的IPs的regexes存储在这样一个元组中:

allowedIps = ('^XXX\.XXX\..+\..+$','^XXX\.XXX\.XXX\..+$', '^XXX\.XXX\.XXX\.XXX$')

Hope this can help or give an idea. Note: if you return authenticated_by_ip to the decorated view, your view will have to accept that parameter, you also can just ommit it, if you dont need it. You can also define the regexes more precisely to only accept digits up to three.

希望这能对您有所帮助或启发。注意:如果您将authenticated_by_ip返回到修饰的视图,那么您的视图将不得不接受这个参数,如果不需要,您也可以省略它。您还可以更精确地定义regexes,只接受最多为3的数字。

#3


3  

There's no need to write an authentication backend for the use case you have written. Writing an IP based dispatcher in the middleware layer will likely be sufficient

不需要为您编写的用例编写一个身份验证后端。在中间件层中编写基于IP的dispatcher可能是足够的。

If your app's url(s) is/are matched, process_request should check for an authenticated django user and match that user to a whitelist.

如果应用程序的url匹配,process_request应该检查经过身份验证的django用户,并将该用户与白名单匹配。

#4


3  

You can try this decorator. I have tested its working fine:

您可以尝试这个decorator。我测试过它的工作性能:

allowedIps = ['129.0.0.1', '127.0.0.1']
def allow_by_ip(view_func):
    def authorize(request, *args, **kwargs):
        user_ip = request.META['REMOTE_ADDR']
        for ip in allowedIps:
            if ip==user_ip:
                return view_func(request, *args, **kwargs)
        return HttpResponse('Invalid Ip Access!')
    return authorize

#5


2  

IMO, solving this with Django is fine if it's a small non performance critical site.

在我看来,用Django来解决这个问题是没问题的,如果它是一个小型的非性能关键站点的话。

It's better to keep the unauthorized users fully at bay using your Apache or Nginx service. For example, in Nginx I have these lines in my site configuration:

最好使用Apache或Nginx服务将未授权用户完全隔离。例如,在Nginx中,我的站点配置中有这些行:

include allowed_ips.conf;
deny all;
error_page 403 forbidden.html;

allowed_ips.conf is in /etc/nginx and looks (something) like this:

allowed_ips。conf在/etc/nginx中,看起来像这样:

allow 110.222.333.222;  # J Bloggs (sys admin)
allow 777.222.0.0/16;   # Government owned
...

I believe this is better because the relatively slow Django processes never get touched by the blocked IPs. This is significant if you are blocking bots or other country address ranges for performance or security reasons.

我认为这更好,因为相对缓慢的Django进程从未被阻塞的ip所影响。如果您出于性能或安全原因而阻塞机器人或其他国家地址范围,那么这一点非常重要。

#6


1  

def login_by_id(request):
    ip = request.META['REMOTE_ADDR']
    try: UserProfile.objects.get(allow_ip=id)
    except UserProfile.DoesNotExist: return HttpResponseRedirect('././')
    else:
        # auth here

You need allow_ip in you UserProfile model, which save on registration or changes on edit user page

您需要在您的UserProfile模型中使用allow_ip,该模型保存注册或编辑用户页面上的更改

#1


9  

There are two suitable approaches for that kind of authentication:

这种认证有两种合适的方法:

  • As Decorator: if some of views (but not many of them) requires this check, then it is better to write a decorator for that (something like @Jingo had written)
  • 作为Decorator:如果一些视图(但不是很多)需要这个检查,那么最好为此编写一个Decorator(类似于@Jingo))
  • As Middleware: if that check needed to be done by all (or many) views, instead of using a decorator, writing a middleware is a better solution.
  • 作为中间件:如果需要由所有(或多个)视图执行检查,而不是使用decorator,那么编写中间件是更好的解决方案。

A sample middleware can be something like:

一个示例中间件可以是这样的:

ALLOWED_IP_BLOCKS = [......]

class NeedToLoginMiddleware(object):
    def process_request(self, request):
        ip = request.META['REMOTE_ADDR']
        if not ip in ALLOWED_IP_BLOCKS: #ip check
            if not request.user.is_authenticated(): #if ip check failed, make authentication check
                return HttpResponseRedirect(...)
    return None
  • You can make ip check using a list, or a regex as @Jingo mentioned.
  • 您可以使用列表或@Jingo提到的regex进行ip检查。
  • If you are using django authentication and REMOTE_ADDR is not in ALLOWED_IP_BLOCKS list, then you can use is_authenticated to check if related user had logged in or not. But for using is_autheticated in a custom middleware, your custom middleware must be placed after AuthenticationMiddleware, because request.user is set on that level.

    如果您正在使用django身份验证,而REMOTE_ADDR不位于ALLOWED_IP_BLOCKS列表中,那么您可以使用is_authenticated来检查相关用户是否已登录。但是对于在自定义中间件中使用is_autheticated,您的自定义中间件必须放在AuthenticationMiddleware之后,因为请求。user设置在该级别上。

    MIDDLEWARE_CLASSES = (
        ...
        'django.contrib.auth.middleware.AuthenticationMiddleware',
        'path.to.my.NeedToLoginMiddleware',
        ...
    )
    
    • If a few views do not require this authentication, then you can make a list of exceptional urls and get the request url from request.path and check if the request url requires ip check/authentication.
    • 如果一些视图不需要此身份验证,那么您可以创建一个异常url列表,并从请求中获取请求url。路径并检查请求url是否需要ip检查/身份验证。

More info about custom middleware classes

更多关于自定义中间件类的信息

#2


7  

You can also write a small decorator for this purpose:

您还可以为此编写一个小的decorator:

def login_by_ip(view_func):
    def authorize(request, *args, **kwargs):
        user_ip = request.META['REMOTE_ADDR']
        for ip in allowedIps.allowedIps:
            authenticated_by_ip = re.compile(ip).match(user_ip)
            if authenticated_by_ip:
                return view_func(request, authenticated_by_ip, *args, **kwargs)
        return HttpResponseRedirect('/redirect/path/')
    return authorize

allowedIps is in my case a file (allowedIps.py) which stores the regexes for allowed IPs in a tuple like this:

allowedIps在我的例子中是一个文件(allowedIps.py),它将允许的IPs的regexes存储在这样一个元组中:

allowedIps = ('^XXX\.XXX\..+\..+$','^XXX\.XXX\.XXX\..+$', '^XXX\.XXX\.XXX\.XXX$')

Hope this can help or give an idea. Note: if you return authenticated_by_ip to the decorated view, your view will have to accept that parameter, you also can just ommit it, if you dont need it. You can also define the regexes more precisely to only accept digits up to three.

希望这能对您有所帮助或启发。注意:如果您将authenticated_by_ip返回到修饰的视图,那么您的视图将不得不接受这个参数,如果不需要,您也可以省略它。您还可以更精确地定义regexes,只接受最多为3的数字。

#3


3  

There's no need to write an authentication backend for the use case you have written. Writing an IP based dispatcher in the middleware layer will likely be sufficient

不需要为您编写的用例编写一个身份验证后端。在中间件层中编写基于IP的dispatcher可能是足够的。

If your app's url(s) is/are matched, process_request should check for an authenticated django user and match that user to a whitelist.

如果应用程序的url匹配,process_request应该检查经过身份验证的django用户,并将该用户与白名单匹配。

#4


3  

You can try this decorator. I have tested its working fine:

您可以尝试这个decorator。我测试过它的工作性能:

allowedIps = ['129.0.0.1', '127.0.0.1']
def allow_by_ip(view_func):
    def authorize(request, *args, **kwargs):
        user_ip = request.META['REMOTE_ADDR']
        for ip in allowedIps:
            if ip==user_ip:
                return view_func(request, *args, **kwargs)
        return HttpResponse('Invalid Ip Access!')
    return authorize

#5


2  

IMO, solving this with Django is fine if it's a small non performance critical site.

在我看来,用Django来解决这个问题是没问题的,如果它是一个小型的非性能关键站点的话。

It's better to keep the unauthorized users fully at bay using your Apache or Nginx service. For example, in Nginx I have these lines in my site configuration:

最好使用Apache或Nginx服务将未授权用户完全隔离。例如,在Nginx中,我的站点配置中有这些行:

include allowed_ips.conf;
deny all;
error_page 403 forbidden.html;

allowed_ips.conf is in /etc/nginx and looks (something) like this:

allowed_ips。conf在/etc/nginx中,看起来像这样:

allow 110.222.333.222;  # J Bloggs (sys admin)
allow 777.222.0.0/16;   # Government owned
...

I believe this is better because the relatively slow Django processes never get touched by the blocked IPs. This is significant if you are blocking bots or other country address ranges for performance or security reasons.

我认为这更好,因为相对缓慢的Django进程从未被阻塞的ip所影响。如果您出于性能或安全原因而阻塞机器人或其他国家地址范围,那么这一点非常重要。

#6


1  

def login_by_id(request):
    ip = request.META['REMOTE_ADDR']
    try: UserProfile.objects.get(allow_ip=id)
    except UserProfile.DoesNotExist: return HttpResponseRedirect('././')
    else:
        # auth here

You need allow_ip in you UserProfile model, which save on registration or changes on edit user page

您需要在您的UserProfile模型中使用allow_ip,该模型保存注册或编辑用户页面上的更改