Django一分钟:借助Django的认证系统快速实现RBAC权限校验以及Session会话
引言
Django自带一套认证与权限系统帮助我们快速的实现RBAC权限控制。今天我们要讨论的是Django的认证与权限系统怎么使用,以及Django在背后为我们做了些什么。
一、权限系统会创建数据库表
如果使用了Django的认证系统(在settings中注册app'django.contrib.auth'
),初次执行migrate
迁移,Django会自动在数据库中创建5张表:用户、权限、组以及三者两两之间的关系表。这在RBAC权限管理系统的数据库表设计中非常常见。
- user
- group
- permission
- user_group
- group_permission
- user_permission
在使用Django的认证系统我们需要知道以下几件事:
- 我们可以自己在permission表中创建一些权限,但通常来说不需要,Django在执行数据库迁移时,会自动为已注册app的模型创建增、删、改、查四个权限。
- 我们可以为用户分配权限,本质上就是在
user_permission
关系表中创建一条数据。我们也可以创建一个组,你可以将组命名为“采购部门”,为组分配权限,被分配到这个组中的用户将自动获取这个组的权限。 - 通过
createsuperuser
创建的超级用户会拥有所有的权限(准确来说是自动通过权限认证),普通用户的权限需要自己分配。
二、创建和验证用户
我们可以直接使用Django中的User模型创建用户。创建组和权限也是一样的做法,它们都是普通的模型,只不过Django提前帮我们写好了。
from django.contrib.auth.models import User
user = User.objects.create_user("john", "lennon@thebeatles.com", "johnpassword")
Django不在数据库中存储用户的明文密码,(不在数据库中存储用户的明文密码是基本常识),而是存储散列值。
验证用户密码需要通过内置的authenticate
方法,验证成功会获取获取到对应User对象,在登录的步骤中我们会用到此方法:
from django.contrib.auth import authenticate
user = authenticate(username="john", password="secret")
if user is not None:
print("验证成功")
else:
print("验证失败")
可以重置密码:
from django.contrib.auth.models import User
u = User.objects.get(username="john")
u.set_password("new password")
u.save()
查看用户权限直接使用user对象的has_perm
方法:
user.has_perm("myapp.change_blogpost")
三、登录、登出与会话
Django使用Session(会话)和中间件将身份验证系统和请求对象request
连接在一起
当请求进来时,我们在视图函数中可以获取到request: HttpRequest
我们可以检查user
是否通过授权,即是否登录。
if request.user.is_authenticated:
print("已登录用户")
else:
print("未登录用户")
用户登录需要调用login()
方法。该方法会为传入的user对象在数据库的session表中创建一条数据,这代表该用户的会话信息,会话可以控制用户的登录状态。Django会将SessionID设置到响应头中的SET-COOKIE
字段里返回给用户的浏览器客户端,这样浏览器客户端就会自动存储用户cookie,并为用户以后的每条请求带上cookie,我们后端就能区分用户的身份和会话状态了。
from django.contrib.auth import authenticate, login
ef my_view(request):
username = request.POST["username"]
password = request.POST["password"]
user = authenticate(request, username=username, password=password)
if user is not None:
# 用户通过验证之后就执行登录函数
login(request, user)
登出只需对请求对象使用logout
方法:
def logout_view(request):
logout(request)
四、权限控制
我们可以通过不同的方法检查用户是否登录:
- 通过
request.user.is_authenticated
方法:
def my_view(request):
if not request.user.is_authenticated:
return render(request, "myapp/login_error.html")
- 通过
@login_required
装饰器:
你需要设置settings.LOGIN_URL
控制未登录用户被重定向的页面
from django.contrib.auth.decorators import login_required
@login_required
# @login_required(login_url="/accounts/login/")
def my_view(request): ...
- 在视图类中使用
LoginRequiredMixin
from django.contrib.auth.mixins import LoginRequiredMixin
class MyView(LoginRequiredMixin, View):
# 可选配置
login_url = "/login/"
redirect_field_name = "redirect_to"
控制权限方法也类似:
- 直接检查
def my_view(request):
if not request.user.has_perm("myapp.change_blogpost"):
print("用户无权更改blogpost")
- 使用装饰器
from django.contrib.auth.decorators import permission_required
@permission_required("polls.add_choice")
def my_view(request): ...
你可以提供raise_exception
参数,装饰器将会触发PermissionDenied
,从而触发403(HTTP Forbidden)视图
,比较常用:
from django.contrib.auth.decorators import login_required, permission_required
@login_required
@permission_required("polls.add_choice", raise_exception=True)
def my_view(request): ...
- 在视图类中使用
PermissionRequiredMixin
from django.contrib.auth.mixins import PermissionRequiredMixin
class MyView(PermissionRequiredMixin, View):
permission_required = "polls.add_choice"
# 或者:
permission_required = ["polls.view_choice", "polls.change_choice"]
总结
- Django会在数据库中创建辅助认证和会话系统的表。
- 用户登录和登出会自动创建和删除会话。
- 在视图中获
request
对象手动对用户的身份和权限进行验证,Django内置的装饰器和混入类可以给我们提供帮助。