Django之用户认证Auth组件

Posted CodeDevMaster

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Django之用户认证Auth组件相关的知识,希望对你有一定的参考价值。

Django之用户认证Auth组件

用户认证Auth

概述

Django 用户认证(Auth)组件一般用在用户的登录注册上,用于判断当前的用户是否合法,并跳转到登陆成功或失败页面。

Django认证组件的重要信息:

用户模型:

Django的认证组件使用的默认用户模型(User)存储用户信息,包括用户名、密码和电子邮件地址。通过配置,可以使用扩展的用户模型(例如添加额外的自定义字段)。

视图:

Django的认证组件包含了一系列视图,用于处理用户认证(登录、注销、密码重置)、用户管理(注册、修改密码、更改用户信息)以及权限管理。

身份验证后端(Authentication backends):

Django的认证组件使用身份验证后端来验证用户身份。默认情况下,Django提供基于用户名和密码的身份验证后端,但是你也可以编写自己的身份验证后端来满足你的特定需求。

用户组和权限:

Django的认证组件允许创建用户组,为这些用户组分配权限,并控制哪些用户可以访问特定的视图。

Django用户认证组件需要导入auth模块:

# 认证模块
from django.contrib import auth

# 对应数据库
from django.contrib.auth.models import User

User模型类

Django框架默认使用一个User模型类, 保存有关用户的字段,使用auth_user表存储。

User模型类继承自AbstractUser类

AbstractUser类

User对象基本属性

创建用户必选: username、password

创建用户可选:email、first_name、last_name、last_login、date_joined、is_active 、is_staff、is_superuse

判断用户是否通过认证:is_authenticated

创建用户

创建用户对象的三种方法:

create():创建一个普通用户,密码是明文

create_user():创建一个普通用户,密码是密文

create_superuser():创建一个超级用户,密码是密文,要多传一个邮箱email参数

参数:

username: 用户名

password:密码

email:邮箱 (create_superuser 方法要多加一个 email)
from django.contrib.auth.models import User 

User.objects.create(username='john',password='password')

user = User.objects.create_user('john', 'john@example.com', 'password')

User.objects.create_superuser(username='john',password='password',email='john@example.com')

身份验证

验证用户的用户名和密码使用authenticate() 方法,从需要 auth_user 表中过滤出用户对象

参数:

username:用户名

password:密码

返回值:如果验证成功,就返回用户对象,反之,返回 None。

from django.contrib import auth

# 通过用户名和密码进行身份验证
user_obj = auth.authenticate(username=username, password=password)
print(user_obj.username)

登陆

给验证成功的用户加 session,将 request.user 赋值为用户对象。

user_obj = auth.authenticate(username=username, password=password)
print(user_obj.username)

if not user_obj:
     return redirect("/login/")
else:
    auth.login(request, user_obj)
    path = request.GET.get("next") or "/index/"
    print(path)
    return redirect(path)

注销

注销用户使用 logout() 方法,需要清空 session 信息,将 request.user 赋值为匿名用户。

def logout(request):
    auth.logout(request)
    return redirect("/login/")

判断用户是否登录

is_authenticate

Django用户认证系统提供了方法request.user.is_authenticated()来判断用户是否登录。如果通过登录验证则返回True。反之,返回False。

缺点:凡是需要登录验证都需要判断逻

LoginRequiredMixin封装了判断用户是否登录的操作。

from django.contrib.auth.mixins import LoginRequiredMixin

class UserInfoView(LoginRequiredMixin, View):
    def get(self, request):
        return render(request, 'user_center_info.html')
class UserInfoView(View):

    def get(self, request):
        """个人信息界面"""
        
        # 判断是否登录验证
        if request.user.is_authenticated():
            # 如果登录, 则加载用户中心页面
            return render(request, 'user_center.html')
        else:
            # 否则, 进入登录页面,进行登录
            return redirect(reverse('user:login'))

@login_required装饰器

Django的用户认证系统提供了@login_required这个装饰器来判断用户是否登录,其内部封装了 is_authenticate

1.装饰函数视图

装饰器可以直接装饰函数视图

from django.contrib.auth.decorators import login_required

