auth模块

一. auth模块前戏

python 复制代码
# 引入:
    其实我们在创建好一个django项目之后直接执行数据库迁移命令会自动生成很多表 例如:
        django_session
        auth_user

    我们知道django在启动之后就可以直接访问admin路由,需要输入用户名和密码,数据参考的就是auth_user表,并且还必须是管理员用户才能进入,
    现在我们就可以通过在命令行中执行创建超级用户(管理员)的命令, 来创建你的admin用户
        python3 manage.py createsuperuser

# auth模块作用:
    主要就是依赖于auth_user表完成用户相关的所有功能.

二. auth模块方法及使用

主要分为登录认证, 保存用户状态, 判断当前用户是否登陆, 获取当前登陆用户, 登录认证装饰器, 密码核对, 修改密码, 注销当前登录用户, 注册

1. 登录认证

python 复制代码
# 登录认证: 比对用户名和密码是否正确
'''
效验成功返回用户对象. 效验失败返回None.
注意: request可以不指定默认就是None,  而括号内必须同时传入用户名和密码. 不能只指定一个.
'''
from django.contrib import auth
user_obj = auth.authenticated(request, username=username, password=password)
print(user_obj)           # jason  提示: 返回用户对象, 只是内部书写了__str__烦伐. 如果数据不符合则返回None
print(user_obj.username)  # jason
print(user_obj.password)  # pbkdf2_sha256$36000$zeNDf8CkZj7y$b+e/CjzZoAnbBIpvUWgz25ybBDqDzRTmYAHPytxqRYQ=

2. 保存用户状态

python 复制代码
# 保存用户状态:
'''
1. 内部自动查找auth_user标签
2. 内部自动给密码加密再比对
提示: 只要执行了该方法 你就可以在任何地方通过request.user获取到当前登陆的用户对象
'''
auth.login(request, user_obj)   # 内部调用的就是 request.session['key']=user_obj

3. 判断当前用户是否登陆

python 复制代码
# 判断当前用户是否登陆:
request.user.is_authenticated()

4. 获取当前登陆用户

python 复制代码
# 获取当前登陆用户:
'''
1. 获取浏览器提交过来的sessionid对应的随机字符串
2. 后端拿到随机字符串到django_session表中获取数据
    2-1. 如果浏览器提交的随机字符串没有匹配上. 返回 AnonymousUser 匿名用户
    2-2. 如果浏览器提交的随机字符串有匹配上了. 返回对应的用户对象
'''
request.user  # 匹配上返回对应用户对象. 没有匹配上返回 AnonymousUser 对象

5. 登录认证装饰器

python 复制代码
# 登录认证装饰器:
from django.contrib.auth.decorators import login_required
# 局部设置
    @login_required(login_url='/login/')   # login_url 控制跳到指定的页面.  否则会以accounts打头, 跟随你的登录页面路径/login/?next=/my_home/
# 全局设置
    到settings.py中书写: LOGIN_URL = '/login/'
    @login_required    # 之后的装饰器使用就不需要传login_url参数了
# 优先级: 局部 > 全局
# 局部优缺点:
    缺点: 如果争对需要装饰的对象都是在没有登录以后跳转到同一个页面代码就会显得冗余 .
    优点: 不同的视图函数在用户没有登陆的情况下可以跳转到不同的页面
# 全局优缺点:
    缺点: 跳转的页面却很单一
    优点: 全局的好处在于无需重复写代码

6. 密码核对

python 复制代码
# 密码核对: 比对原密码
'''
会将old_password加密与原密码比对. 返回布尔值.
'''
is_correct = request.user.check_password(old_password)

7. 修改密码

python 复制代码
# 修改密码:
'''
第一步仅仅是在修改对象的属性
第二步才是真正的操作数据进行保存
'''
request.user.set_password(new_password)
request.user.save()

8. 注销当前登录用户

python 复制代码
# 注销当前登录用户:
auth.logout(request)  # 内部使用的就是 request.session.flush()

9. 注册

