• Django【十四】内置的anth认证


    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
    </head>
    <body>
    <h1>用户登录:</h1>
    <form action="{% url "user_login" %}" method="post">
        {% csrf_token %}
        用户名:<input type="text" name="username">
        密码:<input type="password" name="password">
        <input type="submit" value="登录">
    
    </form>
    </body>
    </html>
    
    user_login
    user_login

    一、auth认证

    我们在开发一个网站的时候,无可避免的需要设计实现网站的用户系统。此时我们需要实现包括用户注册、用户登录、用户认证、注销、修改密码等功能,这还真是个麻烦的事情呢。

      Django作为一个完美主义者的终极框架,当然也会想到用户的这些痛点。它内置了强大的用户认证系统--auth,它默认使用 auth_user 表来存储用户数据,使用auth模块来进行用户认证,那么需要使用人家django自带的auth_user表来存储用户的信息数据。

      模块导入:

    from django.contrib import auth

      那么有人就有疑问 了,这个auth_user表并不是我们自己在models.py文件中创建的啊,这通过代码怎么操作啊?

      其中一个往auth_user表里面添加数据的命令,可以先多添加几个用户,方便后面操作:

    python manage.py createsuperuser  #要通过这个指令来创建用户,因为这个指令会将你的密码加密。

        

        然后表中就有数据了:这个表里面的数据现在先关注username和password字段就可以了,其他的字段可为空。

        

    user表具有一下字段:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    内置的User模型拥有以下的字段:
    username: 用户名。150个字符以内。可以包含数字和英文字符,以及_、@、+、.和-字符。不能为空,且必须唯一!
    first_name:歪果仁的first_name,在30个字符以内。可以为空。
    last_name:歪果仁的last_name,在150个字符以内。可以为空。
    email:邮箱。可以为空。
    password:密码。经过哈希过后的密码。
    #groups:分组。一个用户可以属于多个分组,一个分组可以拥有多个用户。groups这个字段是跟Group的一个多对多的关系。
    #user_permissions:权限。一个用户可以拥有多个权限,一个权限可以被多个用户所有用。和Permission属于一种多对多的关系。
    is_staff:是否可以进入到admin的站点。代表是否是员工。这个字段如果不使用admin的话,可以自行忽略,不影响使用
    is_active:是否是可用的。对于一些想要删除账号的数据,我们设置这个值为False就可以了,而不是真正的从数据库中删除。
    is_superuser:是否是超级管理员。如果是超级管理员,那么拥有整个网站的所有权限。
    last_login:上次登录的时间。
    date_joined:账号创建的时间。

    二、认证流程

    1、用户注册

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

    普通用户:create_user()

    超级管理员:create_superuser()

    from django.contrib.auth.models import User
    user = User.objects.create_user(username='用户名',password='密码',email='邮箱',...)
    
    user_obj = User.objects.create_superuser(username='用户名',password='密码',email='邮箱',...)

    注册代码:

    复制代码
    # 注册
    def register(request):
    
        if request.method=="GET":
            return render(request,"register.html")
        else:
            username = request.POST.get("username")
            password = request.POST.get("password")
            # 判断用户名是否存在
            if User.objects.filter(username=username).exists():
                return HttpResponse("用户名已经存在")
            # 注册用户
            User.objects.create_user(username=username,password=password)
            return redirect("user_login")
    复制代码

    2、用户登录

    提供了用户认证功能,即验证用户名以及密码是否正确,一般需要username 、password两个关键字参数,因为你仔细看看auth_user表的话,你会发现用户名和密码的字段名称就是username和password。

    如果认证成功(用户名和密码正确有效,就是去auth_user表中查询一下是否存在这条记录),便会返回一个 User 对象,查询认证失败返回None。

    authenticate()会在该 User 对象上设置一个属性来标识后端已经认证了该用户,且该信息在后续的登录过程中是需要的。

    1
    user = auth.authenticate(username='theuser',password='thepassword')

    登录代码:

    复制代码
    # 登录
    def user_login(request):
        if request.method=="GET":
            return render(request,"user_login.html")
        else:
            # 登录验证
            username = request.POST.get("username")
            password = request.POST.get("password")
            # 判断用户名密码是否正确,输入正确返回含用户信息的对象,输入错误返回None
            user_obj = auth.authenticate(username=username,password=password)
            if user_obj:
                # 登录成功设置session(干了和session一样的事情),request,user=user_obj(把查询到的user_obj的model对象给了request.user)
                auth.login(request,user_obj)
                return redirect("home")
            else:
                # 登录失败重定向登录界面
                return redirect("user_login")
    复制代码

    3、首页

    login(HttpRequest, user)

    该函数接受一个HttpRequest对象,以及一个经过认证的User对象。

    该函数实现一个用户登录的功能。它本质上会在后端为该用户生成相关session数据,保持会话用。

    复制代码
    # 首页
    def home(request):
        print(request.user) # 是当前登陆的user对象,并且可以在模板语言里面直接使用{{ request.user.username }},万能的句点号
        print(request.user.id) # user表中的用户id
        print(request.user.username) # 登录用户的名称
        print(request.user.is_active) # 登录状态
        # 判断当前是否是用户登录的
        if request.user.is_authenticated():
                return render(request,"home.html")
        else:
            return redirect("user_login")
    复制代码

    4、注销

    logout(request) 

    该函数接受一个HttpRequest对象,无返回值。

    当调用该函数时,当前请求的session信息会全部清除。该用户即使没有登录,使用该函数也不会报错。

    其实内部就是执行了request.session.flush()

    # 注销
    def logout(request):
        # 清空session
        auth.logout(request)
        return redirect("user_login")

    5、修改密码

    check_password(raw_password)

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

    密码正确返回True,否则返回False。

    1
    ok = user_obj.check_password('密码')

    或者直接针对当前请求的user对象校验原密码是否正确:

    1
    ok = request.user.check_password(raw_password='原密码')

    set_password(raw_password)

    auth 提供的一个修改密码的方法,接收 要设置的新密码 作为参数。

    注意:设置完一定要调用用户对象的save方法!!!

    1
    2
    user_obj.set_password('新密码')  #user_obj其实就是request.user
    user_obj.save()                 #request.user.save()

    修改代码:

    复制代码
    # 修改密码
    def set_password(request):
        if request.method=="GET":
            return render(request,"set_password.html")
        else:
    
            old_password = request.POST.get("old_password")
            new_password = request.POST.get("new_password")
            r_new_password = request.POST.get("r_new_password")
            print(new_password)
            print(r_new_password)
            # 判断用户输入的旧密码是否与数据库中的一致
            if request.user.check_password(old_password):
                if new_password != r_new_password:
                    return HttpResponse("两次输入的密码不一致")
                else:
                    # 修改密码
                    request.user.set_password(new_password)
                    request.user.save()
                    return redirect("user_login")
            else:
                return HttpResponse("旧密码输入有误!!!")
    复制代码

    用户对象的属性

    user_obj能够拿到认证所用用户表的数据属性,比如username, password等。

    其他常用属性含义如下:

       is_staff : 用户是否拥有网站的管理权限.

       is_active : 是否允许用户登录, 设置为 False,可以在不删除用户的前提下禁止用户登录。

    注意:

    只要使用login(request, user_obj)之后,request.user就能拿到当前登录的用户对象。否则request.user得到的是一个匿名用户对象(AnonymousUser Object,是request.user的默认值),这个匿名用户的状态在我的视图函数博客的那个request那一节有介绍,都是空。

    详细原理请查看 AuthenticationMiddleware 中间件源码。

    使用login方法之前,打印user的状态

    def index(request):
        print(request.user) #没有经过login方法来封装用户的信息,那么这个显示的是一个匿名用户
        print(request.user.id) #None
        print(request.user.username) #空的
        print(request.user.is_active) #False
        return render(request,'index.html')

    使用login方法之后,打印user的状态

    复制代码
    def index(request):
        print(request.user) #chao,request.user对象本身是全局的,是当前登陆的user对象,并且可以在模板语言里面直接使用{{ request.user.username }},万能的句点号
        print(request.user.id) #1  #通过id、username等可以判断用户是否登陆了,但是一般我们都用后面要学的is_authenticated()方法来进行判断。
        print(request.user.username) #chao
        print(request.user.is_active) #True
        return render(request,'index.html')
    复制代码

    三、整套认证流程代码

    from django.shortcuts import render,HttpResponse,redirect
    from django.contrib.auth.models import User
    from django.contrib import auth
    # Create your views here.
    
    # 注册
    def register(request):
    
        if request.method=="GET":
            return render(request,"register.html")
        else:
            username = request.POST.get("username")
            password = request.POST.get("password")
            # 判断用户名是否存在
            if User.objects.filter(username=username).exists():
                return HttpResponse("用户名已经存在")
            # 注册用户
            User.objects.create_user(username=username,password=password)
            return redirect("user_login")
    
    # 登录
    def user_login(request):
        if request.method=="GET":
            return render(request,"user_login.html")
        else:
            # 登录验证
            username = request.POST.get("username")
            password = request.POST.get("password")
            # 判断用户名密码是否正确,输入正确返回含用户信息的对象,输入错误返回None
            user_obj = auth.authenticate(username=username,password=password)
            if user_obj:
                # 登录成功设置session(干了和session一样的事情),request,user=user_obj(把查询到的user_obj的model对象给了request.user)
                auth.login(request,user_obj)
                return redirect("home")
            else:
                # 登录失败重定向登录界面
                return redirect("user_login")
    
    # 首页
    def home(request):
        print(request.user) # 是当前登陆的user对象,并且可以在模板语言里面直接使用{{ request.user.username }},万能的句点号
        print(request.user.id) # user表中的用户id
        print(request.user.username) # 登录用户的名称
        print(request.user.is_active) # 登录状态
        # 判断当前是否是用户登录的
        if request.user.is_authenticated():
                return render(request,"home.html")
        else:
            return redirect("user_login")
    
    # 注销
    def logout(request):
        # 清空session
        auth.logout(request)
        return redirect("user_login")
    
    # 修改密码
    def set_password(request):
        if request.method=="GET":
            return render(request,"set_password.html")
        else:
    
            old_password = request.POST.get("old_password")
            new_password = request.POST.get("new_password")
            r_new_password = request.POST.get("r_new_password")
            print(new_password)
            print(r_new_password)
            # 判断用户输入的旧密码是否与数据库中的一致
            if request.user.check_password(old_password):
                if new_password != r_new_password:
                    return HttpResponse("两次输入的密码不一致")
                else:
                    # 修改密码
                    request.user.set_password(new_password)
                    request.user.save()
                    return redirect("user_login")
            else:
                return HttpResponse("旧密码输入有误!!!")
    
    视图函数
    视图函数
    from django.shortcuts import render,HttpResponse,redirect
    from django.contrib.auth.models import User
    from django.contrib import auth
    # Create your views here.
    
    # 注册
    def register(request):
    
        if request.method=="GET":
            return render(request,"register.html")
        else:
            username = request.POST.get("username")
            password = request.POST.get("password")
            # 判断用户名是否存在
            if User.objects.filter(username=username).exists():
                return HttpResponse("用户名已经存在")
            # 注册用户
            User.objects.create_user(username=username,password=password)
            return redirect("user_login")
    
    # 登录
    def user_login(request):
        if request.method=="GET":
            return render(request,"user_login.html")
        else:
            # 登录验证
            username = request.POST.get("username")
            password = request.POST.get("password")
            # 判断用户名密码是否正确,输入正确返回含用户信息的对象,输入错误返回None
            user_obj = auth.authenticate(username=username,password=password)
            if user_obj:
                # 登录成功设置session(干了和session一样的事情),request,user=user_obj(把查询到的user_obj的model对象给了request.user)
                auth.login(request,user_obj)
                return redirect("home")
            else:
                # 登录失败重定向登录界面
                return redirect("user_login")
    
    # 首页
    def home(request):
        print(request.user) # 是当前登陆的user对象,并且可以在模板语言里面直接使用{{ request.user.username }},万能的句点号
        print(request.user.id) # user表中的用户id
        print(request.user.username) # 登录用户的名称
        print(request.user.is_active) # 登录状态
        # 判断当前是否是用户登录的
        if request.user.is_authenticated():
                return render(request,"home.html")
        else:
            return redirect("user_login")
    
    # 注销
    def logout(request):
        # 清空session
        auth.logout(request)
        return redirect("user_login")
    
    # 修改密码
    def set_password(request):
        if request.method=="GET":
            return render(request,"set_password.html")
        else:
    
            old_password = request.POST.get("old_password")
            new_password = request.POST.get("new_password")
            r_new_password = request.POST.get("r_new_password")
            print(new_password)
            print(r_new_password)
            # 判断用户输入的旧密码是否与数据库中的一致
            if request.user.check_password(old_password):
                if new_password != r_new_password:
                    return HttpResponse("两次输入的密码不一致")
                else:
                    # 修改密码
                    request.user.set_password(new_password)
                    request.user.save()
                    return redirect("user_login")
            else:
                return HttpResponse("旧密码输入有误!!!")
    
    视图函数
    register
    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
    </head>
    <body>
    <h1>当前登录用户:{{ request.user.username }}</h1>
    <a href="{% url "logout" %}">注销</a>
    <a href="{% url "set_password" %}">修改密码</a>
    </body>
    </html>
    
    home
    home
    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
    </head>
    <body>
    <h1>修改密码</h1>
    <form action="{% url "set_password" %}" method="post">
        {% csrf_token %}
        旧密码:<input type="password" name="old_password">
        新密码:<input type="password" name="new_password">
        确认密码:<input type="password" name="r_new_password">
        <input type="submit" value="提交">
    </form>
    </body>
    </html>
    
    set_password
    set_password

    校验用户是否登录

    user对象的 is_authenticated()

    如果是真正的 User 对象,返回值恒为 True 。 用于检查用户是否已经通过了认证。
        通过认证并不意味着用户拥有任何权限,甚至也不检查该用户是否处于激活状态,这只是表明用户成功的通过了认证。 这个方法很重要, 在后台用request.user.is_authenticated()判断用户是否已经登录,如果true则可以向前台展示request.user.name

    要求:

      1  用户登陆后才能访问某些页面,

      2  如果用户没有登录就访问该页面的话直接跳到登录页面

      3  用户在跳转的登陆界面中完成登陆后,自动访问跳转到之前访问的地址

    方法1:

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

    方法2:

    django已经为我们设计好了一个用于此种情况的装饰器:login_requierd()

    from django.contrib.auth.decorators import login_required
          
    @login_required
    def my_view(request):
      ...

    若用户没有登录,则会跳转到django默认的 登录URL '/accounts/login/ ' (这个值可以在settings文件中通过LOGIN_URL进行修改)。并传递  当前访问url的绝对路径 (登陆成功后,会重定向到该路径)。

    login_requierd()

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

        用法:

    from django.contrib.auth.decorators import login_required
          
    @login_required
    def my_view(request):
      ...

        若用户没有登录,则会跳转到django默认的 登录URL '/accounts/login/ ' 并传递当前访问url的绝对路径 (登陆成功后,会重定向到该路径)。

        如果需要自定义登录的URL,则需要在settings.py文件中通过LOGIN_URL进行修改。

        示例:

    LOGIN_URL = '/login/'  # 这里配置成你项目登录页面的路由

    登录成功之后跳转页面实列:

    前端跳转:

    用户直接请求修改密码的界面,因为没有登录所以必须先登录,并且会记录用户登录成功后需要跳转的下一个页面,

    取到/set_password/路径,判断如果取到路径就跳转,如果没有就跳转指定的路径下(首页)

    后端跳转:

     

    四、扩展auth_user表

    这内置的认证系统这么好用,但是auth_user表字段都是固定的那几个,我在项目中没法拿来直接使用啊!

      比如,我想要加一个存储用户手机号的字段,怎么办?

      聪明的你可能会想到新建另外一张表然后通过一对一和内置的auth_user表关联,这样虽然能满足要求但是有没有更好的实现方式呢?

      答案是当然有了。

      我们可以通过继承内置的 AbstractUser 类,来定义一个自己的Model类。django给我们自动创建的一张user表,而如果要用auth模块,就必须要使用(或继承)这张表。

      这样既能根据项目需求灵活的设计用户表,又能使用Django强大的认证系统了。继承表的好处是我们可以增加一些自己需要的字段,并且同时可以使用auth模块提供的接口、方法

    复制代码
    复制代码
    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
    复制代码
    复制代码

      需要注意的是,UserInfo表里就不需要有auth_user里重复的字段了,比如说username以及password等,但是还是可以直接使用这些字段的,并且django会自动将password进行加密

      按上面的方式扩展了内置的auth_user表之后,一定要在settings.py中告诉Django,我现在使用我新定义的UserInfo表来做用户认证。写法如下:

    # 引用Django自带的User表,继承使用时需要设置,这样django就知道从我们的app名的应用下的models文件中去查找UserInfo这张表了
    AUTH_USER_MODEL = "app名.UserInfo"

      自定义认证系统默认使用的数据表之后,我们就可以像使用默认的auth_user表那样使用我们的UserInfo表了。比如:

      创建普通用户:

    UserInfo.objects.create_user(username='用户名', password='密码')

      创建超级用户:

    UserInfo.objects.create_superuser(username='用户名', password='密码')

      再次注意:

        一旦我们指定了新的认证系统所使用的表,我们就需要重新在数据库中创建该表,而不能继续直接使用原来默认的auth_user表了。

  • 相关阅读:
    day_07 深浅拷贝
    day_06 再谈编码
    day_05 字典
    day_04 列表
    day_03 字符串
    HDU 1049 Climbing Worm
    HDU 1720 A+B Coming
    Pascal向C++的跨越
    B-Boxes
    喵哈哈村的狼人杀大战(4)
  • 原文地址:https://www.cnblogs.com/youxiu123/p/11603324.html
Copyright © 2020-2023  润新知