【问题标题】:Django request.user becomes anonymous after redirect. Using custom user model and authentication重定向后 Django request.user 变为匿名。使用自定义用户模型和身份验证
【发布时间】:2019-10-01 00:10:38
【问题描述】:

我正在使用 django 2.1.7 和 python 3.6.5 版本,使用“AbstractUser”扩展 django 用户模型并覆盖 django 身份验证方法。在登录之前一切似乎都可以正常工作,但是当页面重定向到主页时 request.user 变为空。

我尝试渲染而不是重定向,这似乎可行,但是当我在不同的选项卡中打开同一页面或刷新页面时,我收到 403 禁止错误(CSRF 验证失败。请求中止。) 尝试了以下设置,但似乎仍然不起作用 CSRF_COOKIE_SECURE = 假 SESSION_COOKIE_SECURE = 假

settings.py

LOGIN_URL = '/'
LOGIN_REDIRECT_URL = 'home'
AUTH_USER_MODEL = 'hygie_portal.CustomUser'

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'hygie_portal',
    'django_assets',
]

MIDDLEWARE = [
    'django.middleware.security.SecurityMiddleware',
    'django.contrib.sessions.middleware.SessionMiddleware',
    'django.middleware.common.CommonMiddleware',
    'django.middleware.csrf.CsrfViewMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'django.contrib.messages.middleware.MessageMiddleware',
    'django.middleware.clickjacking.XFrameOptionsMiddleware',
]

AUTHENTICATION_BACKENDS = (
                            'hygie_portal.mybackend.MyBackEnd',
)

models.py

from django.contrib.auth.models import AbstractUser

class CustomUser(AbstractUser):
    is_admin = models.IntegerField(blank=True, null=True)
    role = models.CharField(max_length=256, blank=True, null=True)

    def set_is_admin(self, is_admin):
        self.is_admin = is_admin

    def set_role(self, role):
        self.last_update = datetime.now()
        self.role = role

forms.py

from django.contrib.auth import get_user_model
from django.contrib.auth.forms import UserCreationForm, UserChangeForm
from django.forms import TextInput,EmailInput,PasswordInput

CustomUser = get_user_model()

class LoginForm(forms.Form):
    """Login form."""

    username = forms.CharField(label='Username', max_length=100)
    password = forms.CharField( widget=forms.PasswordInput())

class CustomUserCreationForm(UserCreationForm):

    class Meta(UserCreationForm):
        model = CustomUser
        fields = ('username',)

class CustomUserChangeForm(UserChangeForm):

    class Meta:
        model = CustomUser
        fields = ('username', 'email')

admin.py

from django.contrib import admin
from django.contrib.auth import get_user_model
from django.contrib.auth.admin import UserAdmin
from .forms import CustomUserCreationForm, CustomUserChangeForm

CustomUser = get_user_model()

class CustomUserAdmin(UserAdmin):
    add_form = CustomUserCreationForm
    form = CustomUserChangeForm
    model = CustomUser
    list_display = ['username',]

admin.site.register(CustomUser, CustomUserAdmin)

urls.py

app_name = "hygie_portal"
urlpatterns = [
    path("", views.login_request, name='login'),
    path(r'login/', RedirectView.as_view(url='/login/')),
    path('home/', views.home, name='home'),
]

view.py

from django.contrib.auth import get_user_model
CustomUser = get_user_model()

@csrf_protect
def login_request(request):
    if request.user.is_authenticated:
        return render(request, 'users/home_public.html')
    if request.method == 'POST':
        form = LoginForm(request.POST)
        if form.is_valid():
            username = form.cleaned_data.get('username')
            password = form.cleaned_data.get('password')

        user_info = MyBackEnd.authenticate(encode(username), encode(password), request=request)

        if not user_info:
            messages.error(request,'Invalid username or password. Please try again.')
            return redirect('hygie_portal:login')
        is_admin = True if 'manager' in user_info['role'] else False
        try:
            user_profile = CustomUser.objects.get(username=username)
        except:
            user_profile = ''

        if not user_profile:
            if 'name' in user_info:
                try:
                    fname = user_info['name'].split()[0]
                except IndexError:
                    fname = ''
                try:
                    sname = user_info['name'].split()[1]
                except IndexError:
                    sname = ''

            else:
                fname, sname = '', ''  
            user_profile = CustomUser(username=username, email=user_info['email'], first_name=fname, last_name=sname, role=user_info['role'])
            user_profile.save()

        try:
            user_profile.set_is_admin(is_admin)
            user_profile.set_role(user_info['role'])
            user_profile.save()
        except Exception as e:
            logger.error('Exception updating user****' % e)
        login(request, user_profile, backend='hygie_portal.mybackend.MyBackEnd')
    #After login request.user.is_authenticated returns true
    #return redirect('hygie_portal:home') #when redirected to home request.user.is_authenticated returns False
    return render(request, 'users/home_public.html') #works fine, but throws 403 error when page is refreshed or url accessed in different tab

    form = LoginForm()   
    if form.errors:
        messages.error(form.errors, 'danger')
        flash(form.errors, 'danger')

    # default to login page if not authenticated or no form submit.
    return render(request, 'public/login.html',{'form': form})

