Django作为一个功能完善的web开发框架,自带了一个强大的用户认证系统,可以实现用户认证、会话管理、权限管理等功能。下面用实例实现一个授权访问指定视图的功能。所有功能是在一个complaint应用下实现。

一、创建登录表单

在应用目录下创建一个forms.py文件,在文件内创建一个继承自AuthenticationForm的表单,表单由username和password字段构成。后面在登录页面和视图中都需要用到这个表单。

# forms.py
from django import forms
from django.contrib.auth.forms import AuthenticationForm
from django.contrib.auth.models import User

class CustomLoginForm(AuthenticationForm):
    # 可以自定义字段属性,比如增加CSS类
    username = forms.CharField(
        label='用户名',
        max_length=100,
        widget=forms.TextInput(attrs={'class': 'form-control', 
                                      'placeholder': '用户名',
                                      'autocomplete':'true'})
    )
    password = forms.CharField(
        label='密码',
        widget=forms.PasswordInput(attrs={'class': 'form-control',
                                          'placeholder': '密码'})
    )

二、创建登录和退出视图

登录视图可以用CBV或FBV来实现,使用authenticate和login函数来验证用户并登录,首先用基于类的视图来实现:

from .forms import CustomLoginForm
from django.contrib.auth import authenticate, login

def user_login(request):
    if request.method == 'POST':
        # print("获取到了POST请求")
        form = CustomLoginForm(data=request.POST)
        if form.is_valid():
            username = form.cleaned_data.get('username')
            password = form.cleaned_data.get('password')
            # print(f'收到的用户名是{username},密码是{password}')
            user = authenticate(request,username=username, password=password)
            if user is not None:#如果user能返回数据,说明用户名密码正确
                login(request, user)
                return redirect('index')# 'index'已在urls.py中定义
            else:
                # print("用户名或密码错误啊")
                form.add_error(None, '用户名或密码错误')
        else:
            # print("form.is_valid没有通过")
            for field, errors in form.errors.items():
                for error in errors:
                    print(f"Error in {field}: {error}")
    else:
        # print('初始化表单')
        form = CustomLoginForm()
    return render(request, 'complaint/login.html', {'form': form})  #返回到登录表单模板

用基于类的视图来实现:

from .forms import CustomLoginForm
from django.contrib.auth.views import LoginView
from django.urls import reverse_lazy

class CustomLoginView(LoginView):
    template_name = 'complaint/login.html'  # 登录表单模板
    # 如果用户已登录,则重定向到的地址
    redirect_authenticated_user = True
    # 登录成功后的重定向地址
    success_url = reverse_lazy('index')
    
    def form_valid(self, form):
        # 这里可以添加自定义的登录逻辑
        username = form.cleaned_data.get('username')
        password = form.cleaned_data.get('password')
        print(f'收到的用户名是{username},密码是{password}')
        
        return super().form_valid(form)  # 调用父类的 form_valid 来完成默认的登录逻辑

退出视图比较简单,使用auth包中自带的logout函数退出后重定向到‘index’页面

from django.contrib.auth import logout
def user_logout(request):
    logout(request)
    return redirect('index')  # 重定向到登录页面

三、创建一个登录模板

创建一个login.html,用于登录页面。‘login’稍后步骤中定义。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>登录</title>
</head>
<body>
    <form method="post" action="{% url 'login' %}">
        {% csrf_token %}
        {{ form.as_p }}
        <button type="submit">Login</button>
    </form>
</body>
</html>

四、创建URLS路径

创建urls.py,添加路径来处理登录和退出的url。

from django.urls import path
from .views import *
from complaint import views

urlpatterns = [
    path("",views.index,name='index'),
    path('login/',views.user_login,name='login'),  
    # 如果是CBV,则使用下面路径。
 #path('login/',CustomLoginView.as_view(),name='login'),  
    path('logout/',views.user_logout,name='logout'),  
]

五、使用装饰器保护视图

如果是基于函数的视图,直接在视图函数前面添加@login_required即可。添加后,只有登录用户才可以访问

from django.contrib.auth.decorators import login_required

@login_required
def index(request):
  # 只有登录用户才能访问此视图
  # 需要在template文件夹下创建index.html。
    return render(request, 'complaint/index.html')
  

如果是基于类的视图,则需要引入LoginRequiredMixin类。让被保护的视图继承自该类。

from django.views.generic import View
from django.contrib.auth.mixins import LoginRequiredMixin
from django.shortcuts import render

class IndexView(LoginRequiredMixin, View):
    # 这里可以设置一些登录后重定向的选项
    login_url = '/login/'  # 未登录用户将被重定向到的URL
    redirect_field_name = 'redirect_to'

    def get(self, request, *args, **kwargs):
        # 视图的GET请求处理逻辑
        return render(request, 'complaint/index.html')

LogininRequiredMixin类提供了两个参数来实现自定义的重定向行为。

  • login_url:未登录用户将被重定向到的URL。默认是/accounts/login/

  • redirect_field_name:用于存储原始请求路径的查询参数字段名。默认是next

六、其他

如果想在登录成功后返回正在访问的页面,可以通过设置redirect_field_name属性,通过在登录页面中设置一个隐藏的next属性来实现。

<!-- login.html -->
<form method="post" action="{% url 'login' %}">
    {% csrf_token %}
    {{ form.as_p }}
    <input type="hidden" name="next" value="{{ request.GET.next }}">
    <button type="submit">登录</button>
</form>

然后修改登录视图,在登录成功后重定向到next页面。

def user_login(request):
    if request.method == 'POST':
        form = CustomLoginForm(data=request.POST)
        if form.is_valid():
            username = form.cleaned_data.get('username')
            password = form.cleaned_data.get('password')
            #  在这里获取隐藏的next属性的值
            next = request.POST.get('next')
            user = authenticate(request,username=username, password=password)
            print(user)
            if user is not None
                login(request, user)
                if (next is not None) and next !='':
                    print(f"登录成功重定向到{next}")
                    return redirect(next)  # 重定向
                else:
                    print('重定向到首页')
                    return redirect('index')
            else:
                form.add_error(None, '用户名或密码错误')
        else:
            for field, errors in form.errors.items():
                for error in errors:
                    print(f"Error in {field}: {error}")
    else:
        print('初始化表单')
        form = CustomLoginForm()
    return render(request, 'complaint/login.html', {'form': form})