当前位置:   article > 正文

Django-14:auth认证模块

Django-14:auth认证模块

Auth模块

一、auth user表

本小节主要推导出auth模块是如何使用的,以及一些前置内容。

auth_user表:

  • 在创建好一个django项目之后,直接执行数据库迁移命令,会自动生成很多表,如django_session,本小节将介绍另外一个很重要的表auth_user

    image-20221215235817481
  • 从字段名可以看出,这张表储存的就是用户相关的一些信息。

那么auth_user这张表怎么使用呢?

  • django在启动之后就可以直接访问admin路由,需要输入用户名和密码,数据参考的就是auth_user表,只有is_superuser字段的值为1,那就说明是超级用户可以登陆到django自带的用户后台管理页面。

如何创建超级用户和普通用户呢?

  • 创建超级用户(管理员)

    python manage.py createsuperuser

    image-20221216000608850 image-20221216003742315

    创建好了之后,可以看到密码字段是密文的(data_joined字段的值不是参考当前时间的,所以会对不上)

    后面用户的登陆、注销、校验等功能,全都都依靠auth_user这张表来完成。

  • 创建普通用户

    稍后补充

二、auth模块常用方法

汇总一览:

语法作用
authenticate对用户名和密码进行校验
login保存用户状态
user获取当前用户对象
is_authenticated()校验当前用户是否经过校验
login_required装饰器需要通过校验才能访问的视图函数
check_oassword用于做密码校验
set_password、save修改密码及数据库更新操作
logout注销
create_user、create_superuser注册

逐步推导:

书写一个登陆页面,视图层层以外的代码略。

def login(request):
    if request.method == 'POST':
        name = request.POST.get('uname')
        passwd = request.POST.get('upassword')
        # 去auth_user表中校验数据
    return render(request,'login.html')
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
'
运行

这个时候就遇到了问题:

  • 既然是使用auth_user表来完成用户的登陆注销等功能,那表如何获取呢
  • 还有,auth_user表中的密码字段是加密过的,如何将request.POST的明文密码与表中的密文密码进行比对

方法1:authenticate

作用: 自动查找auth_user表,对用户名和密码进行校验,其中自动给密码加密再比对

代码示例:

  • from django.contrib import auth
    def login(request):
        if request.method == 'POST':
            name = request.POST.get('uname')
            passwd = request.POST.get('upassword')
            # 去auth_user表中校验数据
            auth.authenticate(request,username=name,password=passwd)
        return render(request,'login.html')
    
    '''
    username和password为auth_user表中对应的字段名
    '''
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    注意事项:括号内必须同时传入用户名和密码,不能只传用户名。

返回值:

  • 校验通过的情况下: 返回用户数据对象,该对象的类中定义了双下str方法,打印对象可以获取到该用户数据对象的用户名,也就是username字段的值。

    进入到准备好的Login页面,输入一章节中注册的超级用户,测试校验结果。

    from django.contrib import auth
    def login(request):
    if request.method == 'POST':
      name = request.POST.get('uname')
      passwd = request.POST.get('upassword')
    		
      res = auth.authenticate(request,username=name,password=passwd)
      print(res)
      print(res.username)
      print(res.password)
    return render(request,'login.html')
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    image-20221216061605519

    可以看到,authenticate方法的返回值就是登陆用户的数据对象,可以通过“.字段名”的形式,来获取该对象的信息,比如用户名和密码,当然了密码还是加密的。

  • 校验不通过的情况下: 返回None(演示略)

方法2:login

**作用:**保存用户状态,类似于request.session

代码示例:

from django.contrib import auth
def login(request):
    if request.method == 'POST':
        name = request.POST.get('uname')
        passwd = request.POST.get('upassword')

        user_obj = auth.authenticate(request,username=name,password=passwd)

        if user_obj:
            auth.login(request,user_obj)
            return redirect('/userlist')
        
    return render(request,'login.html')
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 登陆认证装饰器,后面方法5会补充。

方法3:user

**作用:**保存用户状态之后,可以在任意地方使用request.user来获取当前登陆认证的用户数据对象

代码示例:

from django.contrib import auth
def login(request):
    if request.method == 'POST':
        name = request.POST.get('uname')
        passwd = request.POST.get('upassword')

        user_obj = auth.authenticate(request,username=name,password=passwd)

        if user_obj:
            auth.login(request,user_obj)
            return redirect('/userlist')
        
    return render(request,'login.html')

