參考: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)