@login_required
def index(request):
	# 该视图需要登录才能访问
    return HttpResponse("index页面。。。")

2.装饰类视图

要想使用 login_required装饰器装饰类视图,可以间接的装饰as_view()方法的返回值,as_view()方法就是将类视图转成的函数视图

from django.contrib.auth.decorators import login_required
from django.urls import path, re_path

from user.views import UserInfoView

urlpatterns = [
    re_path(r'^userInfo/$', login_required(UserInfoView.as_view()), name='userInfo'),
]

3.封装装饰器

不推荐对as_view() 方法添加装饰行为,因为url.py 文件只是一个加载路由的文件, 把逻辑写在这个文件不好

1.定义 View 子类封装 login_required 装饰器

# 继承自: View
class LoginRequired(View):
    """验证用户是否登陆的工具类"""

    # 重写as_view() , 对as_view()进行装饰
    @classmethod
    def as_view(cls, **initkwargs):
        # 重写这个方法, 不做任何的修改操作, 直接调用父类的 super().as_view() 函数.
        view = super().as_view()
        return login_required(view)

url配置还原

urlpatterns = [
    re_path(r'^userInfo/$', UserInfoView.as_view(), name='userInfo'),
]

让需要认证登录的的类视图继承LoginRequired

class UserInfoView(LoginRequired):

    def get(self, request):
        return render(request, 'user_info.html')

2.定义 obejct子类封装login_required装饰器

工具类直接依赖于视图类 View,所以复用性相对来说很差,定义obejct子类封装login_required装饰器

更好的做法是将将这个工具类封装成一个扩展类,在views.py中进行导入

class LoginRequired(object):

    # 重写as_view() , 对as_view()进行装饰
    @classmethod
    def as_view(cls, **initkwargs):
        # 重写这个方法, 不做任何的修改操作, 直接调用父类的 super().as_view() 函数.
        view = super().as_view()
        return login_required(view)


# 需要认证登录的的类视图继承工具类 + View
class UserInfoView(LoginRequired, View):

    def get(self, request):
        return render(request, 'user_info.html')

如果通过登录验证则进入到视图内部,执行视图逻辑。如果未通过登录验证则被重定向到LOGIN_URL 配置项指定的地址

在项目的 settings.py文件配置

# 默认重定向
# LOGIN_URL = '/accounts/login'

# 表示当用户未通过登录验证时,将用户重定向到登录页面
LOGIN_URL = '/user/login/'

next参数

next参数记录了用户未登录时访问的地址信息,可以帮助实现在用户登录成功后直接进入未登录时访问的地址。

简单的说就是next参数用于设置从哪个页面访问,登录成功后就返回哪个页面。

#  获取跳转过来的地址
next = request.GET.get('next')
if next:
	# 如果是从别的页面跳转过来的, 则重新跳转到原来的页面
    response = redirect(next)
else:
	 # 如果是直接登陆成功,就重定向到首页
    response = redirect(reverse('user:index'))

如果get() 就取不到值,path可以自定义设置返回的页面

path = request.GET.get("next") or "/index/"
return redirect(path)

修改密码

打开Terminal,执行以下代码,然后输入新的密码。此方法会校验密码强度,不能设置过于简单的密码)

python manage.py changepassword 用户名

打开Terminal,进入shell环境,执行以下代码,此方法不会校验密码强度,可设置简单的密码

from django.contrib.auth.models import User
user = User.objects.get(username='用户名')
user.set_password('新的密码')
user.save()

使用Django提供的PasswordChangeForm表单来实现修改密码

如果是请求是POST,则使用PasswordChangeForm表单来验证并保存修改密码的请求,然后重定向到index。

如果请求是GET,将渲染一个模板,显示一个表单,让用户输入当前密码和新密码。
from django.contrib.auth.forms import PasswordChangeForm
from django.contrib.auth.decorators import login_required
from django.shortcuts import render, redirect


@login_required
def change_password(request):
    if request.method == 'POST':
        form = PasswordChangeForm(user=request.user, data=request.POST)
        # 进行数据校验
        if form.is_valid():
            form.save()
            return redirect(reverse('user_app:index'))
    else:
        form = PasswordChangeForm(user=request.user)
    return render(request, 'change_password.html', 'form': form)

