Python 是否有可能实施;下次登录时更改密码“;在django管理员中键入功能?

Python 是否有可能实施;下次登录时更改密码“;在django管理员中键入功能?,python,django,Python,Django,我希望能够在用户设置中设置一个选项,强制他们在下次登录管理界面时更改密码。这可能吗?它将如何实施?我现在正在使用默认的auth模型,但并不反对修改或更改它。感谢您的帮助。来自以下网站上的一个线程: 这并不理想,但应该能奏效 (或促使某人求婚 更好的东西) 为用户添加一对一的表, 包含首字母的字段 密码(当然是加密的,所以 看起来像是中的密码 auth_user表) 当用户登录时,进行登录 页面检查密码是否正确 匹配。如果有,请重定向到 密码更改页面,而不是 正常重定向页面 实际上我自己也在做这件

我希望能够在用户设置中设置一个选项,强制他们在下次登录管理界面时更改密码。这可能吗?它将如何实施?我现在正在使用默认的auth模型,但并不反对修改或更改它。感谢您的帮助。

来自以下网站上的一个线程:

这并不理想,但应该能奏效 (或促使某人求婚 更好的东西)

为用户添加一对一的表, 包含首字母的字段 密码(当然是加密的,所以 看起来像是中的密码
auth_user
表)

当用户登录时,进行登录 页面检查密码是否正确 匹配。如果有,请重定向到 密码更改页面,而不是 正常重定向页面


实际上我自己也在做这件事。您需要三个组件:用户配置文件(如果尚未在站点上使用)、中间件组件和预保存信号

我的代码在一个名为“帐户”的应用程序中

# myproject/accounts/models.py

from django.db import models
from django.db.models import signals
from django.contrib.auth.models import User

class UserProfile(models.Model):
    user = models.ForeignKey(User, unique=True)
    force_password_change = models.BooleanField(default=False)

def create_user_profile_signal(sender, instance, created, **kwargs):
    if created:
        UserProfile.objects.create(user=instance)

def password_change_signal(sender, instance, **kwargs):
    try:
        user = User.objects.get(username=instance.username)
        if not user.password == instance.password:
          profile = user.get_profile()
          profile.force_password_change = False
          profile.save()
    except User.DoesNotExist:
        pass

signals.pre_save.connect(password_change_signal, sender=User, dispatch_uid='accounts.models')

signals.post_save.connect(create_user_profile_signal, sender=User, dispatch_uid='accounts.models')
首先,我们创建一个UserProfile,其中包含一个外键。正如其名称所述,
force\u password\u change
布尔值将在您想要强制用户更改密码时设置为true。你可以在这里做任何事。在我的组织中,我们还选择每隔90天实施一次强制更改,因此我还有一个DateTime字段,用于存储用户上次更改密码的时间。然后在预保存信号中设置,
password\u changed\u信号

其次,我们有
create\u user\u profile\u信号
。这主要是为了完整性而添加的。如果您刚刚将用户配置文件添加到项目中,则需要一个post_save信号,该信号将在每次创建用户时创建用户配置文件。这就完成了这项任务

第三,我们改变了密码信号。这是一个预保存信号,因为在此过程中,用户表中的实际行尚未更新。因此,我们可以访问以前的密码和即将保存的新密码。如果两者不匹配,则表示用户更改了密码,然后我们可以重置强制密码更改布尔值。这将是一个关键点,在这里您还需要处理您添加的任何其他事情,例如设置前面提到的DateTimeField

最后两行将这两个功能附加到相应的信号上

如果还没有,您还需要将以下行添加到项目的
设置.py
(更改应用程序标签和型号名称以匹配您的设置):

这涵盖了基本内容。现在我们需要一个中间件组件来检查
force\u password\u change
标志的状态(以及任何其他必要的检查)

这个非常简单的中间件连接到页面加载过程的
process\u request
阶段。它检查1)用户是否已经登录,2)他们正在尝试访问管理员中的某个页面,3)他们正在访问的页面不是密码更改页面本身(否则,您将得到无限的重定向循环)。如果所有这些都为真,并且
force\u password\u change
标志已设置为
true
,则用户将重定向到密码更改页面。在更改密码(触发前面讨论过的pre_save信号)之前,他们将无法导航到其他任何地方

最后,您只需将此中间件添加到项目的
设置.py
(同样,根据需要更改导入路径):


我使用了Chris Pratt的解决方案,只是做了一点小小的改动:我没有使用中间件,而是对每个页面执行中间件,并使用相应的资源,我想我只是截取登录视图

在my urls.py中,我已将以下内容添加到我的URL模式中:

url(r'^accounts/login/$', 'userbase.views.force_pwd_login'),
然后,我将以下内容添加到userbase.views:

def force_pwd_login(request, *args, **kwargs):
    response = auth_views.login(request, *args, **kwargs)
    if response.status_code == 302:
        #We have a user
        try:
            if request.user.get_profile().force_password_change:
                return redirect('django.contrib.auth.views.password_change')
        except AttributeError: #No profile?
            pass
    return response

它在Django 1.2上似乎工作得很完美,但我没有理由相信1.3+会有问题。

基于会话检查这个简单的包(用Django 1.8测试)

在myapp.views.py中创建自定义视图

