Django的用户模块与权限系统的示例代码

一 导言

设计一个好的用户系统往往不是那么容易,Django提供的用户系统可以快速实现基本的功能,并可以在此基础上继续扩展以满足我们的需求。

先看看Django的用户系统都提供哪些功能:

  1. 提供用户模块(User Model)
  2. 权限验证(默认添加已有模块的增加删除修改权限)
  3. 用户组与组权限功能
  4. 用户鉴权与登录功能
  5. 与用户登录验证相关的一些函数与装饰方法

如配置了Django的用户系统,仅需调用Django提供的几个函数,便可实现用户的登录,注销,权限验证等功能。例如以下情景

1.登录

# some_view.py
from django.contrib.auth import authenticate, login

def login(request):
  username = request.POST['username']
  password = request.POST['password']

  # Django提供的authenticate函数,验证用户名和密码是否在数据库中匹配
  user = authenticate(username=username, password=password)

  if user is not None:
    # Django提供的login函数,将当前登录用户信息保存到会话key中
    login(request, user)

    # 进行登录成功的操作,重定向到某处等
    ...
  else:
    # 返回用户名和密码错误信息
    ...

2.注销

# some_view.py
from django.contrib.auth import logout

def logout(request):
  # logout函数会清除当前用户保存在会话中的信息
  logout(request)

3.验证是否登录

# some_view.py

def some_fuction(request):
  user = request.user
  if user.is_authenticated:
    # 已登录用户,可以往下进行操作
  else:
    # 返回要求登录信息

4.验证是否有权限

# some_view.py

def some_fuction(request):
  user = request.user
  if user.has_perm('myapp.change_bar'):
    # 有权限,可以往下进行操作
  else:
    # 返回禁止访问等信息

二 用户模块

Django的用户模块类定义在auth应用中,如要直接使用Django的用户类,在setting配置文件中的INSTALLAPP添加一行django.contrib.auth。

这样就可以在代码中直接用User类创建用户:

from django.contrib.auth.models import User
user = User.objects.create_user('john', 'lennon@thebeatles.com', 'johnpassword')
user.last_name = 'Lennon'
user.save()

看看Django的源码User类定义了什么属性:

class User(AbstractUser):
  """
  Users within the Django authentication system are represented by this
  model.
  Username, password and email are required. Other fields are optional.
  """
  class Meta(AbstractUser.Meta):
    swappable = 'AUTH_USER_MODEL'

…,啥都没有?

注意到User类继承AbstractUser类, 用户名和密码信息等定义在父类了。所以再看看AbstractUser类的定义, 限于篇幅仅列出其中一部分,源码在https://github.com/django/django/blob/master/django/contrib/auth/models.py

class AbstractUser(AbstractBaseUser, PermissionsMixin):
  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=30, blank=True)
  email = models.EmailField(_('email address'), blank=True)
  date_joined = models.DateTimeField(_('date joined'), default=timezone.now)
....

可以看到AbstractUser类中定义了username这个字段了,另外还有email、first_name、last_name等。AbstractUser类还继承AbstractBaseUser和PermissionsMixin类,AbstractBaseUser类提供password字段及将密码加密保存的相关方法,PermissionsMixin类提供User类权限认证功能。

总的来说,一个User类,定义了以下字段:

  • username: 用户名
  • password: 密码
  • first_name: 姓名
  • last_name: 姓名
  • email: 邮箱
  • groups: Group类多对多的关系对象管理器
  • user_permissions: Permission类多对多的关系对象管理器
  • is_staff: 是否工作人员
  • is_active: 是否激活
  • is_superuser: 是否管理员
  • last_login: 最近登录时间
  • date_joined: 注册时间

另外,生成一个User实例,可以使用以下属性:

  • is_authenticated: 只读,用来判断用户是否存在。只有AnonymousUser类这个属性为False。
  • is_anonymous: 就是用来区分 User 和 AnonymousUser 对象而已。

User类提供了许多方法方便我们执行各种操作:

  • set_password(raw_password): 可以将password转成hash值,记得再调用user.save()保存。
  • check_password(raw_password): 相反,将原生密码与保存在数据库中hash值密码对比(直接对比肯定不相同的)。
  • has_perm(perm, obj=None),has_perms(perm_list, obj=None): 验证该用户是否拥有某个权限或者权限列表
  • get_all_permissions(obj=None): 返回该用户拥有的所有权限

三 验证与登录用户

验证用户名和密码看起来很简单,对比提交的用户名和密码与数据库保存的一致即可。

如:

# some_view.py
def some_fuction(request):
  username = request.POST['username']
  password = request.POST['password']
  try:
    user = User.objects.get(username=username, password=password)
  except ObjectNotExists:
    # 用户名和密码不匹配一个用户

  ...

