Python裝飾器實(shí)現(xiàn)幾類(lèi)驗(yàn)證功能做法實(shí)例
最近新需求來(lái)了,要給系統(tǒng)增加幾個(gè)資源權(quán)限。盡量減少代碼的改動(dòng)和程序的復(fù)雜程度。所以還是使用裝飾器比較科學(xué)
之前用了一些登錄驗(yàn)證的現(xiàn)成裝飾器模塊。然后仿寫(xiě)一些用戶管理部分的權(quán)限裝飾器。
比如下面這種
def permission_required(permission): def decorator(f): @wraps(f) def decorated_function(*args, **kwargs): if not current_user.can(permission): abort(403) return f(*args, **kwargs) return decorated_function return decorator def admin_required(f): return permission_required(Permission.SMY)(f)
調(diào)用權(quán)限的時(shí)候很好理解。直接仿寫(xiě)admin_required的格式就好了。然后每個(gè)頁(yè)面入口用語(yǔ)法糖這樣寫(xiě): @admin_required
于是頁(yè)面的入口權(quán)限就做好了。但是資源權(quán)限和頁(yè)面權(quán)限不同。上面內(nèi)容中提到的permission是寫(xiě)在model.py的靜態(tài)內(nèi)容里面的。
從封裝來(lái)看,至少是看不出來(lái)哪個(gè)地方暴露了用戶查詢的方法(菜鳥(niǎo)水平下)。只能簡(jiǎn)單的看出來(lái)if判斷的時(shí)候似乎使用了current_user這個(gè)變量的內(nèi)置方法
但是current_user其實(shí)是一個(gè)第三方的包的內(nèi)容,和登錄模塊引入的包相同,是一整套記錄token信息的代碼。詳細(xì)內(nèi)容太多。從這個(gè)地方出發(fā)去寫(xiě),會(huì)go die
因?yàn)槟呐挛抑榔鋵?shí)調(diào)用的.can(permission)是model類(lèi)里面定義的類(lèi)方法。可是current_user是取了哪個(gè)部分的東西還是不清楚。
所以不管它。從頭來(lái)梳理一下裝飾器的內(nèi)容。
首先一個(gè)簡(jiǎn)單的裝飾器寫(xiě)法是很好理解的。比如原函數(shù)是這樣寫(xiě)的:
def page(): if user == 'admin': form = Form() if request.method=='POST': db.session.add(form) db.session.commit() flash("success") return 0
這當(dāng)然是隨便寫(xiě)的一個(gè)函數(shù)(明顯有很多問(wèn)題),只是用來(lái)表達(dá)一個(gè)過(guò)程。首先通過(guò)路由調(diào)用這個(gè)函數(shù)的時(shí)候,會(huì)先執(zhí)行第一個(gè)if判斷。這個(gè)判斷即我們想要的驗(yàn)證內(nèi)容
驗(yàn)證通過(guò)以后,說(shuō)明用戶可以訪問(wèn)這個(gè)頁(yè)面,然后頁(yè)面內(nèi)容會(huì)渲染出來(lái),交互功能也被允許……
那么裝飾器,就是把這個(gè)if的功能提取出來(lái)了。那么原函數(shù)寫(xiě)成這樣的形式:
@admin_check def page(): form = Form() if request.method=='POST': db.session.add(form) db.session.commit() flash("success") return 0
單從這個(gè)函數(shù)來(lái)說(shuō),這樣寫(xiě)并沒(méi)有任何好處,似乎本來(lái)一行代碼搞定的問(wèn)題,多用了幾行代碼。我們展開(kāi)這個(gè)形式的完整代碼看一下:
def admincheck(func): if user=='admin': return func def page(): form = Form() if request.method=='POST': db.session.add(form) db.session.commit() flash("success") return 0 page = admincheck(page())
上面的裝飾器只是把page=admincheck這一句寫(xiě)成了@模式。
但是這種寫(xiě)法只能解決最基本的驗(yàn)證問(wèn)題。也就是相對(duì)獨(dú)立的入口驗(yàn)證。這個(gè)驗(yàn)證還沒(méi)有拿到程序傳遞到page()函數(shù)當(dāng)中的參數(shù)。也就是說(shuō),這個(gè)驗(yàn)證這么看起來(lái)沒(méi)什么用處
不過(guò)機(jī)制是這樣。接下來(lái)就可以研究怎樣的做法是把路由傳遞過(guò)來(lái)的請(qǐng)求數(shù)據(jù)進(jìn)行驗(yàn)證然后繼續(xù)執(zhí)行的了。
def admincheck(func): def inner(arg): if user == 'admin': if arg == 'false': abort(403) return func(arg) return inner
同樣的,多個(gè)參數(shù)的時(shí)候,只需要把 def inner(arg)改寫(xiě)成def inner(arg1,arg2)
n個(gè)參數(shù)的時(shí)候,則寫(xiě)成def inner(*args,**kwargs) 這個(gè)需要注意一下。*args是元組,即('user',1);**kwargs是字典,即{'user':1}
同時(shí)寫(xiě)這兩個(gè)形參的話,基本上就能處理所有傳遞進(jìn)來(lái)的參數(shù)類(lèi)型了。
當(dāng)然。除此以外還有更復(fù)雜的裝飾器寫(xiě)法。不過(guò)能處理傳遞過(guò)來(lái)的參數(shù)并且不影響被裝飾函數(shù)的正常執(zhí)行?;旧蠈?shí)現(xiàn)了之前的功能。
那么回過(guò)頭來(lái)看示例當(dāng)中的寫(xiě)法。最外層使用def permission_required(permission): 的意義,顯然是想要實(shí)現(xiàn)復(fù)用。
def admin_required(f): return permission_required(Permission.SMY)(f)
上面的(permission)形參顯然對(duì)應(yīng)permission_required(Permission.SMY)中(Permission.SMY)這個(gè)參數(shù)。把這個(gè)參數(shù)的形參傳遞到方法體內(nèi)部
這也是為什么要在裝飾器decorator(f)外面再嵌套一層函數(shù)的原因——實(shí)現(xiàn)復(fù)用
于是之前這個(gè)寫(xiě)法的內(nèi)容就很清晰了
def permission_required(permission): #通過(guò)形參實(shí)現(xiàn)了一個(gè)裝飾器類(lèi)。對(duì)于不同針對(duì)性的裝飾器,都可以調(diào)用這個(gè)函數(shù)的實(shí)現(xiàn),而只需要做最小的改動(dòng)(傳遞形參) def decorator(f): #這個(gè)才是裝飾器開(kāi)始執(zhí)行的第一步 @wraps(f) #這個(gè)裝飾器實(shí)際上是為了保證函數(shù)的原始屬性不發(fā)生改變。所謂原始屬性,指的是__name__ 這種屬性 def decorated_function(*args, **kwargs): #這個(gè)裝飾器方法把原函數(shù)的形參繼承了。因此實(shí)際上相當(dāng)于在原函數(shù)開(kāi)頭增加了這個(gè)函數(shù)的內(nèi)容 if not current_user.can(permission): #這個(gè)地方很明顯。current_user是從內(nèi)存中?。ǚ?wù)端),然后permission就會(huì)根據(jù)我們實(shí)際需要驗(yàn)證的permission進(jìn)行形參到實(shí)參的轉(zhuǎn)化 abort(403) #明顯的異常處理,當(dāng)然,403是一個(gè)粗暴的方法。更粗暴的方法,我會(huì)用redirect(url_for(logout))... return f(*args, **kwargs) #結(jié)束判斷,把參數(shù)傳遞給原函數(shù)(此處的f()即是原函數(shù)(更具體的權(quán)限驗(yàn)證裝飾器),只是f是個(gè)丑陋的形參而已) return decorated_function return decorator
這樣差不多就結(jié)束了。如果有人想補(bǔ)充,歡迎留言。
以上這篇Python裝飾器實(shí)現(xiàn)幾類(lèi)驗(yàn)證功能做法實(shí)例就是小編分享給大家的全部?jī)?nèi)容了,希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
- Python類(lèi)中的裝飾器在當(dāng)前類(lèi)中的聲明與調(diào)用詳解
- Python上下文管理器類(lèi)和上下文管理器裝飾器contextmanager用法實(shí)例分析
- Python類(lèi)裝飾器實(shí)現(xiàn)方法詳解
- Python 使用類(lèi)寫(xiě)裝飾器的小技巧
- 基于Python 裝飾器裝飾類(lèi)中的方法實(shí)例
- python類(lèi)裝飾器用法實(shí)例
- python通過(guò)裝飾器檢查函數(shù)參數(shù)數(shù)據(jù)類(lèi)型的方法
- Python中使用裝飾器和元編程實(shí)現(xiàn)結(jié)構(gòu)體類(lèi)實(shí)例
- 巧用Python裝飾器 免去調(diào)用父類(lèi)構(gòu)造函數(shù)的麻煩
- Python如何將裝飾器定義為類(lèi)
相關(guān)文章
Python?NumPy科學(xué)計(jì)算庫(kù)的高級(jí)應(yīng)用
這篇文章主要為大家介紹了Python?NumPy科學(xué)計(jì)算庫(kù)的高級(jí)應(yīng)用深入詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-07-07python list刪除元素時(shí)要注意的坑點(diǎn)分享
下面小編就為大家分享一篇python list刪除元素時(shí)要注意的坑點(diǎn)分享,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2018-04-04python框架flask入門(mén)之路由及簡(jiǎn)單實(shí)現(xiàn)方法
這篇文章主要介紹了python框架flask入門(mén)路由及路由簡(jiǎn)單實(shí)現(xiàn)方法,本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-06-06Python實(shí)現(xiàn)的簡(jiǎn)單dns查詢功能示例
這篇文章主要介紹了Python實(shí)現(xiàn)的簡(jiǎn)單dns查詢功能,結(jié)合實(shí)例形式分析了Python基于socket模塊的dns信息查詢實(shí)現(xiàn)技巧,需要的朋友可以參考下2017-05-05python 進(jìn)程的幾種創(chuàng)建方式詳解
這篇文章主要介紹了python 進(jìn)程的幾種創(chuàng)建方式詳解,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2019-08-08Python自動(dòng)化辦公實(shí)現(xiàn)數(shù)據(jù)自動(dòng)填充需求
這篇文章主要為大家介紹了Python自動(dòng)化辦公實(shí)現(xiàn)數(shù)據(jù)自動(dòng)填充需求,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-06-06Python簡(jiǎn)單連接MongoDB數(shù)據(jù)庫(kù)的方法
這篇文章主要介紹了Python簡(jiǎn)單連接MongoDB數(shù)據(jù)庫(kù)的方法,結(jié)合實(shí)例形式分析了Python使用pymongo模塊操作MongoDB數(shù)據(jù)庫(kù)的相關(guān)技巧,需要的朋友可以參考下2016-03-03Python的爬蟲(chóng)包Beautiful Soup中用正則表達(dá)式來(lái)搜索
這篇文章主要介紹了Python的爬蟲(chóng)包Beautiful Soup中用正則表達(dá)式來(lái)搜索的技巧,包括使用正則表達(dá)式去搜索多種可能的關(guān)鍵字以及查找屬性值未知的標(biāo)簽等,需要的朋友可以參考下2016-01-01