class PassWordReset(admin.AdminSite):

    def login(self, request, extra_context=None):
        if request.method == 'POST':
            response = super(PassWordReset, self).login(request, extra_context=extra_context)
            if response.status_code == 302 and request.user.is_authenticated():
                if not "fpr" in request.session or request.session['fpr']:
                    request.session['fpr'] = True
                    return HttpResponseRedirect("/admin/password_change/")
            return response
        return super(PassWordReset, self).login(request, extra_context=extra_context)

    def password_change(self, request, extra_context=None):
        if request.method == 'POST':
            response = super(PassWordReset, self).password_change(request, extra_context=extra_context)
            if response.status_code == 302 and request.user.is_authenticated():
                request.session['fpr'] = False
            return response
        return super(PassWordReset, self).password_change(request, extra_context=extra_context)


pfr_login = PassWordReset().login
pfr_password_change = PassWordReset().admin_view(PassWordReset().password_change, cacheable=True)
from myapp.views import pfr_password_change, pfr_login
urlpatterns = [
    ......
    url(r'^admin/login/$', pfr_login),
    url(r'^admin/password_change/$', pfr_password_change),
    url(r'^admin/', admin.site.urls),
    ....
]
class FPRCheck(object):
    def process_request(self, request):
        if request.user.is_authenticated() \
                and re.match(r'^/admin/?', request.path) \
                and (not "fpr" in request.session or ("fpr" in request.session and request.session['fpr'])) \
                and not re.match(r"/admin/password_change|/admin/logout", request.path):
            return HttpResponseRedirect("/admin/password_change/")
class Users(AbstractUser):
    default_pwd_updated = models.NullBooleanField(default=None, editable=False)
    pwd_update_time = models.DateTimeField(editable=False, null=True, default=None)  # reserved column to support further interval password (such as 60 days) update policy

     def set_password(self, raw_password):
        if self.default_pwd_updated is None:
            self.default_pwd_updated = False
        elif not self.default_pwd_updated:
            self.default_pwd_updated = True
            self.pwd_update_time = timezone.now()
        else:
            self.pwd_update_time = timezone.now()
        super().set_password(raw_password)
AUTH_USER_MODEL = 'newuser.Users'
from django.contrib.admin import AdminSite
from django.contrib.auth.views import LoginView
from django.utils.translation import gettext as _, gettext_lazy
from django.urls import reverse
from django.views.decorators.cache import never_cache
from django.contrib.auth import REDIRECT_FIELD_NAME
from django.http import HttpResponseRedirect


class NewLoginView(LoginView):
    def get_redirect_url(self):
        if self.request.method == "POST" and self.request.user.get_username()\
                and not self.request.user.default_pwd_updated:
            redirect_to = reverse("admin:password_change")
        else:
            redirect_to = self.request.POST.get(
                self.redirect_field_name,
                self.request.GET.get(self.redirect_field_name, '')
            )
        return redirect_to


class NewAdminSite(AdminSite):
    site_header = site_title = gettext_lazy("Customized Admin Site")

    def __init__(self, name="admin"):
        super().__init__(name)

    @never_cache
    def login(self, request, extra_context=None):
        """
        Display the login form for the given HttpRequest.
        """
        if request.method == 'GET' and self.has_permission(request):
            # Already logged-in, redirect to admin index
            if request.user.get_username() and not request.user.default_pwd_updated:
                # default password not changed, force to password_change view
                path = reverse('admin:password_change', current_app=self.name)
            else:
                path = reverse('admin:index', current_app=self.name)
            return HttpResponseRedirect(path)

        from django.contrib.auth.views import LoginView
        from django.contrib.admin.forms import AdminAuthenticationForm
        context = {
            **self.each_context(request),
            'title': _('Log in'),
            'app_path': request.get_full_path(),
            'username': request.user.get_username(),
        }
        if (REDIRECT_FIELD_NAME not in request.GET and
                REDIRECT_FIELD_NAME not in request.POST):
            context[REDIRECT_FIELD_NAME] = reverse('admin:index', current_app=self.name)
        context.update(extra_context or {})

        defaults = {
            'extra_context': context,
            'authentication_form': self.login_form or AdminAuthenticationForm,
            'template_name': self.login_template or 'admin/login.html',
        }
        request.current_app = self.name
        return NewLoginView.as_view(**defaults)(request) # use NewLoginView

    @never_cache
    def index(self, request, extra_context=None):
        if request.user.get_username() and not request.user.default_pwd_updated:
            # if default password not updated, force to password_change page
            context = self.each_context(request)
            context.update(extra_context or {})
            return self.password_change(request, context)
        return super().index(request, extra_context)


admin_site = NewAdminSite(name="admin")
def using_default_password(self, request):
    if self.has_permission(request) and request.user.get_username() and not request.user.default_pwd_updated:
            return True
    return False

def each_context(self, request):
    context = super().each_context(request)
    context["force_pwd_change"] = self.using_default_password(request)
    return context
然后在project/url.py中

