一、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()函数:
deflogin(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中验证
ifuser_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()视图函数中获取已登录用户信息:
defindex(request):print(request.user.username) #获取已登录的username
return render(request, 'index.html')
如果用户未登录,request.user获取到的是一个匿名用户对象(AnonymousUser Object),request.user.username为None。
在request.user中,除了可以获取username,还可以获取很多用户相关的信息(auth_user表中的字段):
defindex(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 importlogin_required
@login_requireddefmy_view(request):
...
如果用户没有登录,则会跳转到django默认的URL'accounts/login',并传递当前访问URL的绝对路径。
如果要修改该默认的登录URL,则需要在setting中通过LOGIN_URL修改:
如果登录成功,则会跳转到请求的URL。
三、auth提供的其他一些功能
1.创建用户
from django.contrib.auth.models importUser
user= User.objects.create_user(username='用户名',password='密码',email='邮箱',...)
auth 提供的一个创建新用户的方法,需要提供必要参数(username、password)等。
2.创建超级用户
from django.contrib.auth.models importUser
user_obj= User.objects.create_superuser(username='用户名',password='密码',email='邮箱',...)
auth 提供的一个创建新的超级用户的方法,需要提供必要参数(username、password)等。
3.修改密码
用户登录验证通过后,获得了user_obj,调用以下代码修改密码:
user_obj.set_password('新密码')
user_obj.save()
注意,设置完密码,必须要调用用户对象的save方法。。
修改密码示例:
@login_requireddefset_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', '')#检查旧密码是否正确
ifuser.check_password(old_password):if notnew_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 importAbstractUserclassUserInfo(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的源码:
classAbstractUser(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类:
classAbstractBaseUser(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类:
classPermissionsMixin(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♛♛♛♛♛♛