由于密码已经进行hash后保存,上述代码还得先把提交的password值先hash再去数据库中搜索,总得多写了一些代码。这些Django都已经考虑到了, 提供了一个authenticate函数验证是否存在该用户,就像导言的实例代码:

# some_view.py
from django.contrib.auth import authenticate, login

def login(request):
  username = request.POST['username']
  password = request.POST['password']
  user = authenticate(username=username, password=password)
  if user is not None:
    login(request, user)
    ...
  else:
    ...

这里重点说明一下authenticate和login函数。

1.authenticate(**credentials)

传入待验证的参数,默认是username和password,它会调用系统配置里的每一个authentication backend进行验证,验证通过返回一个User实例,否则返回None。

每一个authentication backend指认证后端,在setting.py中配置的AUTHENTICATION_BACKENDS变量,默认是[‘django.contrib.auth.backends.ModelBackend'],可以增加自定义的认证后端,或者使用第三方提供的。authenticate函数会按顺序调用每一个进行验证,如果第一个没有通过,它会使用第二个进行验证,直到所有的认证后端都失败后才返回None。

看看这个ModelBackend类如何返回认证用户(再次缩减源码):

class ModelBackend(object):
  def authenticate(self, request, username=None, password=None, **kwargs):
    if username is None:
      username = kwargs.get(UserModel.USERNAME_FIELD)
    try:
      user = UserModel._default_manager.get_by_natural_key(username)
    except UserModel.DoesNotExist:

      UserModel().set_password(password)
    else:
      if user.check_password(password) and self.user_can_authenticate(user):
        return user
# ...

实际上authenticate函数会调用每一个authentication backend类的authenticate方法, ModelBackend类与我们之前的验证方法稍有不同,它先从数据库用户表中取出与username对应的一个User实例,再通过User的check_password方法验证password是否正确,并返回这个User实例。

2.login(request, user, backend=None)

接收HttpRequest对象和一个User对象,login函数会将当前用户信息保存到会话cookie中,所以要使用Django用户系统的所有功能,也得安装Django默认的会话APP。

看看login函数做了什么:

def login(request, user, backend=None):
  # (清空当前的session信息)
  # ...
  request.session[SESSION_KEY] = user._meta.pk.value_to_string(user)
  request.session[BACKEND_SESSION_KEY] = backend
  request.session[HASH_SESSION_KEY] = session_auth_hash
  if hasattr(request, 'user'):
    request.user = user
  rotate_token(request)
  user_logged_in.send(sender=user.__class__, request=request, user=user)

login函数将当前用户的一个唯一标识信息保存在request.session的SESSION_KEY中, 下次请求时即可从会话cookie中拿到当前登录的用户对象。

如果要求登录用户才能访问相应的View,可以这么写:

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))

通过login函数可以将用户信息保存在会话中,当下一个请求到达view前,Django的会话中间件从会话cookie中取出用户对象,并赋值给request.user。这样,已登录的用户request.user.is_authenticated值为True,可以进行相应的操作。未登录用户request.user返回一个AnonymousUser对象,它的is_authenticated属性值永远为False,那么要将这个请求重定向到登录页面。

这两行代码可以通过在view函数上加一个装饰器实现,如

@login_required
def my_view(request):
  ...

装饰器login_required(redirect_field_name='next', login_url=None), 可传入login_url参数设置未登录用户的请求重定向的地址,否则重定向到settings.LOGIN_URL。

四 权限认证

User模型有两个多对多关系的字段: groups 和 user_permissions, 它们与Pemission模型有关。User与Pemission、User与Permission、Group与Permission均是多对多关系, Permission定义了具体的权限,其字段如下:

class Permission(models.Model):
  name = models.CharField(_('name'), max_length=255)   # 权限名称(用作显示)
  content_type = models.ForeignKey(    # 内容类型: 每个模型对应一个内容类型,用于定位指定模型
    ContentType,
    models.CASCADE,
    verbose_name=_('content type'),
  )
  codename = models.CharField(_('codename'), max_length=100)  # 权限的代码名称,用在如has_permission函数参数

给一个用户添加、删除权限很简单: myuser.user_permissions.set([permission_list]) myuser.user_permissions.add(permission, permission, …) myuser.user_permissions.remove(permission, permission, …) myuser.user_permissions.clear()

其中,permission是具体的permission对象。 也可以通过用户所在的组添加相应的权限: group.permissions.set([permission_list]) group.permissions.add(permission, permission, …) group.permissions.remove(permission, permission, …) group.permissions.clear()

只要这个用户加入该组也拥有组权限。通过User对象的get_all_permissions(obj=None)方法可以获得该用户的所有权限列表(字符列表),也可以通过get_group_permissions(obj=None)获得对应的组权限列表。