python 复制代码
# 注册:
from django.contrib.auth.models import User
# 操作auth_user表写入数据
# 方式一: 密码无加密. 不推荐使用
User.object.create(username=username, password=password)

# 方式二: 创建普通用户. 密码会进行加密
User.object.create_user(username=username, password=password)

# 方式三: 超级用户创建(了解). 密码会进行加密. 代码创建的email必填. 命令行创建的可以不填
User.object.create_superuser(username=username, password=password, email='111@qq.com')

10. 代码示例

python 复制代码
from django.contrib import auth


def my_login(request):
    if request.method == 'POST':
        username = request.POST.get('username')
        password = request.POST.get('password')
        # 1. 登录认证: 比对用户名和密码是否正确
        '''
        user_obj = auth.authenticate(request, username=username, password=password)
        效验成功返回用户对象. 效验失败返回None.
        注意: request可以不指定默认就是None,  而括号内必须同时传入用户名和密码. 不能只指定一个.
        '''
        user_obj = auth.authenticate(request, username=username, password=password)
        print(user_obj, type(user_obj))  # None/egon  <class 'django.contrib.auth.models.User'>
        if user_obj:
            print('user_obj.username:', user_obj.username)  # egon
            print('user_obj.password:',
                  user_obj.password)  # pbkdf2_sha256$36000$PAy4tWE1NiBa$0vgeQMKwDK0U+F3KqiiiT+eEWmA9kD0Iq+mUON1jPLo=

            # 2. 保存用户状态:
            '''
            1. 内部自动查找auth_user标签
            2. 内部自动给密码加密再比对
            提示: 只要执行了该方法 你就可以在任何地方通过request.user获取到当前登陆的用户对象
            '''
            auth.login(request, user_obj)  # 内部调用的就是 request.session['key']=user_obj

            target_url = request.GET.get('next')
            print('target_url:', target_url)  # target_url: None / target_url: /my_index/
            if target_url:
                obj = redirect(target_url)
            else:
                obj = redirect('/my_home/')
            return obj
    return render(request, 'my_login.html')


from django.contrib.auth.decorators import login_required


# 4. 登录认证装饰器 优先级: 局部 > 全局
@login_required  # 全局
def my_home(request):
    # 3. 获取当前登陆用户:
    '''
    request.user  # 匹配上返回对应用户对象. 没有匹配上返回 AnonymousUser 对象
    1. 获取浏览器提交过来的sessionid对应的随机字符串
    2. 后端拿到随机字符串到django_session表中获取数据
        2-1. 如果浏览器提交的随机字符串没有匹配上. 返回 AnonymousUser 匿名用户
        2-2. 如果浏览器提交的随机字符串有匹配上了. 返回对应的用户对象
    '''
    print('request.user:', request.user)
    print('request.user.is_authenticated():', request.user.is_authenticated())
    return HttpResponse('这是 my_home 页面只有登录了的用户才能进来哦~')


@login_required(login_url='/my_login/')  # 局部
def my_index(request):
    return HttpResponse('这是 my_index 页面只有登录了的用户才能进来哦~')


from django.contrib.auth.models import User


@login_required
def my_set_password(request):
    retain_dict = {  # 保留form表单提交后之前的数据. 这里先占位, 再赋值.
        'old_password': '',
        'new_password': '',
        'confirm_password': '',
    }
    error_dict = {  # 展示form表单输入错误的内容. 这里先占位, 再赋值.
        'old_password': '',
        'new_password': '',
        'confirm_password': '',
    }
    if request.method == 'POST':
        old_password = request.POST.get('old_password')
        new_password = request.POST.get('new_password')
        confirm_password = request.POST.get('confirm_password')
        retain_dict['old_password'] = old_password
        retain_dict['new_password'] = new_password
        retain_dict['confirm_password'] = confirm_password

        if confirm_password == new_password:
            # 5. 密码核对: 比对原密码  会将old_password加密与原密码比对. 返回布尔值.
            is_correct = request.user.check_password(old_password)
            print('is_correct:', is_correct)  # is_correct: True
            if is_correct:
                # 6. 修改密码:
                request.user.set_password(new_password)  # 仅仅是在修改对象的属性
                request.user.save()  # 这一步才是真正的操作数据库
                retain_dict.clear()
                return redirect('/my_login/')
            else:
                error_dict['old_password'] = '原密码不正确, 请核对后在输!'
        else:
            error_dict['new_password'] = '两次密码输入不一致!'
            error_dict['confirm_password'] = '两次密码输入不一致!'

    return render(request, 'my_set_password.html', locals())