class PassWordReset(admin.AdminSite):

    def login(self, request, extra_context=None):
        if request.method == 'POST':
            response = super(PassWordReset, self).login(request, extra_context=extra_context)
            if response.status_code == 302 and request.user.is_authenticated():
                if not "fpr" in request.session or request.session['fpr']:
                    request.session['fpr'] = True
                    return HttpResponseRedirect("/admin/password_change/")
            return response
        return super(PassWordReset, self).login(request, extra_context=extra_context)

    def password_change(self, request, extra_context=None):
        if request.method == 'POST':
            response = super(PassWordReset, self).password_change(request, extra_context=extra_context)
            if response.status_code == 302 and request.user.is_authenticated():
                request.session['fpr'] = False
            return response
        return super(PassWordReset, self).password_change(request, extra_context=extra_context)


pfr_login = PassWordReset().login
pfr_password_change = PassWordReset().admin_view(PassWordReset().password_change, cacheable=True)
from myapp.views import pfr_password_change, pfr_login
urlpatterns = [
    ......
    url(r'^admin/login/$', pfr_login),
    url(r'^admin/password_change/$', pfr_password_change),
    url(r'^admin/', admin.site.urls),
    ....
]
class FPRCheck(object):
    def process_request(self, request):
        if request.user.is_authenticated() \
                and re.match(r'^/admin/?', request.path) \
                and (not "fpr" in request.session or ("fpr" in request.session and request.session['fpr'])) \
                and not re.match(r"/admin/password_change|/admin/logout", request.path):
            return HttpResponseRedirect("/admin/password_change/")
class Users(AbstractUser):
    default_pwd_updated = models.NullBooleanField(default=None, editable=False)
    pwd_update_time = models.DateTimeField(editable=False, null=True, default=None)  # reserved column to support further interval password (such as 60 days) update policy

     def set_password(self, raw_password):
        if self.default_pwd_updated is None:
            self.default_pwd_updated = False
        elif not self.default_pwd_updated:
            self.default_pwd_updated = True
            self.pwd_update_time = timezone.now()
        else:
            self.pwd_update_time = timezone.now()
        super().set_password(raw_password)
AUTH_USER_MODEL = 'newuser.Users'
from django.contrib.admin import AdminSite
from django.contrib.auth.views import LoginView
from django.utils.translation import gettext as _, gettext_lazy
from django.urls import reverse
from django.views.decorators.cache import never_cache
from django.contrib.auth import REDIRECT_FIELD_NAME
from django.http import HttpResponseRedirect


class NewLoginView(LoginView):
    def get_redirect_url(self):
        if self.request.method == "POST" and self.request.user.get_username()\
                and not self.request.user.default_pwd_updated:
            redirect_to = reverse("admin:password_change")
        else:
            redirect_to = self.request.POST.get(
                self.redirect_field_name,
                self.request.GET.get(self.redirect_field_name, '')
            )
        return redirect_to


class NewAdminSite(AdminSite):
    site_header = site_title = gettext_lazy("Customized Admin Site")

    def __init__(self, name="admin"):
        super().__init__(name)

    @never_cache
    def login(self, request, extra_context=None):
        """
        Display the login form for the given HttpRequest.
        """
        if request.method == 'GET' and self.has_permission(request):
            # Already logged-in, redirect to admin index
            if request.user.get_username() and not request.user.default_pwd_updated:
                # default password not changed, force to password_change view
                path = reverse('admin:password_change', current_app=self.name)
            else:
                path = reverse('admin:index', current_app=self.name)
            return HttpResponseRedirect(path)

        from django.contrib.auth.views import LoginView
        from django.contrib.admin.forms import AdminAuthenticationForm
        context = {
            **self.each_context(request),
            'title': _('Log in'),
            'app_path': request.get_full_path(),
            'username': request.user.get_username(),
        }
        if (REDIRECT_FIELD_NAME not in request.GET and
                REDIRECT_FIELD_NAME not in request.POST):
            context[REDIRECT_FIELD_NAME] = reverse('admin:index', current_app=self.name)
        context.update(extra_context or {})

        defaults = {
            'extra_context': context,
            'authentication_form': self.login_form or AdminAuthenticationForm,
            'template_name': self.login_template or 'admin/login.html',
        }
        request.current_app = self.name
        return NewLoginView.as_view(**defaults)(request) # use NewLoginView

    @never_cache
    def index(self, request, extra_context=None):
        if request.user.get_username() and not request.user.default_pwd_updated:
            # if default password not updated, force to password_change page
            context = self.each_context(request)
            context.update(extra_context or {})
            return self.password_change(request, context)
        return super().index(request, extra_context)


admin_site = NewAdminSite(name="admin")
def using_default_password(self, request):
    if self.has_permission(request) and request.user.get_username() and not request.user.default_pwd_updated:
            return True
    return False

def each_context(self, request):
    context = super().each_context(request)
    context["force_pwd_change"] = self.using_default_password(request)
    return context
然后添加此中间件myapp/middleware.py

class PassWordReset(admin.AdminSite):

    def login(self, request, extra_context=None):
        if request.method == 'POST':
            response = super(PassWordReset, self).login(request, extra_context=extra_context)
            if response.status_code == 302 and request.user.is_authenticated():
                if not "fpr" in request.session or request.session['fpr']:
                    request.session['fpr'] = True
                    return HttpResponseRedirect("/admin/password_change/")
            return response
        return super(PassWordReset, self).login(request, extra_context=extra_context)

    def password_change(self, request, extra_context=None):
        if request.method == 'POST':
            response = super(PassWordReset, self).password_change(request, extra_context=extra_context)
            if response.status_code == 302 and request.user.is_authenticated():
                request.session['fpr'] = False
            return response
        return super(PassWordReset, self).password_change(request, extra_context=extra_context)