我们更经常要做的是在view中执行操作之前验证用户是否拥有指定权限,这里一般用到User对象的has_perm(perm, obj=None)方法或者has_perms(perm_list, obj=None)判断。

has_perm(perm, obj=None)方法中perm是”."格式的权限字符串, 如果有这个权限,方法会返回True。同样,`has_perms(perm_list, obj=None)`方法中perm_list中是"."格式的权限字符串列表。

同login_required装饰器,权限认证也有对应的permission_required装饰器:

permission_required(perm, login_url=None, raise_exception=False)

如果raise_exception=True, 权限没有认证通过则抛出PermissionDenied异常,返回默认的403页面。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持我们。

(0)

相关推荐

  • 详解Django之auth模块(用户认证)

    auth模块简介 auth模块是对登录认证方法的一种封装,之前我们获取用户输入的用户名及密码后需要自己从user表里查询有没有用户名和密码符合的对象, 而有了auth模块之后就可以很轻松的去验证用户的登录信息是否存在于数据库中. 除此之外,auth还对session做了一些封装,方便我们校验用户是否已登录 auth里的方法 如果想使用auth模块的方法,必须要先导入auth模块 from django.contrib import auth django.contrib.auth中提供了许多方法

  • 详解Django中的权限和组以及消息

    在认证框架中还有其他的一些功能. 我们会在接下来的几个部分中进一步地了解它们. 权限 权限可以很方便地标识用户和用户组可以执行的操作. 它们被Django的admin管理站点所使用,你也可以在你自己的代码中使用它们. Django的admin站点如下使用权限: 只有设置了 add 权限的用户才能使用添加表单,添加对象的视图. 只有设置了 change 权限的用户才能使用变更列表,变更表格,变更对象的视图. 只有设置了 delete 权限的用户才能删除一个对象. 权限是根据每一个类型的对象而设置的

  • django 实现编写控制登录和访问权限控制的中间件方法

    django中,很多时候我们都需要有一个地方来进行更加详细的权限控制,例如说哪些用户可以访问哪些页面,检查登录状态等,这里的话就涉及到了中间件的编写了. 在django项目下的setting.py文件中,有一个MIDDLEWARE_CLASSES的字段,这里存放的就是中间件,用户的访问会先经过这些中间件的处理之后再给各种views函数进行处理.在这个参数中加入我们接下来要编写的中间件: MIDDLEWARE_CLASSES = ( 'django.contrib.sessions.middlew

  • Django权限机制实现代码详解

    本文研究的主要是Django权限机制的相关内容,具体如下. 1. Django权限机制概述 权限机制能够约束用户行为,控制页面的显示内容,也能使API更加安全和灵活:用好权限机制,能让系统更加强大和健壮.因此,基于Django的开发,理清Django权限机制是非常必要的. 1.1 Django的权限控制 Django用user, group和permission完成了权限机制,这个权限机制是将属于model的某个permission赋予user或group,可以理解为全局的权限,即如果用户A对数

  • Django 内置权限扩展案例详解

    当Django的内置权限无法满足需求的时候就自己扩展吧~ 背景介绍 overmind项目使用了Django内置的权限系统,Django内置权限系统基于model层做控制,新的model创建后会默认新建三个权限,分别为:add.change.delete,如果给用户或组赋予delete的权限,那么用户将可以删除这个model下的所有数据. 原本overmind只管理了我们自己部门的数据库,权限设置只针对具体的功能不针对细粒度的数据库实例,例如用户A 有审核的权限,那么用户A 可以审核所有的DB,此

  • Django的用户模块与权限系统的示例代码

    一 导言 设计一个好的用户系统往往不是那么容易,Django提供的用户系统可以快速实现基本的功能,并可以在此基础上继续扩展以满足我们的需求. 先看看Django的用户系统都提供哪些功能: 提供用户模块(User Model) 权限验证(默认添加已有模块的增加删除修改权限) 用户组与组权限功能 用户鉴权与登录功能 与用户登录验证相关的一些函数与装饰方法 如配置了Django的用户系统,仅需调用Django提供的几个函数,便可实现用户的登录,注销,权限验证等功能.例如以下情景 1.登录 # some

  • Spring Boot中使用 Spring Security 构建权限系统的示例代码

    Spring Security是一个能够为基于Spring的企业应用系统提供声明式的安全访问控制解决方案的安全框架.它提供了一组可以在Spring应用上下文中配置的Bean,为应用系统提供声明式的安全访问控制功能,减少了为企业系统安全控制编写大量重复代码的工作. 权限控制是非常常见的功能,在各种后台管理里权限控制更是重中之重.在Spring Boot中使用 Spring Security 构建权限系统是非常轻松和简单的.下面我们就来快速入门 Spring Security .在开始前我们需要一对

  • Django框架实现在线考试系统的示例代码

    1.Django的简介 Django是一个基于MVC构造的框架.但是在Django中,控制器接受用户输入的部分由框架自行处理,所以 Django 里更关注的是模型(Model).模板(Template)和视图(Views),称为 MTV模式.它们各自的职责如下: 层次 职责 模型(Model),即数据存取层 模型(Model),即数据存取层 模板(Template),即表现层 处理与表现相关的决定: 如何在页面或其他类型文档中进行显示. 视图(View),即业务逻辑层 存取模型及调取恰当模板的相

  • Spring Security实现统一登录与权限控制的示例代码

    目录 项目介绍 统一认证中心 配置授权服务器 配置WebSecurity 登录 菜单 鉴权 资源访问的一些配置 有用的文档 项目介绍 最开始是一个单体应用,所有功能模块都写在一个项目里,后来觉得项目越来越大,于是决定把一些功能拆分出去,形成一个一个独立的微服务,于是就有个问题了,登录.退出.权限控制这些东西怎么办呢?总不能每个服务都复制一套吧,最好的方式是将认证与鉴权也单独抽离出来作为公共的服务,业务系统只专心做业务接口开发即可,完全不用理会权限这些与之不相关的东西了.于是,便有了下面的架构图:

  • SpringBoot中整合Shiro实现权限管理的示例代码

    之前在 SSM 项目中使用过 shiro,发现 shiro 的权限管理做的真不错,但是在 SSM 项目中的配置太繁杂了,于是这次在 SpringBoot 中使用了 shiro,下面一起看看吧 一.简介 Apache Shiro是一个强大且易用的Java安全框架,执行身份验证.授权.密码和会话管理.使用Shiro的易于理解的API,您可以快速.轻松地获得任何应用程序,从最小的移动应用程序到最大的网络和企业应用程序. 三个核心组件: 1.Subject 即"当前操作用户".但是,在 Shi

  • 使用SSM+Layui+Bootstrap实现汽车维保系统的示例代码

    本项目主要实现对汽车维修厂的信息化管理功能,主要包含三个角色:管理员,维修师傅,客户.实现的主要功能包含用户管理.配置管理.汽车管理.故障管理.供应商管理.配件管理.维修订单管理.统计信息.公告管理.个人信息管理.主要业务流程:用户在系统内发起汽车维修申请定单,管理员根据情况将定单分配给维修师傅,维修师傅接受任务后开始维修,并根据情况申请配件,处理完成后由管理员生成最终支付订单结算费用,客户进入系统进行费用支付,并可以查看自己相应的维修记录和费用信息等等. 技术架构: 后台开发:SSM框架 前端

  • Django Channel实时推送与聊天的示例代码

    先来看一下最终的效果吧 开始聊天,输入消息并点击发送消息就可以开始聊天了 点击 "获取后端数据"开启实时推送 先来简单了解一下 Django Channel Channels是一个采用Django并将其功能扩展到HTTP以外的项目,以处理WebSocket,聊天协议,IoT协议等.它基于称为ASGI的Python规范构建. 它以Django的核心为基础,并在其下面分层了一个完全异步的层,以同步模式运行Django本身,但异步处理了连接和套接字,并提供了以两种方式编写的选择,从而实现了这

  • C/C++实现个人收支系统的示例代码

    昨天朋友委托我做一个基于C++的课程小设计,题目如下: 设计一个简单的个人收支管理系统,至少包括如下功能: 1.逐笔输入个人收入或支出的明细,写入到文件保存,可不断进行输入和追加. 输入的明细数据包括:收支明细类别编码.发生日期.金额.备注. 1)为了简化用户的输入,类别编码由字母和数字构成:"a"表示收入."b"表示支 出,数字是大类下的编号.类别编码及类别名称的对应由自己定义.组织在程序中 并输出展示.比如 a1 表示收入类的生活费,b1 表示支出类的学习,编号

  • Springboot+Spring Security实现前后端分离登录认证及权限控制的示例代码

    目录 前言 本文主要的功能 一.准备工作 1.统一错误码枚举 2.统一json返回体 3.返回体构造工具 4.pom 5.配置文件 二.数据库表设计 初始化表数据语句 三.Spring Security核心配置:WebSecurityConfig 四.用户登录认证逻辑:UserDetailsService 1.创建自定义UserDetailsService 2.准备service和dao层方法 五.用户密码加密 六.屏蔽Spring Security默认重定向登录页面以实现前后端分离功能 1.实

  • C#窗体通讯录系统的示例代码

    目录 1.首先创建DBhelp类用来连接数据库 2.创建主页面: 3.添加联系人页面 4.修改页面 5.查询页面 本文主要介绍了C#窗体通讯录系统的示例代码,分享给大家,具体如下: 1.首先创建DBhelp类用来连接数据库 代码如下: using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Data.SqlClient; using System.Data

随机推荐