参考:http://www.cnblogs.com/esperyong/
参考:https://docs.djangoproject.com/en/1.8/topics/auth/default/#topic-authorization
在Django的世界中,在权限管理中有内置的Authentication系统。用来管理帐户,组,和许可。还有基于cookie的用户session。
django中内置的权限控制1-User Model
这篇blog主要用来探讨这套内置的Authentication系统。
Django内置的权限系统包括以下三个部分:
用户(Users)
许可(Permissions):用来定义一个用户(user)是否能够做某项任务(task)
组(Groups):一种可以批量分配许可到多个用户的通用方式
首先需要在Django中安装这个组件:
在settings.py配置好数据库连接,运行python manage.py syncdb 。这一步将生成管理界面使用的数据库表。
将'django.contrib.auth'和'django.contrib.contenttypes'放到settings.py中的INSTALLED_APPS中。(使用contenttypes的原因是auth中的Permission模型依赖于contenttypes)
我们可以执行python manage.py shell来启动命令行,对其中的一些API进行学习和使用。
>> User对象 <<
首先最重要的开始就是User模型
User模型对应于一个用户,一个帐户,位于'django.contrib.auth.models'模块中。
User对象有两个多对多的属性分别是:groups和user_permissions
>> 新建User对象 <<
1 from django.contrib.auth.models import User 2 u = User.objects.create_user('test1','test1','aaa')
User对象的Manager,UserManager:
和其他的模型一样,User模型类的objects属性也是一个Manager对象,但是User的Manager对象是自定义的,增加了一些方法:
create_user(username,email=None,password=None)
该方法创建保存一个is_active=True的User对象并返回。username不能够为空,否则抛出ValueError异常。email和password都是可选的。email的domain部分会被自动转变为小写。password如果没有提供,则User对象的set_unusable_password()方法将会被调用。
make_random_password(length=10,allowed_chars='abcdefghjkmnpqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ23456789')
该方法返回一个给定长度和允许字符集的密码。其中默认的allowed_chars有一些字符没有,比如i,l等等。
>> User对象的属性 <<:
username:字符串类型。必填。30个字符以内。
first_name:字符串类型。可选。30个字符以内。
last_name:字符串类型。可选。30个字符以内。
email:可选。
password:明文密码的hash或者是某种元数据。该属性不应该直接赋值明文密码,而应该通过set_password()方法进行赋值,在后面有详细说明TODO。
is_staff:Boolean类型。用这个来判断是否用户可以登录进入admin site。
is_active:Boolean类型。用来判断该用户是否是可用激活状态。在删除一个帐户的时候,可以选择将这个属性置为False,而不是真正删除。这样如果应用有外键引用到这个用户,外键就不会被破坏。
is_superuser:Boolean类型。该属性用来表示该用户拥有所有的许可,而无需明确的赋予给他。
last_login:datetime类型。最近一次登陆时间。
date_joined:datetime类型。创建时间。
可以在新建用户时直接使用这些属性:
1 from django.contrib.auth.models import User 2 u = User.objects.create_user('test1','test1','aaa') 3 u.first_name='a' 4 u.first_name='b' 5 u.save()
>> 修改User对象 <<:
1 from django.contrib.auth.models import User 2 u = User.objects.get(username__exact='test1') 3 u.username='test2' 4 u.save()
注意修改完对象属性需要u.save()。
千万不要直接给User的password属性赋值,应该用set_password()方法进行赋值。
Tips:这些用户属性就是将数据库中用户授权信息的赋值动作。
用python manage.py changepassword *username*来进行修改,需要输入两次密码。
>> User对象方法 <<
像set_password()一样,除了DjangoModel对象的通用方法之外,User对象有以下特有方法:
is_anonymous():
永远返回False.用来将User对象和AnonymousUser(未登录的匿名用户)对象作区分用的识别方法。通常,最好用is_authenticated()方法。
is_authenticated():
永远返回True。该方法不代表该用户有任何的许可,也不代表该用户是active的,而只是表明该用户提供了正确的username和password。
get_full_name():
返回一个字符串,是first_name和last_name中间加一个空格组成。
set_password(raw_password):
调用该方法时候传入一个明文密码,该方法会进行hash转换。该方法调用之后并不会保存User对象。
check_password(raw_password):
如果传入的明文密码是正确的返回True。该方法和set_password是一对,也会考虑hash转换。
set_unusable_password():
将用户设置为没有密码的状态。调用该方法后,check_password()方法将会永远返回false。但是如果,调用set_password()方法重新设置密码后,该方法将会失效,has_usable_password()也会返回True。
has_usable_password():
在调用set_unusable_password()方法之后,该方法返回False,正常情况下返回True。
get_group_permissions(obj=None):
返回该用户通过组所拥有的许可(字符串列表每一个代表一个许可)。obj如果指定,将会返回关于该对象的许可,而不是模型。
get_all_permissions(obj=None):
返回该用户所拥有的所有的许可,包括通过组的和通过用户赋予的许可。
has_perm(perm,obj=None):
如果用户有传入的perm,则返回True。perm可以是一个格式为:'<app label>.<permission codename>'的字符串。如果User对象为inactive,该方法永远返回False。和前面一样,如果传入obj,则判断该用户对于这个对象是否有这个许可。
has_perms(perm_list,obj=None):
和has_perm一样,不同的地方是第一个参数是一个perm列表,只有用户拥有传入的每一个perm,返回值才是True。
has_module_perms(package_name):
传入的是Django app label,按照'<app label>.<permission codename>'格式。当用户拥有该app label下面所有的perm时,返回值为True。如果用户为inactive,返回值永远为False。
email_user(subject,message,from_email=None):
发送一封邮件给这个用户,依靠的当然是该用户的email属性。如果from_email不提供的话,Django会使用settings中的DEFAULT_FROM_EMAIL发送。
get_profile():
返回一个和Site相关的profile对象,用来存储额外的用户信息。这个返回值会在另一片博文中详细描述。
django中内置的权限控制2-Login Logout
而在Web应用中,任何的权限系统要做的第一步就是用户识别,也就是我们常说的登陆(login)。只有正确的登陆校验,知道用户是谁了,才能够知道用户能干什么,那就是许可(Permission)需要负责解决的事情,而Group则是批量设置许可的时候的一个便利手段。
>> 请求用户是否登陆的验证 <<
django有一套方法,可以在每个view方法能够接收到的request对象中增加权限验证相关的方法。要做到这一点,首先需要:
在settings文件中对MIDDLEWARE_CLASSES变量增加上述两个Middleware类SessionMiddleware和AuthenticationMiddleware。
1 MIDDLEWARE_CLASSES = ( 2 'django.contrib.sessions.middleware.SessionMiddleware', 3 'django.middleware.locale.LocaleMiddleware', 4 'django.middleware.common.CommonMiddleware', 5 'django.middleware.csrf.CsrfViewMiddleware', 6 'django.contrib.auth.middleware.AuthenticationMiddleware', 7 'django.contrib.messages.middleware.MessageMiddleware', 8 'django.middleware.transaction.TransactionMiddleware', 9 )
在view中,我们就可以使用request.user获取当前的登陆用户User对象。如果当前用户没有登陆,那么request.user将会是我们之前所说的AnonymousUser对象。我们可以用User对象的is_authenticated()方法将这两者区分开来,我们可以使用django新建一个项目,在view中进行测试:
1 def test(request): 2 if request.user.is_authenticated(): 3 return render_to_response('test.html',{'User_status':'login'}) 4 else: 5 return render_to_response('test.html',{'User_status':'no login'})
在相应的视图函数前面增加@login_required修饰符可以实现非登录用户禁止访问:
from django.contrib.auth.decorators import login_required @login_required def test(request): ...
1、如果用户没登录, 重定向到/accounts/login/(settings.LOGIN_URL),并且把当前绝对URL作为next参数用get方法传递过去
2、如果用户已登录, 正常地执行视图函数
>>authenticate验证<<
使用命令行可以进行测试,authenticate(username,password)函数需要两个参数username,password,如果校验通过则返回User对象,如果校验不通过返回None,例如:
from django.contrib.auth import authenticate, login def my_view(request): user = authenticate(username='root', password='admin') if user is not None: print "User ok" else: print "password err or no user"
>> login_required <<
定义:django.contrib.auth.decorators.login_required([redirect_field_name=REDIRECT_FIELD_NAME,login_url=None])
login_required方法接受两个参数:
redirect_field_name:默认值是next。用来定义登陆成功之后的跳回之前访问界面的url。
login_url:默认值是settings.LOGIN_URL。用来指定登陆界面的url。如果不传入改参数,就需要确保settings.LOGIN_URL的值是正确设置的。
1 from django.contrib.auth.decorators import login_required 2 3 @login_required(login_url='/admin/') 4 def test(request): 5 ...
示例view:
from django.http import HttpResponse,HttpResponseRedirect from django.shortcuts import render_to_response from django.contrib.auth import authenticate, login, logout from django.contrib.auth.decorators import * def webpage_login(request): """ 页面 -> 登录 """ if request.GET.get('next') == None: next_path = '/index/' else: next_path = request.GET.get('next') page = {'next': request.path + '?next=' + next_path} if request.POST.get('user') and request.POST.get('password'): user_str, passwd_str = request.POST.get('user'), request.POST.get('password') if authenticate(username = user_str, password = passwd_str): user_object = authenticate(username = user_str, password = passwd_str) login(request, user_object) return HttpResponseRedirect(request.GET.get('next')) else: return HttpResponse("没有用户或密码错误") else: return render_to_response('login.html', page)
示例template:
<body class="login-bg"> <div class="login-body"> <div class="login-heading"> <h1>Login</h1> </div> <div class="login-info"> <form action="{{ next }}" method="post"> <input type="text" class="user" name="user" placeholder="User" required=""> <input type="password" name="password" class="lock" placeholder="Password"> <input type="submit" name="Sign In" value="Login"> </form> </div> </div> </body>
django中内置的权限控制3-许可(Permission) 和 用户组(Group)
>> 许可(Permissions)<<
当我们在django中安装好了auth应用之后,Django就会为每一个你安装的app中的Model创建三个权限:add/change/delete,执行python manage.py syncdb之后相应的数据会插入到数据库中的。每一次你执行syncdb,Django都会为每个用户给新出现的Model增加这三个权限。
例如,你创建了一个应用叫做school,里面有一个模型叫做StudyGroup,那么你可以用任何一个user对象执行下面的程序,其结果都返回True:
1 user.hash_perm('school.add_studygroup') 2 user.hash_perm('school.change_studygroup') 3 user.hash_perm('school.delete_studygroup')
我们也可以自己定义一些许可,就是在Model类的meta属性中添加permissions定义。比方说,创建了一个模型类叫做Discussion,我们可以创建几个权限来对这个模型的权限许可进行控制,控制某些人可以发起讨论、发起回复,关闭讨论:
1 class Discussion(models.Model): 2 ... 3 class Meta: 4 permissions = ( 5 ("open_discussion", "Can create a discussion"), 6 ("reply_discussion", "Can reply discussion"), 7 ("close_discussion", "Can remove a discussion by setting its status as closed"), 8 )
执行manage.py syncdb就会把增加的权限信息录入到后台数据库。
通过某一个user的user_permissions属性,permission_1为auth_permission表中的id值:
1 user.user_permissions.add(permission_1, permission_2, ...)
删除权限:
1 user.user_permissions.remove(permission_1, permission_2, ...)
通过user的一个组,然后通过group的permissions属性:
1 group.permissions.add(permission_1, permission_2, ...)
我们要判断一个用户是否有发讨论的权限,我们可以用下面的代码:
1 user.has_perm('school.open_discussion')
Permission类和User类没什么特殊的,都是普通的DjangoModel。在第一篇文章中我们详细探讨了User模型的属性和方法。在这里我们探讨一下Permission模型和如何用编程的方式而不是通过预定义然后syncdb的方式创建permission。因为也许在某些时候,需要动态创建并分配权限。
也可以通过Permission.objects.create()方法添加对应app中models的权限,如下app为svn中的模型定义Project添加除默认三个权限外的权限:
1 from svn.models import Project 2 from django.contrib.auth.models import Group, Permission 3 from django.contrib.contenttypes.models import ContentType 4 5 content_type = ContentType.objects.get_for_model(Project) 6 permission = Permission.objects.create(codename='project_admin', 7 name='Department administrator', 8 content_type=content_type)
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· 单线程的Redis速度为什么快?