溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

Django的用戶模塊與權(quán)限系統(tǒng)的示例代碼

發(fā)布時間:2020-09-30 01:49:28 來源:腳本之家 閱讀:325 作者:EVE 欄目:開發(fā)技術(shù)

一 導(dǎo)言

設(shè)計一個好的用戶系統(tǒng)往往不是那么容易,Django提供的用戶系統(tǒng)可以快速實現(xiàn)基本的功能,并可以在此基礎(chǔ)上繼續(xù)擴展以滿足我們的需求。

先看看Django的用戶系統(tǒng)都提供哪些功能:

  1. 提供用戶模塊(User Model)
  2. 權(quán)限驗證(默認(rèn)添加已有模塊的增加刪除修改權(quán)限)
  3. 用戶組與組權(quán)限功能
  4. 用戶鑒權(quán)與登錄功能
  5. 與用戶登錄驗證相關(guān)的一些函數(shù)與裝飾方法

如配置了Django的用戶系統(tǒng),僅需調(diào)用Django提供的幾個函數(shù),便可實現(xiàn)用戶的登錄,注銷,權(quán)限驗證等功能。例如以下情景

1.登錄

# some_view.py
from django.contrib.auth import authenticate, login
 
def login(request):
  username = request.POST['username']
  password = request.POST['password']
 
  # Django提供的authenticate函數(shù),驗證用戶名和密碼是否在數(shù)據(jù)庫中匹配
  user = authenticate(username=username, password=password)
 
  if user is not None:
    # Django提供的login函數(shù),將當(dāng)前登錄用戶信息保存到會話key中
    login(request, user)
 
    # 進行登錄成功的操作,重定向到某處等
    ...
  else:
    # 返回用戶名和密碼錯誤信息
    ...

2.注銷

# some_view.py
from django.contrib.auth import logout
 
def logout(request):
  # logout函數(shù)會清除當(dāng)前用戶保存在會話中的信息
  logout(request)

3.驗證是否登錄

# some_view.py
 
def some_fuction(request):
  user = request.user
  if user.is_authenticated:
    # 已登錄用戶,可以往下進行操作
  else:
    # 返回要求登錄信息

4.驗證是否有權(quán)限

# some_view.py
 
def some_fuction(request):
  user = request.user
  if user.has_perm('myapp.change_bar'):
    # 有權(quán)限,可以往下進行操作
  else:
    # 返回禁止訪問等信息

二 用戶模塊

Django的用戶模塊類定義在auth應(yīng)用中,如要直接使用Django的用戶類,在setting配置文件中的INSTALLAPP添加一行django.contrib.auth。

這樣就可以在代碼中直接用User類創(chuàng)建用戶:

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字段及將密碼加密保存的相關(guān)方法,PermissionsMixin類提供User類權(quán)限認(rèn)證功能。

總的來說,一個User類,定義了以下字段:

  • username: 用戶名
  • password: 密碼
  • first_name: 姓名
  • last_name: 姓名
  • email: 郵箱
  • groups: Group類多對多的關(guān)系對象管理器
  • user_permissions: Permission類多對多的關(guān)系對象管理器
  • is_staff: 是否工作人員
  • is_active: 是否激活
  • is_superuser: 是否管理員
  • last_login: 最近登錄時間
  • date_joined: 注冊時間

另外,生成一個User實例,可以使用以下屬性:

  • is_authenticated: 只讀,用來判斷用戶是否存在。只有AnonymousUser類這個屬性為False。
  • is_anonymous: 就是用來區(qū)分 User 和 AnonymousUser 對象而已。

User類提供了許多方法方便我們執(zhí)行各種操作:

  • set_password(raw_password): 可以將password轉(zhuǎn)成hash值,記得再調(diào)用user.save()保存。
  • check_password(raw_password): 相反,將原生密碼與保存在數(shù)據(jù)庫中hash值密碼對比(直接對比肯定不相同的)。
  • has_perm(perm, obj=None),has_perms(perm_list, obj=None): 驗證該用戶是否擁有某個權(quán)限或者權(quán)限列表
  • get_all_permissions(obj=None): 返回該用戶擁有的所有權(quán)限

三 驗證與登錄用戶

驗證用戶名和密碼看起來很簡單,對比提交的用戶名和密碼與數(shù)據(jù)庫保存的一致即可。

如:

# 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:
    # 用戶名和密碼不匹配一個用戶
 
  ...

由于密碼已經(jīng)進行hash后保存,上述代碼還得先把提交的password值先hash再去數(shù)據(jù)庫中搜索,總得多寫了一些代碼。這些Django都已經(jīng)考慮到了, 提供了一個authenticate函數(shù)驗證是否存在該用戶,就像導(dǎo)言的實例代碼:

# 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函數(shù)。

1.authenticate(**credentials)

