[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♛♛♛♛♛♛

posted @ 2020-01-20 21:36  风间悠香  阅读(301)  评论(0编辑  收藏  举报