Django中使用者權限子產品
1 auth子產品
auth子產品是Django提供的标準權限管理系統,可以提供使用者身份認證, 使用者組和權限管理。
auth可以和admin子產品配合使用, 快速建立網站的管理系統。
在INSTALLED_APPS中添加'django.contrib.auth'使用該APP, auth子產品預設啟用。
2 User屬性與方法
(1) 屬性
User是auth子產品中維護使用者資訊的關系模式(繼承了models.Model), 資料庫中該表被命名為auth_user.
參照後續源碼更清楚的了解User類繼承的屬性與方法.這裡隻是展示一部分.
該資料表在資料庫中:
'auth_user'
"id" integer NOT NULL PRIMARY KEY AUTOINCREMENT,
"password" varchar(128) NOT NULL, "last_login" datetime NULL,
"is_superuser" bool NOT NULL,
"first_name" varchar(30) NOT NULL,
"last_name" varchar(30) NOT NULL,
"email" varchar(254) NOT NULL,
"is_staff" bool NOT NULL,
"is_active" bool NOT NULL,
"date_joined" datetime NOT NULL,
"username" varchar(30) NOT NULL UNIQUE
一般當建立關聯表時需要與User建立關聯,則需要導入使用,比如建議OneToOne聯系
from django.contrib.auth.models import User
class Profile(models.Model):
user = models.OneToOneField(User) #建立關聯到User
blog = models.CharField(maxlength=128, blank=True)
location = models.CharField(maxlength=128, blank=True)
occupation = models.CharField(maxlength=64, blank=True)
(2) 方法
is_anonymous():是否為匿名使用者,如果你已經login,則這個方法傳回始終為false.
is_authenticated():是否通過驗證,也就是通過使用者名和密碼判斷該使用者是否存在
get_group_permissions():得到所有該使用者所屬組别的權限
get_all_permissions():得到該使用者所有的權限.
has_perm(perm):判斷使用者是否具有特定權限,perm的格式是appname.codename
email_user(subject, message, from_email=None):給某使用者發送郵件
例如建立一個superuser裝飾器:
#登入使用者必須為超級使用者,隻能在類中使用
def superuser_required(func):
def _wrapper(self,request,*args,**kwargs):
#User中包含了is_superuser屬性
if not request.user.is_superuser:
#定義的未登入函數 not_authenticated()
return not_authenticated()
return func(self,request,*args,**kwargs)
return _wrapper
這裡設定superuser當然也可以自己設定不同權限的裝飾器,這樣代碼更加簡單!!
3 User常見的用法
(1) 建立使用者
user = User.objects.create_user(username, password)
user.save() #儲存資料庫
auth子產品不存儲使用者密碼明文而是存儲一個Hash值
(2) 驗證登入
from django.contrib.auth import login
username = request.POST['username']
password = request.POST['password']
#login不進行認證,也不檢查is_active标志位, 一般和authenticate配合使用:
user = authenticate(username=username, password=password)
if not user:
if user.is_active:
return HttpResponse(json.dumps({
'error':'使用者名或者密碼錯誤'
}))
login(request,user)
login向session中添加SESSION_KEY, 便于對使用者進行跟蹤.
首先我們要驗證這個使用者,然後再登陸,登陸成功後,我們可以通過request.user 來得到目前登陸的使用者對象
(3) 登出
logout會移除request中user資訊,并重新整理session,清空cookie中sessionid
from django.contrib.auth import logout
def logout_view(request):
logout(request)
(4) 限制非法使用者通路
普通做法通過 request.user.is_authenticated()判定,傳回重定向login登入界面
from django.http import HttpResponseRedirect
def my_view(request):
if not request.user.is_authenticated():
return HttpResponseRedirect('/login.html/')
這種方法會造成重複判定問題
簡單做法采用裝飾器文法糖搞定,django中封裝了login_required
from django.contrib.auth.decorators import login_required
@login_required #過濾掉非登入使用者
def my_view(request):
#限定為合法使用者通路
這樣當你通路my_view的時候,就需要使用者需要通過驗證,不通過可以重定向來解決
4 Group使用者組
django.contrib.auth.models.Group定義了使用者組的模型, 每個使用者組擁有id和name兩個
字段, 該模型在資料庫被映射為auth_group資料表。
User對象中有一個名為groups的多對多字段, 多對多關系由auth_user_groups資料表維護。
Group對象可以通過user_set反向查詢使用者組中的使用者。
# 建立create
group = Group.objects.create(name=group_name)
group.save()
#add
使用者加入使用者組user.groups.add(group)或group.user_set.add(user)
#remove
使用者退出使用者組user.groups.remove(group)或group.user_set.remove(user)
5 Permission
Django的auth系統提供了模型級的權限控制, 即可以檢查使用者是否對某個資料表擁有增(add),
改(change), 删(delete)權限,但無法檢查使用者對某一篇博文是否擁有管理權限。
user.has_perm('blog.add_article')
user.has_perm('blog.change_article')
user.has_perm('blog.delete_article')
user.has_perm方法用于檢查使用者是否擁有操作某個模型權限,若擁有權限則傳回True。僅是進
行權限檢查, 即是使用者沒有權限它也不會阻止程式員執行相關操作。
permission_required修飾器可以代替has_perm并在使用者沒有相應權限時重定向到登入頁或者抛出異常。
# permission_required(perm[, login_url=None, raise_exception=False])
#給blog中article添權重限,裝飾器之後更加簡單
@permission_required('blog.add_article')
def post_article(request):
pass
6 管理使用者權限
User和Permission通過多對多字段user.user_permissions關聯,在資料庫中由
auth_user_user_permissions資料表維護,可以執行添權重限,删除權限,清空權限.
7 User繼承的父類
User先繼承AbstractUser類,該類的基類為AbstractBaseUser,有興趣的了解下,可以更深入的發現User類的功能
附上User繼承類AbstractUser類的基類AbstractBaseUser的源碼:
class AbstractBaseUser(models.Model):
'''
常見的子類User繼承的屬性:
password 密碼
last_login 最後登入
is_active 是否線上
常見定義的方法:
save 儲存
is_authenticated 登入驗證
set_password 設定密碼
check_password 檢查密碼
'''
password = models.CharField(_('password'), max_length=128)
last_login = models.DateTimeField(_('last login'), blank=True, null=True)
is_active = True
REQUIRED_FIELDS = []
class Meta:
abstract = True
def get_username(self):
return getattr(self, self.USERNAME_FIELD)
def __init__(self, *args, **kwargs):
super(AbstractBaseUser, self).__init__(*args, **kwargs)
self._password = None
def __str__(self):
return self.get_username()
def clean(self):
setattr(self, self.USERNAME_FIELD, self.normalize_username(self.get_username()))
def save(self, *args, **kwargs):
super(AbstractBaseUser, self).save(*args, **kwargs)
if self._password is not None:
password_validation.password_changed(self._password, self)
self._password = None
def natural_key(self):
return (self.get_username(),)
@property
def is_anonymous(self):
"""
Always return False. This is a way of comparing User objects to
anonymous users.
"""
return CallableFalse
@property
def is_authenticated(self):
"""
Always return True. This is a way to tell if the user has been
authenticated in templates.
"""
return CallableTrue
def set_password(self, raw_password):
self.password = make_password(raw_password)
self._password = raw_password
def check_password(self, raw_password):
"""
Return a boolean of whether the raw_password was correct. Handles
hashing formats behind the scenes.
"""
def setter(raw_password):
self.set_password(raw_password)
# Password hash upgrades shouldn't be considered password changes.
self._password = None
self.save(update_fields=["password"])
return check_password(raw_password, self.password, setter)
def set_unusable_password(self):
# Set a value that will never be a valid hash
self.password = make_password(None)
def has_usable_password(self):
return is_password_usable(self.password)
def get_full_name(self):
raise NotImplementedError('subclasses of AbstractBaseUser must provide a get_full_name() method')
def get_short_name(self):
raise NotImplementedError('subclasses of AbstractBaseUser must provide a get_short_name() method.')
def get_session_auth_hash(self):
"""
Return an HMAC of the password field.
"""
key_salt = "django.contrib.auth.models.AbstractBaseUser.get_session_auth_hash"
return salted_hmac(key_salt, self.password).hexdigest()
@classmethod
def get_email_field_name(cls):
try:
return cls.EMAIL_FIELD
except AttributeError:
return 'email'
@classmethod
def normalize_username(cls, username):
return unicodedata.normalize('NFKC', force_text(username))
User繼承類AbstractUser類的源碼:
class AbstractUser(AbstractBaseUser, PermissionsMixin):
'''
繼承該類常見屬性有:
username 使用者名
first_name 姓
last_name 名
email 郵箱
'''
username_validator = UnicodeUsernameValidator() if six.PY3 else ASCIIUsernameValidator()
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)
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.'
),
)
date_joined = models.DateTimeField(_('date joined'), default=timezone.now)
objects = UserManager()
EMAIL_FIELD = 'email'
USERNAME_FIELD = 'username'
REQUIRED_FIELDS = ['email']
class Meta:
verbose_name = _('user')
verbose_name_plural = _('users')
abstract = True
def clean(self):
super(AbstractUser, self).clean()
self.email = self.__class__.objects.normalize_email(self.email)
def get_full_name(self):
"""
Returns the first_name plus the last_name, with a space in between.
"""
full_name = '%s %s' % (self.first_name, self.last_name)
return full_name.strip()
def get_short_name(self):
"Returns the short name for the user."
return self.first_name
def email_user(self, subject, message, from_email=None, **kwargs):
"""
Sends an email to this User.
"""
send_mail(subject, message, from_email, [self.email], **kwargs)
注: 1 權限部分參考了原文:
http://www.cnblogs.com/Finley/p/5575305.html非常感謝!!
2 後續我會提供: 注冊/驗證/登入/登出封裝好的類,感謝大家閱讀!!!