pfr_login = PassWordReset().login
pfr_password_change = PassWordReset().admin_view(PassWordReset().password_change, cacheable=True)
from myapp.views import pfr_password_change, pfr_login
urlpatterns = [
    ......
    url(r'^admin/login/$', pfr_login),
    url(r'^admin/password_change/$', pfr_password_change),
    url(r'^admin/', admin.site.urls),
    ....
]
class FPRCheck(object):
    def process_request(self, request):
        if request.user.is_authenticated() \
                and re.match(r'^/admin/?', request.path) \
                and (not "fpr" in request.session or ("fpr" in request.session and request.session['fpr'])) \
                and not re.match(r"/admin/password_change|/admin/logout", request.path):
            return HttpResponseRedirect("/admin/password_change/")
class Users(AbstractUser):
    default_pwd_updated = models.NullBooleanField(default=None, editable=False)
    pwd_update_time = models.DateTimeField(editable=False, null=True, default=None)  # reserved column to support further interval password (such as 60 days) update policy

     def set_password(self, raw_password):
        if self.default_pwd_updated is None:
            self.default_pwd_updated = False
        elif not self.default_pwd_updated:
            self.default_pwd_updated = True
            self.pwd_update_time = timezone.now()
        else:
            self.pwd_update_time = timezone.now()
        super().set_password(raw_password)
AUTH_USER_MODEL = 'newuser.Users'
from django.contrib.admin import AdminSite
from django.contrib.auth.views import LoginView
from django.utils.translation import gettext as _, gettext_lazy
from django.urls import reverse
from django.views.decorators.cache import never_cache
from django.contrib.auth import REDIRECT_FIELD_NAME
from django.http import HttpResponseRedirect


class NewLoginView(LoginView):
    def get_redirect_url(self):
        if self.request.method == "POST" and self.request.user.get_username()\
                and not self.request.user.default_pwd_updated:
            redirect_to = reverse("admin:password_change")
        else:
            redirect_to = self.request.POST.get(
                self.redirect_field_name,
                self.request.GET.get(self.redirect_field_name, '')
            )
        return redirect_to


class NewAdminSite(AdminSite):
    site_header = site_title = gettext_lazy("Customized Admin Site")

    def __init__(self, name="admin"):
        super().__init__(name)

    @never_cache
    def login(self, request, extra_context=None):
        """
        Display the login form for the given HttpRequest.
        """
        if request.method == 'GET' and self.has_permission(request):
            # Already logged-in, redirect to admin index
            if request.user.get_username() and not request.user.default_pwd_updated:
                # default password not changed, force to password_change view
                path = reverse('admin:password_change', current_app=self.name)
            else:
                path = reverse('admin:index', current_app=self.name)
            return HttpResponseRedirect(path)

        from django.contrib.auth.views import LoginView
        from django.contrib.admin.forms import AdminAuthenticationForm
        context = {
            **self.each_context(request),
            'title': _('Log in'),
            'app_path': request.get_full_path(),
            'username': request.user.get_username(),
        }
        if (REDIRECT_FIELD_NAME not in request.GET and
                REDIRECT_FIELD_NAME not in request.POST):
            context[REDIRECT_FIELD_NAME] = reverse('admin:index', current_app=self.name)
        context.update(extra_context or {})

        defaults = {
            'extra_context': context,
            'authentication_form': self.login_form or AdminAuthenticationForm,
            'template_name': self.login_template or 'admin/login.html',
        }
        request.current_app = self.name
        return NewLoginView.as_view(**defaults)(request) # use NewLoginView

    @never_cache
    def index(self, request, extra_context=None):
        if request.user.get_username() and not request.user.default_pwd_updated:
            # if default password not updated, force to password_change page
            context = self.each_context(request)
            context.update(extra_context or {})
            return self.password_change(request, context)
        return super().index(request, extra_context)


admin_site = NewAdminSite(name="admin")
def using_default_password(self, request):
    if self.has_permission(request) and request.user.get_username() and not request.user.default_pwd_updated:
            return True
    return False

def each_context(self, request):
    context = super().each_context(request)
    context["force_pwd_change"] = self.using_default_password(request)
    return context
中间件的顺序

MIDDLEWARE_CLASSES = [
    ....

    'myapp.middleware.FPRCheck'
    ]
注意

MIDDLEWARE_CLASSES = [
    ....

    'myapp.middleware.FPRCheck'
    ]
  • 这将不需要任何额外的型号
  • 也可以使用任何会话引擎
  • 中间件内部没有数据库查询

这是我在Django 1.11中使用的中间件:

# myproject/accounts/middleware.py

from django.http import HttpResponseRedirect
from django.urls import reverse


class PasswordChangeMiddleware:
    def __init__(self, get_response):
        self.get_response = get_response

    def __call__(self, request):
        response = self.get_response(request)
        next = reverse('client:password-update')
        if request.user.is_authenticated() and request.path != next:
            if request.user.account.force_password_change:
                return HttpResponseRedirect(next)

        return response