傳入待驗證的參數(shù),默認(rèn)是username和password,它會調(diào)用系統(tǒng)配置里的每一個authentication backend進行驗證,驗證通過返回一個User實例,否則返回None。

每一個authentication backend指認(rèn)證后端,在setting.py中配置的AUTHENTICATION_BACKENDS變量,默認(rèn)是[‘django.contrib.auth.backends.ModelBackend'],可以增加自定義的認(rèn)證后端,或者使用第三方提供的。authenticate函數(shù)會按順序調(diào)用每一個進行驗證,如果第一個沒有通過,它會使用第二個進行驗證,直到所有的認(rèn)證后端都失敗后才返回None。

看看這個ModelBackend類如何返回認(rèn)證用戶(再次縮減源碼):

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函數(shù)會調(diào)用每一個authentication backend類的authenticate方法, ModelBackend類與我們之前的驗證方法稍有不同,它先從數(shù)據(jù)庫用戶表中取出與username對應(yīng)的一個User實例,再通過User的check_password方法驗證password是否正確,并返回這個User實例。

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

接收HttpRequest對象和一個User對象,login函數(shù)會將當(dāng)前用戶信息保存到會話cookie中,所以要使用Django用戶系統(tǒng)的所有功能,也得安裝Django默認(rèn)的會話APP。

看看login函數(shù)做了什么:

def login(request, user, backend=None):
  # (清空當(dāng)前的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函數(shù)將當(dāng)前用戶的一個唯一標(biāo)識信息保存在request.session的SESSION_KEY中, 下次請求時即可從會話cookie中拿到當(dāng)前登錄的用戶對象。

如果要求登錄用戶才能訪問相應(yīng)的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函數(shù)可以將用戶信息保存在會話中,當(dāng)下一個請求到達view前,Django的會話中間件從會話cookie中取出用戶對象,并賦值給request.user。這樣,已登錄的用戶request.user.is_authenticated值為True,可以進行相應(yīng)的操作。未登錄用戶request.user返回一個AnonymousUser對象,它的is_authenticated屬性值永遠為False,那么要將這個請求重定向到登錄頁面。

這兩行代碼可以通過在view函數(shù)上加一個裝飾器實現(xiàn),如

@login_required
def my_view(request):
  ...

裝飾器login_required(redirect_field_name='next', login_url=None), 可傳入login_url參數(shù)設(shè)置未登錄用戶的請求重定向的地址,否則重定向到settings.LOGIN_URL。

四 權(quán)限認(rèn)證

User模型有兩個多對多關(guān)系的字段: groups 和 user_permissions, 它們與Pemission模型有關(guān)。User與Pemission、User與Permission、Group與Permission均是多對多關(guān)系, Permission定義了具體的權(quán)限,其字段如下:

class Permission(models.Model):
  name = models.CharField(_('name'), max_length=255)   # 權(quán)限名稱(用作顯示)
  content_type = models.ForeignKey(    # 內(nèi)容類型: 每個模型對應(yīng)一個內(nèi)容類型,用于定位指定模型
    ContentType,
    models.CASCADE,
    verbose_name=_('content type'),
  )
  codename = models.CharField(_('codename'), max_length=100)  # 權(quán)限的代碼名稱,用在如has_permission函數(shù)參數(shù)

給一個用戶添加、刪除權(quán)限很簡單: myuser.user_permissions.set([permission_list]) myuser.user_permissions.add(permission, permission, …) myuser.user_permissions.remove(permission, permission, …) myuser.user_permissions.clear()

其中,permission是具體的permission對象。 也可以通過用戶所在的組添加相應(yīng)的權(quán)限: group.permissions.set([permission_list]) group.permissions.add(permission, permission, …) group.permissions.remove(permission, permission, …) group.permissions.clear()

只要這個用戶加入該組也擁有組權(quán)限。通過User對象的get_all_permissions(obj=None)方法可以獲得該用戶的所有權(quán)限列表(字符列表),也可以通過get_group_permissions(obj=None)獲得對應(yīng)的組權(quán)限列表。

我們更經(jīng)常要做的是在view中執(zhí)行操作之前驗證用戶是否擁有指定權(quán)限,這里一般用到User對象的has_perm(perm, obj=None)方法或者has_perms(perm_list, obj=None)判斷。

has_perm(perm, obj=None)方法中perm是”."格式的權(quán)限字符串, 如果有這個權(quán)限,方法會返回True。同樣,`has_perms(perm_list, obj=None)`方法中perm_list中是"."格式的權(quán)限字符串列表。

同login_required裝飾器,權(quán)限認(rèn)證也有對應(yīng)的permission_required裝飾器:

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

如果raise_exception=True, 權(quán)限沒有認(rèn)證通過則拋出PermissionDenied異常,返回默認(rèn)的403頁面。

以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持億速云。

向AI問一下細節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI