Django身份认证系统详解

目录

Django 验证系统在默认配置下的使用方法

概况

User对象

创建用户

验证用户

权限和授权

默认权限

用户组

以编程方式创建权限

权限缓存

Web请求中的身份验证

用户如何登陆

选择验证后端

用户如何登出¶

限制对登录用户的访问

原始方式

使用login_required装饰器

LoginRequired Mixin

限制对通过测试的登录用户的访问

最简单的方法

user_passes_test方法

class UserPassesTestMixin

permission_required 装饰器


 

Django 验证系统在默认配置下的使用方法

Django带有用户身份验证系统。它处理用户帐户,组,权限和基于cookie的用户会话。

Django身份验证同时提供身份验证和授权,通常称为身份验证系统,因为这些功能在某种程度上是耦合的。

概况

Django身份验证系统处理身份验证和授权。简而言之,身份验证验证用户是否是他们声称的用户,并且授权确定允许经过身份验证的用户做什么。

认证系统由以下部分组成:

  • 用户
  • 权限:(bool)标志指定用户是否可以执行某项任务。
  • 组:向多个用户应用标签和权限的通用方法。
  • 可配置的密码散列系统
  • 用于登录用户或限制内容的表单和视图工具
  • 可插拔的后端系统

一些常见问题的解决方案已在第三方软件包中实现:

  • 密码强度检查
  • 限制登录尝试
  • 针对第三方的身份验证(例如OAuth的)
  • 对象级权限

身份验证支持捆绑为Django contrib模块 django.contrib.auth

User对象

User对象是身份验证系统的核心。它们通常代表与站点交互的人员,用于启用限制访问,注册用户配置文件,将内容与创建者关联等内容.

Django 的认证框架中只有一个user类,例如 “superuseradmin”或“staff”只是具有特殊属性集的用户对象,而不是用户对象的不同类。默认用户的主要属性是:

  • username
  • password
  • emil
  • first_name
  • last_name

创建用户

创建用户最直接的方法是使用包含 create_user() 的函数:

>>> from django.contrib.auth.models import User
>>> user = User.objects.create_user('john', '[email protected]', 'johnpassword')

# At this point, user is a User object that has already been saved
# to the database. You can continue to change its attributes
# if you want to change other fields.
>>> user.last_name = 'Lennon'
>>> user.save()

验证用户

方法:authenticaterequest = None**credentials

源码 ——def authenticate request = None ** credentials ): “”“ 如果给定的凭证有效,则返回一个User对象。 ”“”

使用authenticate()验证用户。将凭据作为参数——默认情况下为用户名和密码,对每个验证后端进行检查,如果凭据对后端有效,则返回用户对象。如果凭证对任何后端无效,或者后端引发PermissionDenied,则返回none。例如:

from django.contrib.auth import authenticate
user = authenticate(username='john', password='secret')
if user is not None:
    # A backend authenticated the credentials
else:
    # No backend authenticated the credentials

request是一个可选的HttpRequest,它在身份验证后端的方法authenticate()上传递。

《《这个一个很底层的验证方法。比如,可以通过 RemoteUserMiddleware 来验证。除非你在编写自己的身份验证系统,否则你可能不会用到它。如果你正在寻找用户登录的方法,请参阅 LoginView 。》》

注解

这是一种验证一组凭据的低级方法; 例如,它被使用了 RemoteUserMiddleware。除非您正在编写自己的身份验证系统,否则您可能不会使用它。相反,如果您正在寻找登录用户的方法,请使用 LoginView

权限和授权

Django带有一个简单的权限系统。它提供了一种为特定用户和用户组分配权限的方法。

它在 Django 管理后台界面里使用,但你也可以在自己的代码中使用它。

Django 的 admin 页面使用了如下权限:

  • 对视图对象的访问权限仅限于具有该类型对象的“视图”或“更改”权限的用户。
  • 查看“添加”表单并添加对象权限仅限于具有该类型对象的“添加”权限的用户。
  • 查看更改列表,查看“更改”表单和更改对象权限仅限于具有该类型对象的“更改”权限的用户。
  • 删除对象的权限仅限于具有该类型对象的“删除”权限的用户。

不仅可以为每个对象类型设置权限,还可以为每个指定对象实例设置权限。通过使用 ModelAdmin 类提供的 has_view_permission()has_add_permission()has_change_permission() 和 has_delete_permission() 方法,可以为同一类型的不同实例定制权限。

User 对象有两个多对多字段:groups 和 user_permissions。 User 对象可以像访问其他 :doc:`Django model `: 一样访问他们的相关对象。

myuser.groups.set([group_list])
myuser.groups.add(group, group, ...)
myuser.groups.remove(group, group, ...)
myuser.groups.clear()
myuser.user_permissions.set([permission_list])
myuser.user_permissions.add(permission, permission, ...)
myuser.user_permissions.remove(permission, permission, ...)
myuser.user_permissions.clear()

默认权限

当 INSTALLED_APPS 设置了 django.contrib.auth 时,它将确保你的每个 Django 模型被创建时有四个默认权限:添加、修改、删除和查看。

运行 manage.py migrate 时将创建这些权限。当你添加 django.contrib.auth 到 INSTALLED_APPS 后第一次运行 迁移 ,将会为所有已经安装过的模型以及现在正在安装的模型创建这些默认的权限。之后,每次你运行 manage.py migrate 都会为新模型创建默认权限 (创建权限的函数连接 post_migrate 信号)。

假设你有一个名为 foo 应用程序和一个名为 Bar 的模型,要测试基础权限,你应该使用:

  • 添加——user.has_perm('foo.add_bar')
  • 修改——user.has_perm('foo.change_bar')
  • 删除——user.has_perm('foo.delete_bar')
  • 查看——user.has_perm('foo.view_bar')

权限模型很少会被直接访问。

用户组

django.contrib.auth.models.Group 模型是对用户进行分类的通用方法,因此您可以将权限或其他标签应用于这些用户。用户可以属于任意数量的组。

组里的用户会自动拥有该组的权限。

除权限外,组是一个方便的途径,可以给用户分类,为其提供一些标签或扩展功能。例如,你可以创建一个组 'Special users',并在编写的代码里让该组成员访问网站仅限会员部分的内容,或者对该组成员发送仅限会员查看的电子邮件。

以编程方式创建权限

虽然可以在模型的Meta类中定义权限,也可以直接创建权限。例如,您可以在myapp中为blogpost模型创建can_publish权限:

from myapp.models import BlogPost
from django.contrib.auth.models import Permission
from django.contrib.contenttypes.models import ContentType

content_type = ContentType.objects.get_for_model(BlogPost)
permission = Permission.objects.create(
    codename='can_publish',
    name='Can Publish Posts',
    content_type=content_type,
)

然后,可以通过 user_permissions 属性将权限分配给 User ,或通过 permissions 属性分配给 Group  

权限缓存

在第一次需要获取用户对象的权限检查时, ModelBackend 才会缓存它们的权限。对于请求-响应周期来说,这通常是很好的,因为权限通常不会在添加的时候立刻检查(例如,在 admin 中)。如果你打算在测试或视图中添加权限,并随后检查他们,最简单的解决方案就是从数据库中重新获取用户。

Web请求中的身份验证

Django 使用 sessions 和中间件将身份验证系统挂接到请求对象中

通过在每次请求中都提供的 request.user 属性来判断用户身份。如果当前没有用户登录,这个属性将会被设置为 AnonymousUser ,否则将会被设置为 User 实例。

你可以使用 is_authenticated 区分两者,例如:

if request.user.is_authenticated:
    # Do something for authenticated users.
    ...
else:
    # Do something for anonymous users.
    ...

 

用户如何登陆

如果有一个已验证的用户想附加到当前会话(session)中,将通过 login()  函数完成。

login(requestuserbackend=None)

"""函数描述——

在请求中保留用户ID和后端。这样用户就不会必须对每个请求重新进行身份验证。匿名会话在用户登录时保留

"""

要在视图中让用户登录,使用 login() 。它需要 HttpRequest 对象和 User 对象。通过 Django 的 session 框架, login() 会在 (用户登录之后)session 中保存用户的ID。

《《

  1. 当用户登录后,生成一个字典{key, value},将字典存入session,key是自动生成的一段字符串标识,返回cookie,value是一个自定义格式化的字典;

》》

注意,在匿名会话期间设置的任何数据都会在用户登录后保留在会话中。

这个例子展示了如何使用 authenticate() 和 login(): :

from django.contrib.auth import authenticate, login

def my_view(request):
    username = request.POST['username']            #获取登录请求中(一般通过表单输入)的username属性
    password = request.POST['password']            #获取登录请求中的password属性
    user = authenticate(request, username=username, password=password)#验证用户——通过获取的属性值
    if user is not None:                           #当用户验证通过时,user为authenticate()方法返回的user对象
        login(request, user)                       #使用该返回的对象进行登陆操作——记录对象Id和后端加入会话;
        # Redirect to a success page.
        ...
    else:
        # Return an 'invalid login' error message.
        ...

选择验证后端

当用户登录时,用户 ID 和用于身份验证的后端会被保存在用户会话中。允许相同的 authentication backend 在未来的请求中获取用户详情。选择要在会话中保存的验证后端如下:

  1. 使用提供了的可选 backend 参数值。
  2. 使用 user.backend  的值。允许配对 authenticate()  和 login() :当返回用户对象时 authenticate() 设置 user.backend 属性。
  3. 使用 AUTHENTICATION_BACKENDS 存在的 backend 。
  4. 否则,抛出一个异常。

在1和2中,backend 参数和 user.backend 属性应该是完整的导入路径(像 AUTHENTICATION_BACKENDS 里的路径一样),而不是真实的后端类。

用户如何登出¶

logout(request)[源代码]¶

如果已经通过 django.contrib.auth.login() 登录的用户想退出登录,可以在视图中使用 django.contrib.auth.logout() 。需要传入 HttpRequest 对象,并且该函数不会返回值。例如:

from django.contrib.auth import logout

def logout_view(request):
    logout(request)
    # Redirect to a success page.

注意,如果用户未登录,logout() 不会报错。

调用 logout() 后,当前请求的会话数据会被全部清除。这是为了防止其他使用同一个浏览器的用户访问前一名用户的会话数据。如果想在登出后立即向用户提供的会话中放入任何内容,请在调用 django.contrib.auth.logout() 之后执行此操作。

限制对登录用户的访问

原始方式

限制访问页面最简单的办法就是检查 request.user.is_authenticated 并重定向到登录页面。

from django.conf import settings
from django.shortcuts import redirect

def my_view(request):
    if not request.user.is_authenticated:
        return redirect('%s?next=%s' % (settings.LOGIN_URL, request.path))
    # ...

或者显示一个错误信息:

from django.shortcuts import render

def my_view(request):
    if not request.user.is_authenticated:
        return render(request, 'myapp/login_error.html')
    # ...

使用login_required装饰器

login_required(redirect_field_name='next'login_url=None)

作为快捷方式,你可以使用 login_required() 装饰器:

from django.contrib.auth.decorators import login_required

@login_required
def my_view(request):
    ...

login_required() 会执行以下操作:

  • 如果用户没有登录,会重定向到 settings.LOGIN_URL ,并传递绝对路径到查询字符串中。例如: /accounts/login/?next=/polls/3/ 。
  • 如果用户已经登录,则正常执行视图。视图里的代码可以假设用户已经登录了。

默认情况下,成功验证时用户跳转的路径保存在名为 "next" 的查询字符串参数中。如果你希望这个参数使用不同名称,请在 login_required() 中传递可选参数 redirect_field_name :

from django.contrib.auth.decorators import login_required

@login_required(redirect_field_name='my_redirect_field')
def my_view(request):
    ...

注意,如果你提供了 redirect_field_name 值,则很可能也需要自定义登录模板,因为存储重定向路径的模板上下文变量使用的是 redirect_field_name 值,而不是 "next" (默认情况下)。

login_required() 也有可选参数 login_url 。例如:

from django.contrib.auth.decorators import login_required

@login_required(login_url='/accounts/login/')
def my_view(request):
    ...

注意,如果你没有指定参数 login_url ,你需要确认 settings.LOGIN_URL  和登录视图是正确关联的。例如,使用默认方式,在 URL 配置文件里添加下面这行:

from django.contrib.auth import views as auth_views

path('accounts/login/', auth_views.LoginView.as_view()),

settings.LOGIN_URL 也接受视图方法名和 named URL patterns 。这样你可以在 URLconf 里自由地重新映射你的登录视图,而不需更新配置文件。

注解

login_required 装饰器不会检查用户的 is_active 标识状态,但默认的 AUTHENTICATION_BACKENDS 会拒绝非正常用户。

参见

如果你打算编写自定义的 Django 管理模块视图(或需要与内置视图使用同样的权限检查),你将会发现 django.contrib.admin.views.decorators.staff_member_required() 装饰器是 login_required() 的一个有用的替代方法。

LoginRequired Mixin

使用基于类的视图时,可以使用 LoginRequiredMixin 实现和 login_required 相同的行为。这个 Mixin 应该在继承列表中最左侧的位置。

如果一个视图使用 Mixin ,那么未经验证用户的所有请求都会被重定向到登录页面或者显示 HTTP 403 Forbidden 错误,这取决于 raise_exception 参数。

你可以设置 AccessMixin 的任何参数来自定义未验证用户的处理:

from django.contrib.auth.mixins import LoginRequiredMixin

class MyView(LoginRequiredMixin, View):
    login_url = '/login/'
    redirect_field_name = 'redirect_to'

注解

同 login_required 装饰器一样,Mixin 不会检查用户的 is_active 标识状态,但默认的 AUTHENTICATION_BACKENDS 会拒绝非正常用户。

限制对通过测试的登录用户的访问

根据某些权限或者其他测试来限制访问,你基本上可以执行和上一节所述同样的操作。

最简单的方法

在视图里直接对 request.user 进行测试。举例,这个视图检查用户是否拥有特定域名的邮箱,否则会重定向到登录页:

