一、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)
四、扩展默认的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.' ), )
可以看到,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) ......
可以看到,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", ) ......
可以看到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♛♛♛♛♛♛