这篇文章主要是讲如何让自定义的user模块也能用到django.contrib.auth中的权限管理模块
看这篇文章之前请先看一下我前边的两篇文章,本文以这两篇文章为基础:
下边是一个大概的实现,后边再做详细分析:
1、user model自定义
class AbstractUser(models.Model):
# 登录信息
id = models.AutoField(primary_key=True)
staff = models.IntegerField(default=0, verbose_name=u'员工号')
ename = models.CharField(max_length=30, null=False, blank=False, unique=True, verbose_name=u'英文名')
cname = models.CharField(max_length=30, null=False, blank=True, verbose_name=u'中文名')
deptid = models.IntegerField(default=0, verbose_name=u'部门ID')
deptname = models.CharField(max_length=255, db_index=True, verbose_name=u'部门名')
expiration = models.DateTimeField(null=True)
last_login = models.DateTimeField(default=timezone.now)
is_staff = True
is_active = True
USERNAME_FIELD = 'ename'
REQUIRED_FIELDS = []
class Meta:
verbose_name = 'user'
verbose_name_plural = 'users'
abstract = True
def get_full_name(self):
return u'%s(%s)' % (self.ename, self.cname)
def get_short_name(self):
return self.ename
def get_username(self):
return self.get_full_name()
def __uincode__(self):
return self.get_full_name()
def is_anonymous(self):
return False
def is_authenticated(self):
return True
class User(AbstractUser, PermissionsMixin):
# 组织架构信息
team_id = models.IntegerField(default=0, db_index=True, help_text=u'员工组织架构(id)', null=True)
team_full_id = models.CharField(max_length=255, db_index=True, help_text=u'员工组织架构(路径)', null=True)
# # 权限组信息
# groups = models.ManyToManyField(Group, help_text=u'员工所属用户组')
class Meta:
db_table = 'user'
def has_group(self, group):
if not self.is_active:
return False
if self.is_superuser:
return True
if not hasattr(self, '_group_cache'):
self._group_cache = set([g.name for g in self.groups.all()])
return group in self._group_cache
def __unicode__(self):
return u'%s(%s)' % (self.ename, self.cname)
记得到setting中设置AUTH_USER_MODEL 为自定义user类
2、添加自己的backend类:
class TicketBackend(object):
def __init__(self):
from soap import client
self.passport = client.passport
def authenticate(self, ticket=None):
'''
授权函数
'''
if ticket:
User = get_user_model()
data = self.passport.DecryptTicket(ticket)
if not hasattr(data, 'LoginName'):
return None
#判断权限
if not AuthenticateJudge.hasAuthenticate(data.LoginName):
return None
if data:
ename = unicode(data.LoginName)
try:
user = User.objects.get(ename=ename)
except User.DoesNotExist:
user = User()
user.is_active = True
user.update(data)
user.save()
return user
return None
def get_user(self, user_id):
User = get_user_model()
try:
return User.objects.get(id=user_id)
except User.DoesNotExist:
return None
def get_group_permissions(self, user_obj, obj=None):
"""
Returns a set of permission strings that this user has through his/her
groups.
"""
if user_obj.is_anonymous() or obj is not None:
return set()
if not hasattr(user_obj, '_group_perm_cache'):
if user_obj.is_superuser:
perms = Permission.objects.all()
else:
user_groups_field = get_user_model()._meta.get_field('groups')
user_groups_query = 'group__%s' % user_groups_field.related_query_name()
perms = Permission.objects.filter(**{user_groups_query: user_obj})
perms = perms.values_list('content_type__app_label', 'codename').order_by()
user_obj._group_perm_cache = set(["%s.%s" % (ct, name) for ct, name in perms])
return user_obj._group_perm_cache
def get_all_permissions(self, user_obj, obj=None):
if user_obj.is_anonymous() or obj is not None:
return set()
if not hasattr(user_obj, '_perm_cache'):
user_obj._perm_cache = set(["%s.%s" % (p.content_type.app_label, p.codename) for p in user_obj.user_permissions.select_related()])
user_obj._perm_cache.update(self.get_group_permissions(user_obj))
return user_obj._perm_cache
def has_perm(self, user_obj, perm, obj=None):
if not user_obj.is_active:
return False
return perm in self.get_all_permissions(user_obj, obj)
def has_module_perms(self, user_obj, app_label):
"""
Returns True if user_obj has any permissions in the given app_label.
"""
if not user_obj.is_active:
return False
for perm in self.get_all_permissions(user_obj):
if perm[:perm.index('.')] == app_label:
return True
return False
3、修改settings.py中的AUTHENTICATION_BACKENDS,将授权、权限判断入口设为我们自己的backend类(其实这里可以有多个backend类共存,但是考虑到有些属性、函数对象可能在其他backend不存在而导致异常,所以注释掉系统默认的ModelBackend):
AUTHENTICATION_BACKENDS = [
# 自己的权限判断方法
'users.backend.TicketBackend',
#系统的权限判断方法
# 'django.contrib.auth.backends.ModelBackend',
]
解析:
从《用源码告诉你django权限管理是怎么回事》中我们可以获知,不管是修饰符permission_required 还是 user_passes_test 都是通过调用user.has_perm函数进行权限验证的。为了支持这个接口,我们在自定义自己的AUTH_USER_MODEL类时,我们必须继承 PermissionsMixin 类,这样我们才能使用PermissionsMixin类中提供的函数,包括has_perm,从PermissionMixin的定义我们可以看到,继承了PermissionsMixin类就自动添加了groups、user_permissions属性,这些是使用PermissionsMixin中权限相关的函数所必须的
class PermissionsMixin(models.Model):
"""
A mixin class that adds the fields and methods necessary to support
Django's Group and Permission model using the ModelBackend.
"""
is_superuser = models.BooleanField(_('superuser status'), default=False,
help_text=_('Designates that this user has all permissions without '
'explicitly assigning them.'))
groups = models.ManyToManyField(Group, verbose_name=_('groups'),
blank=True, help_text=_('The groups this user belongs to. A user will '
'get all permissions granted to each of '
'his/her group.'),
related_name="user_set", related_query_name="user")
user_permissions = models.ManyToManyField(Permission,
verbose_name=_('user permissions'), blank=True,
help_text=_('Specific permissions for this user.'),
related_name="user_set", related_query_name="user")
从《用源码告诉你django权限管理是怎么回事》中我们也可以看到通过调用系统的user.has_perm函数从settings.py中的AUTHENTICATION_BACKENDS 中找到相应的backend类,并调用其中的has_perm函数,所以我们需要编写我们自己的backend类,并注册到AUTHENTICATION_BACKENDS 中,这样下来我们就可以通过user.has_perm对我们自定义的user类进行权限验证、授权了。也可以直接调用修饰符:user_passes_test、permission_required。