仍在将其添加到设置中间件列表:

MIDDLEWARE_CLASSES = (
    # Other middleware here
    'myproject.accounts.middleware.PasswordChangeMiddleware',
)

我最近花了两天时间研究这个问题,一个新的解决方案出现了。 希望它有用

如上所述,创建了一个新的用户模型

newuser/models.py

class PassWordReset(admin.AdminSite):

    def login(self, request, extra_context=None):
        if request.method == 'POST':
            response = super(PassWordReset, self).login(request, extra_context=extra_context)
            if response.status_code == 302 and request.user.is_authenticated():
                if not "fpr" in request.session or request.session['fpr']:
                    request.session['fpr'] = True
                    return HttpResponseRedirect("/admin/password_change/")
            return response
        return super(PassWordReset, self).login(request, extra_context=extra_context)

    def password_change(self, request, extra_context=None):
        if request.method == 'POST':
            response = super(PassWordReset, self).password_change(request, extra_context=extra_context)
            if response.status_code == 302 and request.user.is_authenticated():
                request.session['fpr'] = False
            return response
        return super(PassWordReset, self).password_change(request, extra_context=extra_context)


pfr_login = PassWordReset().login
pfr_password_change = PassWordReset().admin_view(PassWordReset().password_change, cacheable=True)
from myapp.views import pfr_password_change, pfr_login
urlpatterns = [
    ......
    url(r'^admin/login/$', pfr_login),
    url(r'^admin/password_change/$', pfr_password_change),
    url(r'^admin/', admin.site.urls),
    ....
]
class FPRCheck(object):
    def process_request(self, request):
        if request.user.is_authenticated() \
                and re.match(r'^/admin/?', request.path) \
                and (not "fpr" in request.session or ("fpr" in request.session and request.session['fpr'])) \
                and not re.match(r"/admin/password_change|/admin/logout", request.path):
            return HttpResponseRedirect("/admin/password_change/")
class Users(AbstractUser):
    default_pwd_updated = models.NullBooleanField(default=None, editable=False)
    pwd_update_time = models.DateTimeField(editable=False, null=True, default=None)  # reserved column to support further interval password (such as 60 days) update policy

     def set_password(self, raw_password):
        if self.default_pwd_updated is None:
            self.default_pwd_updated = False
        elif not self.default_pwd_updated:
            self.default_pwd_updated = True
            self.pwd_update_time = timezone.now()
        else:
            self.pwd_update_time = timezone.now()
        super().set_password(raw_password)
AUTH_USER_MODEL = 'newuser.Users'
from django.contrib.admin import AdminSite
from django.contrib.auth.views import LoginView
from django.utils.translation import gettext as _, gettext_lazy
from django.urls import reverse
from django.views.decorators.cache import never_cache
from django.contrib.auth import REDIRECT_FIELD_NAME
from django.http import HttpResponseRedirect


class NewLoginView(LoginView):
    def get_redirect_url(self):
        if self.request.method == "POST" and self.request.user.get_username()\
                and not self.request.user.default_pwd_updated:
            redirect_to = reverse("admin:password_change")
        else:
            redirect_to = self.request.POST.get(
                self.redirect_field_name,
                self.request.GET.get(self.redirect_field_name, '')
            )
        return redirect_to


class NewAdminSite(AdminSite):
    site_header = site_title = gettext_lazy("Customized Admin Site")

    def __init__(self, name="admin"):
        super().__init__(name)

    @never_cache
    def login(self, request, extra_context=None):
        """
        Display the login form for the given HttpRequest.
        """
        if request.method == 'GET' and self.has_permission(request):
            # Already logged-in, redirect to admin index
            if request.user.get_username() and not request.user.default_pwd_updated:
                # default password not changed, force to password_change view
                path = reverse('admin:password_change', current_app=self.name)
            else:
                path = reverse('admin:index', current_app=self.name)
            return HttpResponseRedirect(path)

        from django.contrib.auth.views import LoginView
        from django.contrib.admin.forms import AdminAuthenticationForm
        context = {
            **self.each_context(request),
            'title': _('Log in'),
            'app_path': request.get_full_path(),
            'username': request.user.get_username(),
        }
        if (REDIRECT_FIELD_NAME not in request.GET and
                REDIRECT_FIELD_NAME not in request.POST):
            context[REDIRECT_FIELD_NAME] = reverse('admin:index', current_app=self.name)
        context.update(extra_context or {})

        defaults = {
            'extra_context': context,
            'authentication_form': self.login_form or AdminAuthenticationForm,
            'template_name': self.login_template or 'admin/login.html',
        }
        request.current_app = self.name
        return NewLoginView.as_view(**defaults)(request) # use NewLoginView

    @never_cache
    def index(self, request, extra_context=None):
        if request.user.get_username() and not request.user.default_pwd_updated:
            # if default password not updated, force to password_change page
            context = self.each_context(request)
            context.update(extra_context or {})
            return self.password_change(request, context)
        return super().index(request, extra_context)


admin_site = NewAdminSite(name="admin")
def using_default_password(self, request):
    if self.has_permission(request) and request.user.get_username() and not request.user.default_pwd_updated:
            return True
    return False

