一 导言
设计一个好的用户系统往往不是那么容易,Django提供的用户系统可以快速实现基本的功能,并可以在此基础上继续扩展以满足我们的需求。
如配置了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.注销
3.验证是否登录
some_view.py some_fuction(request): user = request.user if user.is_authenticated: 已登录用户,可以往下进行操作 返回要求登录信息
4.验证是否有权限
if user.has_perm('myapp.change_bar'): 有权限,可以往下进行操作 返回禁止访问等信息
二 用户模块
Django的用户模块类定义在auth应用中,如要直接使用Django的用户类,在setting配置文件中的INSTALLAPP添加一行django.contrib.auth
。
from django.contrib.auth.models User user = User.objects.create_user('john','[email protected]','johnpassword') user.last_name = Lennon user.save()
class User(AbstractUser): """ Users within the Django authentication system are represented by this model. Username,password and email are required. Other fields are optional. """ Meta(AbstractUser.Meta): swappable = AUTH_USER_MODEL'
注意到User类继承AbstractUser类, 用户名和密码信息等定义在父类了。所以再看看AbstractUser类的定义,限于篇幅仅列出其中一部分,源码在
https://github.com/django/django/blob/master/django/contrib/auth/models.py
AbstractUser(AbstractBaseUser,PermissionsMixin): username = models.CharField( _(),max_length=150,unique=True,help_text=_(required. 150 characters or fewer. Letters,digits and @/./+/-/_ only.[username_validator],error_messages={ unique': _("A user with that username already exists."irst_name = models.CharField(_(first name'),max_length=30,blank=True) last_name = models.CharField(_(last nameTrue) email = models.EmailField(_(email addressTrue) date_joined = models.DateTimeField(_(date joinedtimezone.Now) ....
可以看到AbstractUser类中定义了username这个字段了,另外还有email、first_name、last_name等。AbstractUser类还继承AbstractBaseUser和PermissionsMixin类,AbstractBaseUser类提供password字段及将密码加密保存的相关方法,PermissionsMixin类提供User类权限认证功能。
总的来说,一个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_fuction(request): username = request.POST[] try: user = User.objects.get(username=username,password=password) except ObjectNotExists: 用户名和密码不匹配一个用户 ...
由于密码已经进行hash后保存,上述代码还得先把提交的password值先hash再去数据库中搜索,总得多写了一些代码。这些Django都已经考虑到了, 提供了一个authenticate函数验证是否存在该用户,就像导言的实例代码:
] user = authenticate(username=username,1)"> None: login(request,user) ... : ...
这里重点说明一下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类如何返回认证用户(再次缩减源码):
ModelBackend(object): def authenticate(self,request,username=None,password=None,**kwargs): if username is None: username = kwargs.get(usermodel.USERNAME_FIELD) : user = usermodel._default_manager.get_by_natural_key(username) usermodel.DoesNotExist: usermodel().set_password(password) : if user.check_password(password) and self.user_can_authenticate(user): return user ...
2.login(request,user,backend=None)
接收HttpRequest对象和一个User对象,login函数会将当前用户信息保存到会话cookie中,所以要使用Django用户系统的所有功能,也得安装Django默认的会话APP。
看看login函数做了什么:
def login(request,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中拿到当前登录的用户对象。
from django.conf settings from django.shortcuts redirect my_view(request): if 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,那么要将这个请求重定向到登录页面。
@login_required my_view(request): ...
装饰器login_required(redirect_field_name='next',login_url=None)
, 可传入login_url参数设置未登录用户的请求重定向的地址,否则重定向到settings.LOGIN_UR
四 权限认证
User模型有两个多对多关系的字段: groups 和 user_permissions, 它们与Pemission模型有关。User与Pemission、User与Permission、Group与Permission均是多对多关系, Permission定义了具体的权限,其字段如下:
Permission(models.Model): name = models.CharField(_(name 权限名称(用作显示) content_type = models.ForeignKey( 内容类型: 每个模型对应一个内容类型,用于定位指定模型 ContentType,models.CASCADE,verbose_name=_(content typecodename 权限的代码名称,用在如has_permission函数参数
给一个用户添加、删除权限很简单:
myuser.user_permissions.set([permission_list])
myuser.user_permissions.add(permission,permission,…)
myuser.user_permissions.remove(permission,…)
myuser.user_permissions.clear()
其中,permission是具体的permission对象。 也可以通过用户所在的组添加相应的权限:
group.permissions.set([permission_list])
group.permissions.add(permission,…)
group.permissions.remove(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页面。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 [email protected] 举报,一经查实,本站将立刻删除。