zoukankan      html  css  js  c++  java
  • [Python自学] Django的认证系统

    一、Django自带的用户认证

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

    此时我们需要实现包括用户注册、用户登录、用户认证、注销、修改密码等功能,这还真是个麻烦的事情呢。

    Django作为一个完美主义者的终极框架,当然也会想到用户的这些痛点。

    它内置了强大的用户认证系统--auth,它默认使用 auth_user 表来存储用户数据。

    1.初始化数据库

    执行初始化数据库:

    python manage.py makemigrations
    python manage.py migrate

    可以看到,在数据库中django创建了一张叫做auth_user的表:

    这张表就是用来供auth模块存放用户信息的表。

    2.创建superuser

    在命令行执行以下命令:

    python manage.py createsuperuser

    然后根据提示,创建用户名和密码,这里我们的用户名为"leo",密码为"leokale1234!"。

    3.在数据库中查看创建好的superuser

    在auth_user表中可以看到已经创建好的superuser:leo,密码是经过SHA256加密的。is_superuser表示leo用户是超级用户。

    二、auth模块

    1.导入auth模块:

    from django.contrib import auth

    2.使用authenticate()进行认证

    该方法只是对用户名密码进行比对验证:

    user_obj = auth.authenticate(username='leokale',password='leokale1234!')

    使用authenticate()验证用户名以及密码是否正确,一般需要username 、password两个关键字参数。

    如果认证成功,则返回一个User对象,并在User对象中设置一个标识该用户以被后端认证。

    3.将认证后的用户对象封装到request中(auth.login()函数)

    在login()视图函数中,使用auth中的login()函数:

    def login(request):
        if request.method == "POST":
            username = request.POST.get("username")
            pwd = request.POST.get("password")
            user_obj = auth.authenticate(username=username, password=pwd)  # 去后台数据库auth_user中验证
            if user_obj:
                auth.login(request, user_obj)   # 将登录的用户封装到request.user中
                return redirect("/index/")  # 如果验证通过,则跳转到index页面
        return render(request, "login.html")  # 如果是get请求,返回login页面

    从此以后,用户的每次访问页面,django的中间件: django.contrib.auth.middleware.AuthenticationMiddleware,就会帮我们根据cookie中的session_id去session中找是否登录,如果已经登录,则将该用户信息封装到request.user中,我们就可以在其他页面对应的视图函数中获取了。这里的中间件帮我们统一做了查询session,获取session的操作。

    4.从request中获取已登录的用户信息

    在index()视图函数中获取已登录用户信息:

    def index(request):
        print(request.user.username)  # 获取已登录的username
        return render(request, 'index.html')

    如果用户未登录,request.user获取到的是一个匿名用户对象(AnonymousUser Object),request.user.username为None。

    在request.user中,除了可以获取username,还可以获取很多用户相关的信息(auth_user表中的字段):

    def index(request):
        print(request.user.username)
        print(request.user.password)  # 密码的SHA256加密
        print(request.user.is_superuser)  # 是否是超级用户
        print(request.user.is_authenticated)  # 判断是否通过了认证
        print(request.user.is_staff)  # 用户是否拥有网站的管理权限
        print(request.user.is_active)  # 是否允许用户登录,设置为False,则禁止用户登录
        return render(request, 'auth_index.html',{"username":request.user.username})

    打印结果:

    leo
    pbkdf2_sha256$180000$JIPSlFr8GvYz$wfS6A2qiyDthNA2+tNIdER1TbSCfc4wTuokZA0s8kCE=
    True
    True
    True
    True

    5.用户注销

    只需要在视图函数中调用auth.logout()即可清除session,实现注销:

    auth.logout(request)

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

    6.使用装饰器快捷的提供登录校验

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

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

    如果用户没有登录,则会跳转到django默认的URL'accounts/login',并传递当前访问URL的绝对路径。

    如果要修改该默认的登录URL,则需要在setting中通过LOGIN_URL修改:

    如果登录成功,则会跳转到请求的URL。

    三、auth提供的其他一些功能

    1.创建用户

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

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

    2.创建超级用户

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

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

    3.修改密码

    用户登录验证通过后,获得了user_obj,调用以下代码修改密码:

    user_obj.set_password('新密码')
    user_obj.save()

    注意,设置完密码,必须要调用用户对象的save方法。。

    修改密码示例:

    @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)
    View Code

    四、扩展默认的auth_user表

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

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

    答案是当然有了。

    我们可以通过继承内置的 AbstractUser 类,来定义一个自己的Model类。然后将默认的auth_user(对应User类,from django.contrib.auth.models import User)表换成使用我们自己定义的表。

    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的时候,要继承AbstractUser。auth_user中的字段都在AbstractUser类中。

    查看AbstractUser的源码

    class AbstractUser(AbstractBaseUser, PermissionsMixin):
        """
        An abstract base class implementing a fully featured User model with
        admin-compliant permissions.
    
        Username and password are required. Other fields are optional.
        """
        username_validator = UnicodeUsernameValidator()
    
        username = models.CharField(
            _('username'),
            max_length=150,
            unique=True,
            help_text=_('Required. 150 characters or fewer. Letters, digits and @/./+/-/_ only.'),
            validators=[username_validator],
            error_messages={
                'unique': _("A user with that username already exists."),
            },
        )
        first_name = models.CharField(_('first name'), max_length=30, blank=True)
        last_name = models.CharField(_('last name'), max_length=150, blank=True)
        email = models.EmailField(_('email address'), blank=True)
        is_staff = models.BooleanField(
            _('staff status'),
            default=False,
            help_text=_('Designates whether the user can log into this admin site.'),
        )
        is_active = models.BooleanField(
            _('active'),
            default=True,
            help_text=_(
                'Designates whether this user should be treated as active. '
                'Unselect this instead of deleting accounts.'
            ),
        )
    View Code

    可以看到,AbstractUser类中定义了username、first_name、last_name、email、is_staff、is_active、date_joined等字段。并且继承于AbstractBaseUser和PermissionsMixin类。

    查看AbstractBaseUser类

    class AbstractBaseUser(models.Model):
        password = models.CharField(_('password'), max_length=128)
        last_login = models.DateTimeField(_('last login'), blank=True, null=True)
        ......
    View Code

    可以看到,AbstractBaseUser类中定义了password和last_login字段。

    再查看PermissionsMixin类

    class PermissionsMixin(models.Model):
        """
        Add the fields and methods necessary to support the Group and Permission
        models using the ModelBackend.
        """
        is_superuser = models.BooleanField(
            _('superuser status'),
            default=False,
            help_text=_(
                'Designates that this user has all permissions without '
                'explicitly assigning them.'
            ),
        )
        groups = models.ManyToManyField(
            Group,
            verbose_name=_('groups'),
            blank=True,
            help_text=_(
                'The groups this user belongs to. A user will get all permissions '
                'granted to each of their groups.'
            ),
            related_name="user_set",
            related_query_name="user",
        )
        user_permissions = models.ManyToManyField(
            Permission,
            verbose_name=_('user permissions'),
            blank=True,
            help_text=_('Specific permissions for this user.'),
            related_name="user_set",
            related_query_name="user",
        )
        ......
    View Code

    可以看到PermissionsMixin类中定义了is_superuser字段,以及groups、user_permissions等多表对应关系。

    定义完model后,需要告诉Django,在setting中设置:

    # 引用Django自带的User表,继承使用时需要设置
    AUTH_USER_MODEL = "app名.UserInfo"

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

    创建普通用户:

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

    创建超级用户:

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

    注意:

    一旦我们指定了新的认证系统所使用的表,我们就需要重新在数据库中创建该表,而不能继续使用原来默认的auth_user表了。也就是说我们不能再使用User类来进行操作,而要使用我们的UserInfo类。

    ♛♛♛♛♛♛End♛♛♛♛♛♛

  • 相关阅读:
    JS-窗体对象 与 事件返回值属性
    JS-事件流操作
    JS-鼠标、键盘事件及事件对象/event
    JS-事件
    JS-DOM样式操作
    JS-DOM节点属性
    AVS 通信模块之AVSConnectionManager
    AVS 通信模块
    AVS 通信协议
    AVS SampleApp
  • 原文地址:https://www.cnblogs.com/leokale-zz/p/12219504.html
Copyright © 2011-2022 走看看