def each_context(self, request):
    context = super().each_context(request)
    context["force_pwd_change"] = self.using_default_password(request)
    return context
将此模型设置为AUTH_USER_模型

[project]/settings.py

class PassWordReset(admin.AdminSite):

    def login(self, request, extra_context=None):
        if request.method == 'POST':
            response = super(PassWordReset, self).login(request, extra_context=extra_context)
            if response.status_code == 302 and request.user.is_authenticated():
                if not "fpr" in request.session or request.session['fpr']:
                    request.session['fpr'] = True
                    return HttpResponseRedirect("/admin/password_change/")
            return response
        return super(PassWordReset, self).login(request, extra_context=extra_context)

    def password_change(self, request, extra_context=None):
        if request.method == 'POST':
            response = super(PassWordReset, self).password_change(request, extra_context=extra_context)
            if response.status_code == 302 and request.user.is_authenticated():
                request.session['fpr'] = False
            return response
        return super(PassWordReset, self).password_change(request, extra_context=extra_context)


pfr_login = PassWordReset().login
pfr_password_change = PassWordReset().admin_view(PassWordReset().password_change, cacheable=True)
from myapp.views import pfr_password_change, pfr_login
urlpatterns = [
    ......
    url(r'^admin/login/$', pfr_login),
    url(r'^admin/password_change/$', pfr_password_change),
    url(r'^admin/', admin.site.urls),
    ....
]
class FPRCheck(object):
    def process_request(self, request):
        if request.user.is_authenticated() \
                and re.match(r'^/admin/?', request.path) \
                and (not "fpr" in request.session or ("fpr" in request.session and request.session['fpr'])) \
                and not re.match(r"/admin/password_change|/admin/logout", request.path):
            return HttpResponseRedirect("/admin/password_change/")
class Users(AbstractUser):
    default_pwd_updated = models.NullBooleanField(default=None, editable=False)
    pwd_update_time = models.DateTimeField(editable=False, null=True, default=None)  # reserved column to support further interval password (such as 60 days) update policy

     def set_password(self, raw_password):
        if self.default_pwd_updated is None:
            self.default_pwd_updated = False
        elif not self.default_pwd_updated:
            self.default_pwd_updated = True
            self.pwd_update_time = timezone.now()
        else:
            self.pwd_update_time = timezone.now()
        super().set_password(raw_password)
AUTH_USER_MODEL = 'newuser.Users'
from django.contrib.admin import AdminSite
from django.contrib.auth.views import LoginView
from django.utils.translation import gettext as _, gettext_lazy
from django.urls import reverse
from django.views.decorators.cache import never_cache
from django.contrib.auth import REDIRECT_FIELD_NAME
from django.http import HttpResponseRedirect


class NewLoginView(LoginView):
    def get_redirect_url(self):
        if self.request.method == "POST" and self.request.user.get_username()\
                and not self.request.user.default_pwd_updated:
            redirect_to = reverse("admin:password_change")
        else:
            redirect_to = self.request.POST.get(
                self.redirect_field_name,
                self.request.GET.get(self.redirect_field_name, '')
            )
        return redirect_to


class NewAdminSite(AdminSite):
    site_header = site_title = gettext_lazy("Customized Admin Site")

    def __init__(self, name="admin"):
        super().__init__(name)

    @never_cache
    def login(self, request, extra_context=None):
        """
        Display the login form for the given HttpRequest.
        """
        if request.method == 'GET' and self.has_permission(request):
            # Already logged-in, redirect to admin index
            if request.user.get_username() and not request.user.default_pwd_updated:
                # default password not changed, force to password_change view
                path = reverse('admin:password_change', current_app=self.name)
            else:
                path = reverse('admin:index', current_app=self.name)
            return HttpResponseRedirect(path)

        from django.contrib.auth.views import LoginView
        from django.contrib.admin.forms import AdminAuthenticationForm
        context = {
            **self.each_context(request),
            'title': _('Log in'),
            'app_path': request.get_full_path(),
            'username': request.user.get_username(),
        }
        if (REDIRECT_FIELD_NAME not in request.GET and
                REDIRECT_FIELD_NAME not in request.POST):
            context[REDIRECT_FIELD_NAME] = reverse('admin:index', current_app=self.name)
        context.update(extra_context or {})

        defaults = {
            'extra_context': context,
            'authentication_form': self.login_form or AdminAuthenticationForm,
            'template_name': self.login_template or 'admin/login.html',
        }
        request.current_app = self.name
        return NewLoginView.as_view(**defaults)(request) # use NewLoginView

    @never_cache
    def index(self, request, extra_context=None):
        if request.user.get_username() and not request.user.default_pwd_updated:
            # if default password not updated, force to password_change page
            context = self.each_context(request)
            context.update(extra_context or {})
            return self.password_change(request, context)
        return super().index(request, extra_context)


admin_site = NewAdminSite(name="admin")
def using_default_password(self, request):
    if self.has_permission(request) and request.user.get_username() and not request.user.default_pwd_updated:
            return True
    return False

def each_context(self, request):
    context = super().each_context(request)
    context["force_pwd_change"] = self.using_default_password(request)
    return context
现在您只需要自定义LoginViewAdminSite中的一些方法

[project]/admin.py