@login_required
def logout(request):
    auth.logout(request)  # 内部使用的就是 request.session.flush()
    return redirect("/my_login/")


from django.contrib.auth.models import User
from app01 import models


def my_register(request):
    retain_dict = {
        'username': '',
        'password': '',
        'confirm_password': '',
        'phone': '',
        'addr': '',
    }
    error_dict = {
        'username': '',
        'password': '',
        'confirm_password': '',
        'phone': '',
        'addr': '',
    }

    if request.method == 'POST':
        username = request.POST.get('username')
        password = request.POST.get('password')
        confirm_password = request.POST.get('confirm_password')
        phone = request.POST.get('phone')
        addr = request.POST.get('addr')
        retain_dict['username'] = username
        retain_dict['password'] = password
        retain_dict['confirm_password'] = confirm_password
        retain_dict['phone'] = phone
        retain_dict['addr'] = addr
        print(retain_dict)

        if password == confirm_password:
            user_queryset = models.UserInfo.objects.filter(username=username)
            print('user_queryset:', user_queryset)
            if user_queryset:
                error_dict['username'] = '该用户已经存在!'
            else:
                # 7. 注册:
                # 方式一: 密码无加密. 不推荐使用
                # User.objects.create(username=username, password=password)

                # 方式二: 创建普通用户. 密码会进行加密
                User.objects.create_user(username=username, password=password)

                # 方式三: 超级用户创建(了解). 密码会进行加密. 代码创建的email必填. 命令行创建的可以不填
                # User.objects.create_superuser(username=username, password=password, email='111@qq.com')
                retain_dict.clear()
                return redirect('/my_login/')
        else:
            error_dict['password'] = '两次密码输入不一致!'
            error_dict['confirm_password'] = '两次密码输入不一致!'

    return render(request, 'my_register.html', locals())

三. 扩展auth_user表

python 复制代码
from django.db import models
from django.contrib.auth.models import User
from django.contrib.auth.models import AbstractUser


# 第一种: 建立一对一外键关系  (不推荐)
class UserDetail(models.Model):
    phone = models.BigIntegerField()
    user = models.OneToOneField(to='User')


# 第二种:面向对象的继承
class UserInfo(AbstractUser):
    """
    提示:
        如果继承了AbstractUser
        那么在执行数据库迁移命令的时候auth_user表就不会再创建出来了
        而UserInfo表中会出现auth_user所有的字段外加自己扩展的字段
        这么做的好处在于你能够直接点击你自己的表更加快速的完成操作及扩展

    继承的注意事项:
        1. 继承之前保证没有执行过数据库迁移命令
            保证auth_user没有被创建,如果当前库已经创建了那么你就重新换一个库
        2. 继承的类里面不要覆盖AbstractUser默认的的字段名
            表里面有的字段都不要动,只扩展额外字段即可
        3. 需要在settings.py配置文件中告诉django你要用UserInfo替代auth_user.
            AUTH_USER_MODEL = 'app01.UserInfo'
                              '应用名.表名'
            这句话的含义就类似于我们书写表类的完后, 执行完数据库迁移命令会生成的app01_userinfo这张表
    """
    phone = models.BigIntegerField(verbose_name='用户手机号码')
    addr = models.CharField(max_length=255, verbose_name='家庭住址')


# 使用: auth模块的功能还是照常使用,参考的表页只是由原来的auth_user变成了UserInfo
    # 原来的:
    from django.contrib.auth.models import User
    User.objects.create_user(username=username, password=password)

    # 现在的:
    from app01.models import UserInfo
    UserInfo.objects.create_use