def get_userlist(request):
    data_queryset = models.User.objects.all()

    # 获取当前登陆用户对象在auth_user表中的username字段值
    current_user = request.user.username

    return render(request,'userlist/userlist.html',locals())
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 在做一些比如后台管理界面的时候,可以显示当前登陆的用户信息,如:
  • image-20221218030704648

方法4:is_authenticated()

**作用:**用于校验用户数据对象是否是经过校验的,没登录是匿名用户AnonymousUser,所以该方法就是用来快速判断的,返回值为bool

代码示例:

print(request.user.is_authenticated())
'''
返回值为bool
'''
  • 1
  • 2
  • 3
  • 4

方法5:login_required

**作用:**装饰器函数,被装饰的函数必须要通过校验才可以访问

代码示例:

from django.contrib.auth.decorators import  login_required

@login_required(login_url='/login/')
def get_userlist(request):
    return render(request,'userlist/userlist.html',locals())
  • 1
  • 2
  • 3
  • 4
  • 5
image-20221218043846259

login_required装饰器函数需要传入参数login_url,用于指定当用户没有登陆校验时,跳转到哪个页面,如上图中的登陆页面。

如果很多个视图函数都需要进行装饰,那么推荐使用全局配置。

局部配置

@login_required(login_url='/login/')
  • 1

全局配置

'''
settings文件
'''
LOGIN_URL = '/login/'


'''
视图
'''
@login_required     
def get_userlist(request):
	return render(request,'userlist/userlist.html',locals())
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

如果局部和全局都有配置,以局部为准,优先级局部大于全局。

方法6:check_password

**作用:**用于做密码对比校验

代码推导及示例:

给后台页面书写一个用户修改密码的功能

def change_password(request):
	old_password = request.POST.get('old_password')
	new_password = request.POST.get('new_password')
	# 校验老的密码对不对
	# 如果输入的老密码正确,那就准备修改成新密码
	# 保存到数据库
	return render(request,'change_password.html',locals())
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
'
运行

此时会遇到一个问题,由于表单上提交的数据后端接收后都是明文的,而auth_user表中是加密的密文,所以无法直接进行比对

前文中提到的authenticate方法虽然可以进行密码校验,但是需要传入用户名,直接传入密码进行校验是不行的,而这是修改密码也不会再去获得用户名,所以就衍生出了check_oassword方法。

  • 将输入的老密码与数据库中的密码进行比对成功之后,下一步就是将老密码替修改成新的密码,如何设置见set_password

格式: request.user.check_password( 新密码 ):

@login_required
def set_password(request):
    # <QueryDict: {'oldpassword': ['123'], 'newpassword': ['111'], 'confirmpassword': ['111']}>
    if request.is_ajax():
        if request.method == 'POST':
            back_dic = {'code':1003,'msg':''}

            oldpassword = request.POST.get('oldpassword')
            newpassword = request.POST.get('newpassword')
            confirmpassword = request.POST.get('confirmpassword')

            # 校验老密码是否一致
            if request.user.check_password(oldpassword):
                # 如果老密码正确就对比两次输入的新密码
                if newpassword == confirmpassword:
                    #两次密码也都一致,那就操作数据库
                    request.user.set_password(newpassword)
                    request.user.save()
                    back_dic['msg'] = '修改成功'
                    return JsonResponse(back_dic)
                else:
                    back_dic['code'] = 1005
                    back_dic['msg'] = '两次新密码不一致'
                    return JsonResponse(back_dic)
            else:
                # 如果不正确就返回错误信息
                back_dic['code'] = 1004
                back_dic['msg'] = '原密码错误'
                return JsonResponse(back_dic)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29

方法7:set_password、save

**作用:**用于修改auth_user表中的密码,前者用于修改,后者用于操作数据库完成最终的修改操作

示例:

request.user.set_password('新密码')
request.user.save()

'''
不执行save操作是改变不了数据库中的数据。
'''
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

方法8:logout

**作用:**注销(清除session)

示例:

def logout(request):
    auth.logout(request)
    return render(request,'index.html')
  • 1
  • 2
  • 3
'
运行

方法9:create_user、create_superuser

**作用:**创建用户,auth_user表中新增数据。前者用于创建普通用户,后者用户创建超级用户(了解即可)

示例:

from django.contrib.auth.models import User     #导入表
def register(request):
    if request.method == 'POST':
        username = request.POST.get('username')
        password = request.POST.get('password')
        # 操作auth_user表写入数据
        User.objects.create_user(username=username,password=password)
        return render(request,'login.html')
	return render(request,'register.html')

'''
create_superuser 创建超级用户,了解即可,因为也不会暴露给用户去创建。
	与命令行不同,代码创建必须要传入邮箱。
'''
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

为什么要使用专门的create_user,而不用ORM章节中的create方法呢?

  • 使用create方法虽然可以创建用户,但是密码在存入表中的时候是明文的,此时就authenticatecheck_oassword等方法就都无法使用,并且明文储存密码会有极大的安全风险。

三、扩展auth_user表

原生的auth_user中有很多字段,但是如果现在就想要一个phone字段来存储用户的手机号,或者想要一个reg_time字段来存储注册的时间,那这个时候该怎么办呢。

  • 建立表关系一对一,但是很麻烦,不推荐。
  • 利用面向对象的继承,推荐√。

推导:

  • 下列为导入auth模块和auth_user表的代码,点开User表的源码。

    from django.contrib import auth
    from django.contrib.auth.models import User
    
    • 1
    • 2
    image-20221219235600495

    可以发现,User类中什么字段都没有,都在继承的AbstractUser类当做,所以我们可以通过继承内置的 AbstractUser 类,来定义一个自己的Model类。

配置:

  • 在应用下面的model.py文件中,自定义类。

    from django.contrib.auth.models import AbstractUser
    
    class UserInfo(AbstractUser):
    	# 扩展的自定义字段
        phone = models.BigIntegerField()
    
    • 1
    • 2
    • 3
    • 4
    • 5

如果继承了AbstractUser类,那么在执行数据库迁移命令的时候,auth_user表就不会再创建出来了,而UserInfo表中会出现auth_user所有的字段,外加自己扩展的字段,这样就可以直接点击自己的表,更加快速的完成操作及扩展,就不用再导入auth_user表了。

  • settings配置

    AUTH_USER_MODEL = '应用名.表名(不是类名)'  # 直接应用名.类   中间不要加别的, 不是类名,所以不能大写,app01.User会报错
    
    • 1
    '
    运行

前提:

  • 在继承之前没有执行过数据库迁移命令,也就是说auth_user没有被创建,如果当前库已经创建了那么就需要重新换一个库,所以需要在数据库的设计阶段就要确定好,是否要进行扩展。

  • 继承的类里面不要覆盖AbstractUser里面的字段名,表里面有的字段都不要动,只扩展额外字段即可。

  • 需要在配置文件中告诉django,要用自定义表替代auth_user,不配置会报错,配置如下:

    AUTH_USER_MODEL = '应用名.表名(类名)'
    
    '''
    直接“应用名.表名”,不要再中间再加一个models
    
    另外:
    配置好之后,先执行数据库迁移,不然会报错:
    	ValueError: Dependency on app with no migrations:应用名
    '''
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    '
    运行

当上述的操作都完成,并且具备了前提条件的情况下,就可以执行数据库迁移命令了(具体命令见ORM章节)。

注:

  • 当我们书写了自己的表,代替了auth_user,那么auth模块的功能还是可以照常使用,但是auth_user表不需要导入,改为了自定义的表,并且在使用auth模块的功能时,原本是User的都需要改成自己的表,如:“UserInfo”

    以注册功能为例,因为需要引入auth_user表进行新增数据的操作,当我们更换了表,如:“UserInfo”之后代码就变成了:

    # from django.contrib.auth.models import User     # 由于替换成了UserInfo表,所以就不需要导入User表了。
    from 应用名.models  import  UserInfo
    
    def register(request):
    if request.method == 'POST':
      username = request.POST.get('username')
      password = request.POST.get('password')
      
      # 操作auth_user表写入数据,由于替换成了Userinfo,所以原来的User要进行修改
      # User.objects.create_user(username=username,password=password)
      UserInfo.objects.create_user(username=username,password=password)
      return render(request,'login.html')
    	return render(request,'register.html')
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

3.1 报错:ValueError: Dependency on app with no migrations

在扩展了auth模块的user表之后,需要先执行数据库迁移命令,然后再启动django项目。
直接启动就会报这个错误

python manage.py makemigrations
python manage.py migrate
  • 1
  • 2
声明:本文内容由网友自发贡献,转载请注明出处:【wpsshop】
推荐阅读
相关标签
  

闽ICP备14008679号