from django.shortcuts import redirect

def my_view(request):
    if not request.user.email.endswith('@example.com'):
        return redirect('/login/?next=%s' % request.path)
    # ...

user_passes_test方法

作为快捷方式,你可以方便的调用 user_passes_test 装饰器,当调用返回 False 时会执行重定向。

from django.contrib.auth.decorators import user_passes_test

def email_check(user):
    return user.email.endswith('@example.com')

@user_passes_test(email_check)
def my_view(request):
    ...

user_passes_test() 接受一个必要的参数:一个带有:class:~django.contrib.auth.models.User 对象的调用,如果允许用户访问这个页面,则返回 True 。注意,user_passes_test() 不会自动检查用户是否匿名。

user_passes_test() 可以传递两个可选参数:

login_url

允许你指定用户没有通过测试时跳转的地址。它可能是一个登录页面,如果你没指定,默认是 settings.LOGIN_URL 。

redirect_field_name

与 login_required() 相同。如果你想把没通过检查的用户重定向到没有 "next page" 的非登录页面时,把它设置为 None ,这样它会在 URL 中移除。

例如:

@user_passes_test(email_check, login_url='/login/')
def my_view(request):
    ...

class UserPassesTestMixin

使用基于类的视图时,可以使用 UserPassesTestMixin 执行此操作。

test_func()¶

你必须覆盖类方法 test_func() 以提供执行的测试。此外,还可以设置 AccessMixin 的任何参数来自定义处理未授权用户:

from django.contrib.auth.mixins import UserPassesTestMixin

class MyView(UserPassesTestMixin, View):

    def test_func(self):
        return self.request.user.email.endswith('@example.com')

get_test_func()¶

你也可以覆盖 get_test_func() 方法,以使 mixin 对其检查使用不同名称的函数(而不是 test_func() )。

Stacking UserPassesTestMixin

由于实现了 UserPassesTestMixin 方式,不能在继承列表中堆砌它们。下述方式将不能工作:

class TestMixin1(UserPassesTestMixin):
    def test_func(self):
        return self.request.user.email.endswith('@example.com')

class TestMixin2(UserPassesTestMixin):
    def test_func(self):
        return self.request.user.username.startswith('django')

class MyView(TestMixin1, TestMixin2, View):
    ...

如果 TestMixin1 调用 super() 并把结果考虑在内,TestMixin1 将不能独立运行。

permission_required 装饰器

permission_required(permlogin_url=Noneraise_exception=False)

It's a relatively common task to check whether a user has a particular permission. For that reason, Django provides a shortcut for that case: the permission_required() decorator.:

from django.contrib.auth.decorators import permission_required

@permission_required('polls.can_vote')
def my_view(request):
    ...

Just like the has_perm() method, permission names take the form "." (i.e. polls.can_vote for a permission on a model in the polls application).

The decorator may also take an iterable of permissions, in which case the user must have all of the permissions in order to access the view.

Note that permission_required() also takes an optional login_url parameter:

from django.contrib.auth.decorators import permission_required

@permission_required('polls.can_vote', login_url='/loginpage/')
def my_view(request):
    ...

As in the login_required() decorator, login_url defaults to settings.LOGIN_URL.

If the raise_exception parameter is given, the decorator will raise PermissionDenied, prompting the 403 (HTTP Forbidden) viewinstead of redirecting to the login page.

If you want to use raise_exception but also give your users a chance to login first, you can add the login_required() decorator:

from django.contrib.auth.decorators import login_required, permission_required

@login_required
@permission_required('polls.can_vote', raise_exception=True)
def my_view(request):
    ...

This also avoids a redirect loop when LoginView's redirect_authenticated_user=True and the logged-in user doesn't have all of the required permissions.

The PermissionRequiredMixin mixin¶

To apply permission checks to class-based views, you can use the PermissionRequiredMixin:

class PermissionRequiredMixin

This mixin, just like the permission_required decorator, checks whether the user accessing a view has all given permissions. You should specify the permission (or an iterable of permissions) using the permission_required parameter:

from django.contrib.auth.mixins import PermissionRequiredMixin

class MyView(PermissionRequiredMixin, View):
    permission_required = 'polls.can_vote'
    # Or multiple of permissions:
    permission_required = ('polls.can_open', 'polls.can_edit')

You can set any of the parameters of AccessMixin to customize the handling of unauthorized users.

你可能同样需要重写这些方法:

get_permission_required()¶

Returns an iterable of permission names used by the mixin. Defaults to the permission_required attribute, converted to a tuple if necessary.

has_permission()¶

Returns a boolean denoting whether the current user has permission to execute the decorated view. By default, this returns the result of calling has_perms() with the list of permissions returned by get_permission_required().

你可能感兴趣的:(python)