class PassWordReset(admin.AdminSite):

    def login(self, request, extra_context=None):
        if request.method == 'POST':
            response = super(PassWordReset, self).login(request, extra_context=extra_context)
            if response.status_code == 302 and request.user.is_authenticated():
                if not "fpr" in request.session or request.session['fpr']:
                    request.session['fpr'] = True
                    return HttpResponseRedirect("/admin/password_change/")
            return response
        return super(PassWordReset, self).login(request, extra_context=extra_context)

    def password_change(self, request, extra_context=None):
        if request.method == 'POST':
            response = super(PassWordReset, self).password_change(request, extra_context=extra_context)
            if response.status_code == 302 and request.user.is_authenticated():
                request.session['fpr'] = False
            return response
        return super(PassWordReset, self).password_change(request, extra_context=extra_context)


pfr_login = PassWordReset().login
pfr_password_change = PassWordReset().admin_view(PassWordReset().password_change, cacheable=True)
from myapp.views import pfr_password_change, pfr_login
urlpatterns = [
    ......
    url(r'^admin/login/$', pfr_login),
    url(r'^admin/password_change/$', pfr_password_change),
    url(r'^admin/', admin.site.urls),
    ....
]
class FPRCheck(object):
    def process_request(self, request):
        if request.user.is_authenticated() \
                and re.match(r'^/admin/?', request.path) \
                and (not "fpr" in request.session or ("fpr" in request.session and request.session['fpr'])) \
                and not re.match(r"/admin/password_change|/admin/logout", request.path):
            return HttpResponseRedirect("/admin/password_change/")
class Users(AbstractUser):
    default_pwd_updated = models.NullBooleanField(default=None, editable=False)
    pwd_update_time = models.DateTimeField(editable=False, null=True, default=None)  # reserved column to support further interval password (such as 60 days) update policy

     def set_password(self, raw_password):
        if self.default_pwd_updated is None:
            self.default_pwd_updated = False
        elif not self.default_pwd_updated:
            self.default_pwd_updated = True
            self.pwd_update_time = timezone.now()
        else:
            self.pwd_update_time = timezone.now()
        super().set_password(raw_password)
AUTH_USER_MODEL = 'newuser.Users'
from django.contrib.admin import AdminSite
from django.contrib.auth.views import LoginView
from django.utils.translation import gettext as _, gettext_lazy
from django.urls import reverse
from django.views.decorators.cache import never_cache
from django.contrib.auth import REDIRECT_FIELD_NAME
from django.http import HttpResponseRedirect


class NewLoginView(LoginView):
    def get_redirect_url(self):
        if self.request.method == "POST" and self.request.user.get_username()\
                and not self.request.user.default_pwd_updated:
            redirect_to = reverse("admin:password_change")
        else:
            redirect_to = self.request.POST.get(
                self.redirect_field_name,
                self.request.GET.get(self.redirect_field_name, '')
            )
        return redirect_to


class NewAdminSite(AdminSite):
    site_header = site_title = gettext_lazy("Customized Admin Site")

    def __init__(self, name="admin"):
        super().__init__(name)

    @never_cache
    def login(self, request, extra_context=None):
        """
        Display the login form for the given HttpRequest.
        """
        if request.method == 'GET' and self.has_permission(request):
            # Already logged-in, redirect to admin index
            if request.user.get_username() and not request.user.default_pwd_updated:
                # default password not changed, force to password_change view
                path = reverse('admin:password_change', current_app=self.name)
            else:
                path = reverse('admin:index', current_app=self.name)
            return HttpResponseRedirect(path)

        from django.contrib.auth.views import LoginView
        from django.contrib.admin.forms import AdminAuthenticationForm
        context = {
            **self.each_context(request),
            'title': _('Log in'),
            'app_path': request.get_full_path(),
            'username': request.user.get_username(),
        }
        if (REDIRECT_FIELD_NAME not in request.GET and
                REDIRECT_FIELD_NAME not in request.POST):
            context[REDIRECT_FIELD_NAME] = reverse('admin:index', current_app=self.name)
        context.update(extra_context or {})

        defaults = {
            'extra_context': context,
            'authentication_form': self.login_form or AdminAuthenticationForm,
            'template_name': self.login_template or 'admin/login.html',
        }
        request.current_app = self.name
        return NewLoginView.as_view(**defaults)(request) # use NewLoginView

    @never_cache
    def index(self, request, extra_context=None):
        if request.user.get_username() and not request.user.default_pwd_updated:
            # if default password not updated, force to password_change page
            context = self.each_context(request)
            context.update(extra_context or {})
            return self.password_change(request, context)
        return super().index(request, extra_context)


admin_site = NewAdminSite(name="admin")
def using_default_password(self, request):
    if self.has_permission(request) and request.user.get_username() and not request.user.default_pwd_updated:
            return True
    return False

def each_context(self, request):
    context = super().each_context(request)
    context["force_pwd_change"] = self.using_default_password(request)
    return context
注意:如果您打算使用自定义模板更改默认密码,您可以覆盖每个\u上下文方法,然后确定在标记强制\u pwd\u更改之前应使用哪个模板

[project]/admin.py