def home(request):  
    if request.user.is_authenticated:
        return render(request, 'users/home_public.html')
    else:
        return redirect('hygie_portal:login')

我的后端.py

class MyBackEnd(object):
    def authenticate(username, password, request=None):
        bind_attr = settings.AD_BIND_ATTR
        user_dn = settings.AD_DN
        login_attr = '(%s=%s)' % (settings.AD_LOGIN_ATTR, username)

        data = get_bind_user(username) #returns the user info with role permissions

        if len(data) == 0:
            return None
        if 'mail' not in data:
            logger.warning('No email found in AD, adding dummy email')
        info['name'] = data['cn'][0] if 'cn' in data else None
        info['email'] = data['mail'][0] if 'mail' in data else 'nomail@xx.com' 
        try:
            info['phone'] = data['telephoneNumber'][0]
        except KeyError:
            info['phone'] = 'Not Available'

        try:
            info['role'] = data['role']
        except KeyError:
            info['role'] = False

        conn = ldap.initialize(settings.AD_URL)
        conn.set_option(ldap.OPT_REFERRALS,0)
        conn.set_option(ldap.OPT_PROTOCOL_VERSION, settings.LDAP_PROTOCOL_VERSION)

        try:
            conn.bind_s(data[bind_attr][0].decode(), password)
            conn.search(user_dn, ldap.SCOPE_SUBTREE, login_attr, None)
            conn.result()
            return info
        except (ldap.INVALID_CREDENTIALS, ldap.OPERATIONS_ERROR):
            logger.info('Invalid credentials for :%s' % username)
            return None

    def get_user(self, username):
        try:
            return CustomUser.objects.get(username=username)
        except CustomUser.DoesNotExist:
            return None

我在 django 文档中看到 login() 将保存整个会话的用户 ID。我希望用户登录后可以跨不同的选项卡访问会话。

我尝试使用 request.session 检查会话详细信息,但我不知道如何在我的代码中使用它。

【问题讨论】:

    标签: django python-3.x


    【解决方案1】:

    您是否在settings.py 中添加了'django.contrib.sessions' 中的INSTALLED_APPS? 您应该考虑使用Django Sessions

    默认情况下,Django 将会话信息保存在数据库中(django_session 表或集合),但您可以将引擎配置为使用其他方式存储信息,例如:在文件中或在缓存中。启用会话后,每个请求(Django 中任何视图的第一个参数)都有一个会话(dict)属性。 您可以使用会话字典来手动存储User 信息,例如

    request.session['username'] = username
    
    request.session['email'] = email
    

    【讨论】:

    • 我在 settings.py 的 INSTALLED_APPS 中已经有 'django.contrib.sessions',编辑了代码。我是 django 新手,你能详细解释一下如何将用户添加到会话字典中,并确保 request.user.is_authenticated 在用户已经登录的情况下为真。这是实现的好方法吗??跨度>
    【解决方案2】:

    我认为问题在于您的get_user 方法。它应该接受用户 ID,而不是用户名,并使用它来查找模型实例。

    def get_user(self, user_id):
        try:
            return CustomUser.objects.get(pk=user_id)
        except CustomUser.DoesNotExist:
            return None
    

    【讨论】:

      【解决方案3】:

      Django 使用AuthenticationMiddleware 将用户对象附加到httpRequest 对象。

      当您login() 用户时,它将保存users.id 以及backend 对会话的引用:

      login(request, user, backend='your.auth.backend')
      

      这允许AuthenticationMiddleware 稍后使用相同的后端来获取user 对象并将其附加到httpRequest。 它通过调用get_user() 方法,将user_id 作为参数传递来做到这一点。

      重要的是不要覆盖 get_user() 以接受除主键或用户 ID 以外的任何内容

      更多信息可以在这里找到:

      https://docs.djangoproject.com/en/3.1/ref/middleware/#django.contrib.auth.middleware.AuthenticationMiddleware

      https://docs.djangoproject.com/en/3.1/topics/auth/customizing/#writing-an-authentication-backend][2]

      【讨论】:

        猜你喜欢
        • 2017-01-03
        • 2016-10-07
        • 2012-05-06
        • 1970-01-01
        • 2021-01-09
        • 2016-10-18
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多