创建URL模式

from user.views import IndexView, change_password

urlpatterns = [
    re_path('index/', IndexView.as_view(), name='index'),
    path('change_password/', change_password, name='change_password'),
]

创建修改密码表单模板

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>

  <h2>修改密码</h2>
  <form method="post">
    % csrf_token %
     form.as_p 
    <button type="submit">提交</button>
  </form>

</body>
</html>

先进行用户登录,然后访问http://127.0.0.1:8000/user/change_password/修改密码

自定义用户模型类

当Django默认用户模型类中没有需要字段时,就需要自定义用户模型类。

自义用户模型类只需要继承AbstractUser,然后新增需要的额外字段即可,如新增mobile字段

from django.contrib.auth.models import AbstractUser
from django.db import models


# 重写用户模型类, 继承AbstractUser
class User(AbstractUser):
    """自定义用户模型类"""

    # 增加mobile字段
    mobile = models.CharField(max_length=11, unique=True, verbose_name='手机号')

    # 对当前表进行相关设置: 
    class Meta:
        db_table = 'tb_users'
        verbose_name = '用户'
        verbose_name_plural = verbose_name

    # str方法中, 返回用户名称
    def __str__(self):
        return self.username

Django中默认使用auth子应用下面的User作为用户模型类。即Django用户模型类是通过全局配置项AUTH_USER_MODEL决定的

因为重写用户模型类, 所以需要在settings.py文件重新指定默认的用户模型类

AUTH_USER_MODEL = '应用名.模型类名'

生成表

# 生成迁移文件
python manage.py makemigrations

# 进行数据迁移
python manage.py migrate
mysql> show tables;
+---------------------------+
| Tables_in_demo            |
+---------------------------+
| auth_group                |
| auth_group_permissions    |
| auth_permission           |
| django_admin_log          |
| django_content_type       |
| django_migrations         |
| django_session            |
| tb_users                  |
| tb_users_groups           |
| tb_users_user_permissions |
+---------------------------+
10 rows in set (0.00 sec)

注册

项目 配置urls.py

from django.contrib import admin
from django.urls import path, include, re_path

urlpatterns = [
    path('admin/', admin.site.urls),
    re_path(r'^user/*/', include(('user.urls', 'user_app'), namespace='user'))
]

应用 配置urls.py

from user.views import RegisterView, IndexView

urlpatterns = [
    re_path('index/', IndexView.as_view(), name='index'),
    re_path('register/', RegisterView.as_view(), name='register'),
]

注册功能实现

import re

from django import http
from django.contrib.auth import login
from django.db import DatabaseError
from django.shortcuts import redirect
from django.shortcuts import render
from django.urls import reverse
from django.views import View
from django_redis import get_redis_connection

from user.models import User


class IndexView(View):

    def get(self, request):
        return render(request, 'index.html')

    def post(self, request):
        return render(request, 'index.html')


class RegisterView(View):

    def get(self, request):
        return render(request, 'register.html')

    def post(self, request):
        username = request.POST.get('username')
        password = request.POST.get('password')
        password2 = request.POST.get('password2')
        mobile = request.POST.get('mobile')
        sms_code = request.POST.get('sms_code')

        # 判断参数是否齐全
        if not all([username, password, password2, mobile, sms_code]):
            return http.HttpResponseBadRequest('缺少必传参数')
        # 判断用户名是否是5-20个字符
        if not re.match(r'^[a-zA-Z0-9_]5,20$', username):
            return http.HttpResponseBadRequest('请输入5-20个字符的用户名')
        # 判断密码是否是8-20个数字
        if not re.match(r'^[0-9A-Za-z]8,20$', password):
            return http.HttpResponseBadRequest('请输入8-20位的密码')
        # 判断两次密码是否一致
        if password != password2:
            return http.HttpResponseBadRequest('两次输入的密码不一致')
        # 判断手机号是否合法
        if not re.match(r'^1[3-9]\\d9$', mobile):
            return http.HttpResponseBadRequest('请输入正确的手机号码')

        # redis 短信验证码
        sms_code_client = request.POST.get('sms_code')
        redis_conn = get_redis_connection('default')
        sms_code_server = redis_conn.get('sms_%s' % mobile)
        if sms_code_server is None:
            return render(request, 'register.html', 'msg': '无效的短信验证码')
        if sms_code_client != sms_code_server.decode():
            return render(request, 'register.html', 'msg': '输入短信验证码有误')

        # 保存注册数据
        try:
            user = User.objects.create_user(username=username, password=password, mobile=mobile)
        except DatabaseError:
            return render(request, 'register.html', 'msg': '注册失败')

        # 实现状态保持
        login(request, user)

        # 响应注册结果
        response = redirect(reverse('user_app:index'))

        # 注册时用户名写入到cookie,有效期15天
        response.set_cookie('username', user.username, max_age=3600 * 24 * 15)

        return response