class PassWordReset(admin.AdminSite):

    def login(self, request, extra_context=None):
        if request.method == 'POST':
            response = super(PassWordReset, self).login(request, extra_context=extra_context)
            if response.status_code == 302 and request.user.is_authenticated():
                if not "fpr" in request.session or request.session['fpr']:
                    request.session['fpr'] = True
                    return HttpResponseRedirect("/admin/password_change/")
            return response
        return super(PassWordReset, self).login(request, extra_context=extra_context)

    def password_change(self, request, extra_context=None):
        if request.method == 'POST':
            response = super(PassWordReset, self).password_change(request, extra_context=extra_context)
            if response.status_code == 302 and request.user.is_authenticated():
                request.session['fpr'] = False
            return response
        return super(PassWordReset, self).password_change(request, extra_context=extra_context)


pfr_login = PassWordReset().login
pfr_password_change = PassWordReset().admin_view(PassWordReset().password_change, cacheable=True)
from myapp.views import pfr_password_change, pfr_login
urlpatterns = [
    ......
    url(r'^admin/login/$', pfr_login),
    url(r'^admin/password_change/$', pfr_password_change),
    url(r'^admin/', admin.site.urls),
    ....
]
class FPRCheck(object):
    def process_request(self, request):
        if request.user.is_authenticated() \
                and re.match(r'^/admin/?', request.path) \
                and (not "fpr" in request.session or ("fpr" in request.session and request.session['fpr'])) \
                and not re.match(r"/admin/password_change|/admin/logout", request.path):
            return HttpResponseRedirect("/admin/password_change/")
class Users(AbstractUser):
    default_pwd_updated = models.NullBooleanField(default=None, editable=False)
    pwd_update_time = models.DateTimeField(editable=False, null=True, default=None)  # reserved column to support further interval password (such as 60 days) update policy

     def set_password(self, raw_password):
        if self.default_pwd_updated is None:
            self.default_pwd_updated = False
        elif not self.default_pwd_updated:
            self.default_pwd_updated = True
            self.pwd_update_time = timezone.now()
        else:
            self.pwd_update_time = timezone.now()
        super().set_password(raw_password)
AUTH_USER_MODEL = 'newuser.Users'
from django.contrib.admin import AdminSite
from django.contrib.auth.views import LoginView
from django.utils.translation import gettext as _, gettext_lazy
from django.urls import reverse
from django.views.decorators.cache import never_cache
from django.contrib.auth import REDIRECT_FIELD_NAME
from django.http import HttpResponseRedirect


class NewLoginView(LoginView):
    def get_redirect_url(self):
        if self.request.method == "POST" and self.request.user.get_username()\
                and not self.request.user.default_pwd_updated:
            redirect_to = reverse("admin:password_change")
        else:
            redirect_to = self.request.POST.get(
                self.redirect_field_name,
                self.request.GET.get(self.redirect_field_name, '')
            )
        return redirect_to


class NewAdminSite(AdminSite):
    site_header = site_title = gettext_lazy("Customized Admin Site")

    def __init__(self, name="admin"):
        super().__init__(name)

    @never_cache
    def login(self, request, extra_context=None):
        """
        Display the login form for the given HttpRequest.
        """
        if request.method == 'GET' and self.has_permission(request):
            # Already logged-in, redirect to admin index
            if request.user.get_username() and not request.user.default_pwd_updated:
                # default password not changed, force to password_change view
                path = reverse('admin:password_change', current_app=self.name)
            else:
                path = reverse('admin:index', current_app=self.name)
            return HttpResponseRedirect(path)

        from django.contrib.auth.views import LoginView
        from django.contrib.admin.forms import AdminAuthenticationForm
        context = {
            **self.each_context(request),
            'title': _('Log in'),
            'app_path': request.get_full_path(),
            'username': request.user.get_username(),
        }
        if (REDIRECT_FIELD_NAME not in request.GET and
                REDIRECT_FIELD_NAME not in request.POST):
            context[REDIRECT_FIELD_NAME] = reverse('admin:index', current_app=self.name)
        context.update(extra_context or {})

        defaults = {
            'extra_context': context,
            'authentication_form': self.login_form or AdminAuthenticationForm,
            'template_name': self.login_template or 'admin/login.html',
        }
        request.current_app = self.name
        return NewLoginView.as_view(**defaults)(request) # use NewLoginView

    @never_cache
    def index(self, request, extra_context=None):
        if request.user.get_username() and not request.user.default_pwd_updated:
            # if default password not updated, force to password_change page
            context = self.each_context(request)
            context.update(extra_context or {})
            return self.password_change(request, context)
        return super().index(request, extra_context)


admin_site = NewAdminSite(name="admin")
def using_default_password(self, request):
    if self.has_permission(request) and request.user.get_username() and not request.user.default_pwd_updated:
            return True
    return False

def each_context(self, request):
    context = super().each_context(request)
    context["force_pwd_change"] = self.using_default_password(request)
    return context

这会阻止用户从更改密码页面导航到其他地方吗?(从未更改过他们的密码)实际上,我现在唯一的问题是更改管理员的“更改密码”页面,这样我就可以向用户显示一条消息,解释他们为什么会出现在这里。简单地将覆盖模板放在项目的templates目录中是不起作用的(它似乎被完全忽略)。并将
admin.site.password\u change\u模板
(1.2中新增)设置为