补充: 如果auth_user表已经创建了, 还想使用. 但是你在迁移数据库执行migrate命令的时候, 会抛出异常, 那么操作方法参考如下连接即可:

ValueError: The field admin.LogEntry.user was declared with a lazy reference to 'system.sysuser', bu-CSDN博客

关于django makemigrations/migrate在生成数据表上遇到的一些问题-CSDN博客

四. 总结

python 复制代码
# auth模块前戏
    # 本质: auth模块本质就是在执行完数据库迁移命令以后, 操作django自动生成的auth_user表.   django的admin后台管理需要用到该表

    # 命令行创建超级用户: python3.6 manage.py createsuperuser

    
# auth模块方法及使用
# auth模块主要提供了登录认证, 保存用户状态, 判断当前用户是否登陆, 获取当前登陆用户, 登录认证装饰器, 密码核对, 修改密码, 注销当前登录用户, 注册等功能

    # 登录认证
        from django.contrib import auth
        user_obj = auth.authenticate(request, username=username, password=password)  # 认证成功返回用户对象, 否则返回None

    # 存储用户状态
        提示: 该方法执行完毕之后,在任意位置都可以通过request.user获取到当前登陆的用户对象
        auth.login(username=username, password=password)  # 内部就是操作request.session[key]=user_obj

    # 获取当前用户对象
        request.user

    # 判断当前用户是否登录
        request.user.is_authenticated()

    # 登录认证装饰器
        from django.contrib.auth.decorators import login_required
        # 局部
        @login_required(login_url='/login/')
        @login_required(login_url='/this_login/')

        # 全局
        先到settings.py配置文件中配置: LOGIN_URL = '/login/'
        @login_required

        # 优先级: 局部 > 全局

    # 效验密码
        is_correct = request.user.check_password(old_password)  # 效验结果是布尔值

    # 修改密码
        request.user.set_password(new_password)
        request.user.save()

    # 注册
        from django.contrib.auth.models import User
        User.objects.create(username=username, password=password)           # 密码无加密 , 不推荐使用
        User.objects.create_user(username=username, password=password)      # 创建普通用户
        User.objects.create_superuser(username=username, password=password, email='111@qq.com') # 创建管理员用户, 与命令行创建方式不同的是, 必须指定邮箱

     # 注销
        auth.logout(request)
        

# 扩展auth_user表
   注意事项:
   1. 在没执行过数据库迁移命令之前, 执行了就换库.
   2. 必须要继承AbstractUser类, 且自定义字段不能与其类里面冲突
   3. 还需要到配置文件中进行声明: AUTH_USER_MODEL = '应用名.表名'

   from django.db import models
   from django.contrib.auth.models AbstractUser
   class UserInfo(AbstractUser):
        phone = models.CharField(max_length=255)
        create_time = models.DateField(auto_now_add=True)

   使用方式换汤不换药:
   from app01.models import UserInfo
   User.objects.create_user(username=username, password=password, phone=phone)        
相关推荐
&岁月不待人&21 分钟前
Kotlin by lazy和lateinit的使用及区别
android·开发语言·kotlin
StayInLove24 分钟前
G1垃圾回收器日志详解
java·开发语言
无尽的大道32 分钟前
Java字符串深度解析:String的实现、常量池与性能优化
java·开发语言·性能优化
爱吃生蚝的于勒35 分钟前
深入学习指针(5)!!!!!!!!!!!!!!!
c语言·开发语言·数据结构·学习·计算机网络·算法
binishuaio1 小时前
Java 第11天 (git版本控制器基础用法)
java·开发语言·git
zz.YE1 小时前
【Java SE】StringBuffer
java·开发语言
就是有点傻1 小时前
WPF中的依赖属性
开发语言·wpf
洋2401 小时前
C语言常用标准库函数
c语言·开发语言
进击的六角龙1 小时前
Python中处理Excel的基本概念(如工作簿、工作表等)
开发语言·python·excel
wrx繁星点点1 小时前
状态模式(State Pattern)详解
java·开发语言·ui·设计模式·状态模式