You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何配置django-defender与django-two-factor-auth(django-otp)协同工作

解决方案:django-defender适配自定义OTP授权页及登录防护增强

一、让django-defender生效在自定义OTP页面

1. 先检查配置是否覆盖了自定义URL

django-defender默认只监控默认auth登录页,需要在settings.py中把自定义OTP页和two-factor第一步登录页的URL加入监控列表:

DEFENDER_LOGIN_FORM_URLS = [
    '/accounts/login/',  # 默认auth登录页
    '/your-custom-login/',  # two-factor第一步自定义登录页
    '/your-custom-otp-token/',  # 自定义OTP授权页
]

添加后,django-defender会自动监控这些URL的POST请求,统计登录失败次数。

2. 手动给OTP验证视图添加@watch_login装饰器

如果配置后仍不生效,需要重写django-two-factor-auth的OTP验证视图(或你的自定义视图),给POST方法加上装饰器:

# 在你的app/views.py中
from two_factor.views import TokenView
from defender.decorators import watch_login

class CustomOTPTokenView(TokenView):
    @watch_login
    def post(self, request, *args, **kwargs):
        # 调用父类的验证逻辑
        response = super().post(request, *args, **kwargs)
        # 装饰器会自动识别登录失败状态(比如返回403/错误页面)
        return response

然后在urls.py中替换原有的OTP视图路由:

from django.urls import path
from .views import CustomOTPTokenView

urlpatterns = [
    # 替换原来的two-factor token路由
    path('two-factor/token/', CustomOTPTokenView.as_view(), name='two_factor_token'),
    # 其他路由...
]

同理,如果two-factor第一步的标准登录页也需要django-defender防护,重写LoginView并添加装饰器:

from two_factor.views import LoginView as TwoFactorLoginView
from defender.decorators import watch_login

class CustomTwoFactorLoginView(TwoFactorLoginView):
    @watch_login
    def post(self, request, *args, **kwargs):
        return super().post(request, *args, **kwargs)

再更新对应的登录路由即可。

二、替代方案:无需django-defender的登录防护增强

如果django-defender适配麻烦,可以结合django-two-factor-auth自带的OTP锁定,给第一步登录页添加额外防护:

1. 使用django-ratelimit限制登录尝试频率

安装django-ratelimit后,给登录视图添加频率限制:

from ratelimit.decorators import ratelimit
from two_factor.views import LoginView as TwoFactorLoginView

class RateLimitedLoginView(TwoFactorLoginView):
    # 限制同一IP/用户名每分钟最多5次尝试,超过则拦截
    @ratelimit(key='username_or_ip', rate='5/m', method='POST', block=True)
    def post(self, request, *args, **kwargs):
        return super().post(request, *args, **kwargs)

2. 手动实现失败次数锁定

用Django缓存记录用户登录失败次数,达到阈值则锁定:

from django.core.cache import cache
from two_factor.views import LoginView as TwoFactorLoginView

class LockableLoginView(TwoFactorLoginView):
    MAX_FAILED_ATTEMPTS = 5
    LOCK_DURATION = 900  # 锁定15分钟(秒)

    def post(self, request, *args, **kwargs):
        username = request.POST.get('username')
        if username:
            cache_key = f'login_fail_{username}'
            failed_attempts = cache.get(cache_key, 0)
            
            if failed_attempts >= self.MAX_FAILED_ATTEMPTS:
                # 返回锁定提示
                return self.render_to_response(self.get_context_data(error='登录尝试过多,请15分钟后再试'))
            
            # 执行父类登录逻辑
            response = super().post(request, *args, **kwargs)
            
            # 如果登录失败,增加失败次数
            if 'error' in self.get_context_data():
                cache.set(cache_key, failed_attempts + 1, self.LOCK_DURATION)
            else:
                # 登录成功,清除失败记录
                cache.delete(cache_key)
            
            return response
        return super().post(request, *args, **kwargs)

内容的提问来源于stack exchange,提问作者Szade

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.02 04:31:16