用户名登录

Django自带的用户认证后端默认是使用用户名实现用户认证的。用户认证后端位置:django.contrib.auth.backends.ModelBackend。

应用 配置urls.py

from user.views import LoginView

urlpatterns = [
    re_path(r'^login/$', LoginView.as_view(), name='login'),
]

登录功能实现

class LoginView(View):
    def get(self, request):
        return render(request, "login.html")

    def post(self, request):
        # 接受参数
        username = request.POST.get('username')
        password = request.POST.get('password')
        remembered = request.POST.get('remembered')

        # 判断参数是否齐全
        if not all([username, password]):
            return http.HttpResponseBadRequest('缺少必传参数')

        # 判断用户名是否是5-20个字符
        if not re.match(r'^[a-zA-Z0-9_-]5,20$', username):
            return http.HttpResponseBadRequest('请输入正确的用户名或手机号')

        # 判断密码是否是8-20个数字
        if not re.match(r'^[0-9A-Za-z]8,20$', password):
            return http.HttpResponseBadRequest('密码最少8位,最长20位')

        # 认证登录用户
        user = authenticate(username=username, password=password)
        if user is None:
            return render(request, 'login.html', 

Django之auth认证组件

原文链接:https://www.jianshu.com/p/612b3d76a1f5

Django之auth组件

前言:我们在开发一个网站的时候,无可避免的需要设计实现网站的用户系统。

    此时我们需要实现包括用户注册、用户登录、用户认证、注销、修改密码等功能,这还真是个麻烦的事情呢。
    Django作为一个完美主义者的终极框架,当然也会想到用户的这些痛点。它内置了强大的用户认证系统--auth,它默认使用 auth_user 表来存储用户数据。

auth中提供的实用方法:

- 创建用户
python manage.py createsuperuser
python manage.py createuser
- 模块的导入:
from django.contrib import auth

1. authenticate()

提供了用户认证功能,即验证用户名以及密码是否正确,一般需要username 、password两个关键字参数。
    如果认证成功(用户名和密码正确有效),便会返回一个 User 对象。
    authenticate()会在该 User 对象上设置一个属性来标识后端已经认证了该用户,且该信息在后续的登录过程中是需要的。

user = authenticate(username='theuser',password='thepassword')

2. login(HttpRequest, user)

该函数接受一个HttpRequest对象,以及一个经过认证的User对象。该函数实现一个用户登录的功能。它本质上会在后端为该用户生成相关session数据

from django.contrib.auth import authenticate, login

def my_view(request):
    username = request.POST['username']
    password = request.POST['password']
    user = authenticate(username=username, password=password)
    if user is not None:
        login(request, user)
        # Redirect to a success page.
        ...
    else:
    # Return an 'invalid login' error message.
    ...

3. logout(request)

该函数接受一个HttpRequest对象,无返回值。当调用该函数时,当前请求的session信息会全部清除。该用户即使没有登录,使用该函数也不会报错。

from django.contrib.auth import logout

def logout_view(request):
    logout(request)
    # Redirect to a success page.

4.is_authenticated()

用来判断当前请求是否通过了认证

def my_view(request):
    if not request.user.is_authenticated():
    return redirect('%s?next=%s' % (settings.LOGIN_URL, request.path))

5.login_requierd()

auth 给我们提供的一个装饰器工具,用来快捷的给某个FBV视图添加登录校验。

from django.contrib.auth.decorators import login_required

@login_required
def my_view(request):
    ...
### 6.auth 给我们提供的一个装饰器工具,用来快捷的给某个CBV视图添加登录校验。
from django.contrib.auth.decorators import login_required
from django.utils.decorators import method_decorator

class Home(views.View):

    @method_decorator(login_required)
    def get(self, request):
        return render(request, 'home.html')
        
        
若用户没有登录,则会跳转到django默认的 登录URL '/accounts/login/ ' 并传递当前访问url的绝对路径 (登陆成功后,会重定向到该路径)。
    如果需要自定义登录的URL,则需要在settings.py文件中通过LOGIN_URL进行修改。 LOGIN_URL = '/login/' # 这里配置成你项目登录页面的路由

7.create_user()

auth 提供的一个创建新用户的方法,需要提供必要参数(username、password)等。

from django.contrib.auth.models import User

user = User.objects.create_user(username = '用户名', password = '密码', email = '邮箱', ...)
create_superuser()

8.create_superuser()

auth 提供的一个创建新的超级用户的方法,需要提供必要参数(username、password)等。

from django.contrib.auth.models import User

user = User.objects.create_superuser(username = '用户名', password = '密码', email = '邮箱', ...)
check_password(password)

9.check_password(password)

auth 提供的一个检查密码是否正确的方法,需要提供当前请求用户的密码。密码正确返回True,否则返回False。

ret = user.check_password('密码')

10.set_password(password)

auth 提供的一个修改密码的方法,接收 要设置的新密码 作为参数。注意:设置完一定要调用用户对象的save方法!!!

user.set_password(password='')  
user.save()

User对象的属性

username,
password
is_staff : 用户是否拥有网站的管理权限.
is_active : 是否允许用户登录, 设置为 False,可以在不删除用户的前提下禁止用户登录。

扩展默认的auth_user表

我们可以通过继承内置的 AbstractUser 类,来定义一个自己的Model类。这样既能根据项目需求灵活的设计用户表,又能使用Django强大的认证系统了。

from django.contrib.auth.models import AbstractUser
class UserInfo(AbstractUser):
    """
    用户信息表
    """
    nid = models.AutoField(primary_key=True)
    phone = models.CharField(max_length=11, null=True, unique=True)
    
    def __str__(self):
        return self.username
注意:按上面的方式扩展了内置的auth_user表之后,一定要在settings.py中告诉Django,我现在使用我新定义的UserInfo表来做用户认证。写法如下:

# 引用Django自带的User表,继承使用时需要设置
AUTH_USER_MODEL = "app名.UserInfo"
再次注意:一旦我们指定了新的认证系统所使用的表,我们就需要重新在数据库中创建该表,而不能继续使用原来默认的auth_user表了。

一个修改密码功能的简单示例:

@login_required
def set_password(request):
    user = request.user
?    err_msg = ''
    if request.method == 'POST':
        old_password = request.POST.get('old_password', '')
        new_password = request.POST.get('new_password', '')
        repeat_password = request.POST.get('repeat_password', '')
        # 检查旧密码是否正确
        if user.check_password(old_password):
            if not new_password:err_msg = '新密码不能为空'
            elif new_password != repeat_password:err_msg = '两次密码不一致'
            else:user.set_password(new_password)user.save()
            return redirect("/login/")
        else:
            err_msg = '原密码输入错误'
            content = {'err_msg': err_msg,}
            return render(request, 'set_password.html', content)
            
            

通过继承内置的 AbstractUser 类,来定义一个自己的Model类

from django.contrib.auth.models import AbstractUser
class UserInfo(AbstractUser):
    """
    用户信息表
    """
    nid = models.AutoField(primary_key=True)
    phone = models.CharField(max_length=11, null=True, unique=True)
    
    def __str__(self):
        return self.username

以上是关于Django之用户认证Auth组件的主要内容,如果未能解决你的问题,请参考以下文章

django之用户认证组件

Django之auth模块(用户认证)登陆组件

Django之用户认证组件

Django之auth组件

django之auth组件等相关内容-76

Django组件之auth