技術(shù)員聯(lián)盟提供win764位系統(tǒng)下載,win10,win7,xp,裝機純凈版,64位旗艦版,綠色軟件,免費軟件下載基地!

當前位置:主頁 > 教程 > 服務器類 >

Python裝飾器實現(xiàn)幾類驗證功能做法實例

來源:技術(shù)員聯(lián)盟┆發(fā)布時間:2017-10-25 12:21┆點擊:

  最近新需求來了,要給系統(tǒng)增加幾個資源權(quán)限。盡量減少代碼的改動和程序的復雜程度。所以還是使用裝飾器比較科學

  之前用了一些登錄驗證的現(xiàn)成裝飾器模塊。然后仿寫一些用戶管理部分的權(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)限的時候很好理解。直接仿寫admin_required的格式就好了。然后每個頁面入口用語法糖這樣寫: @admin_required

  于是頁面的入口權(quán)限就做好了。但是資源權(quán)限和頁面權(quán)限不同。上面內(nèi)容中提到的permission是寫在model.py的靜態(tài)內(nèi)容里面的。

  從封裝來看,至少是看不出來哪個地方暴露了用戶查詢的方法(菜鳥水平下)。只能簡單的看出來if判斷的時候似乎使用了current_user這個變量的內(nèi)置方法

  但是current_user其實是一個第三方的包的內(nèi)容,和登錄模塊引入的包相同,是一整套記錄token信息的代碼。詳細內(nèi)容太多。從這個地方出發(fā)去寫,會go die

  因為哪怕我知道其實調(diào)用的.can(permission)是model類里面定義的類方法??墒莄urrent_user是取了哪個部分的東西還是不清楚。

  所以不管它。從頭來梳理一下裝飾器的內(nèi)容。

  首先一個簡單的裝飾器寫法是很好理解的。比如原函數(shù)是這樣寫的:

  def page():

  if user == 'admin':

  form = Form()

  if request.method=='POST':

  db.session.add(form)

  db.session.commit()

  flash("success")

  return 0

  這當然是隨便寫的一個函數(shù)(明顯有很多問題),只是用來表達一個過程。首先通過路由調(diào)用這個函數(shù)的時候,會先執(zhí)行第一個if判斷。這個判斷即我們想要的驗證內(nèi)容

  驗證通過以后,說明用戶可以訪問這個頁面,然后頁面內(nèi)容會渲染出來,交互功能也被允許……

  那么裝飾器,就是把這個if的功能提取出來了。那么原函數(shù)寫成這樣的形式:

  @admin_check

  def page():

  form = Form()

  if request.method=='POST':

  db.session.add(form)

  db.session.commit()

  flash("success")

  return 0

  單從這個函數(shù)來說,這樣寫并沒有任何好處,似乎本來一行代碼搞定的問題,多用了幾行代碼。我們展開這個形式的完整代碼看一下:

  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這一句寫成了@模式。

  但是這種寫法只能解決最基本的驗證問題。也就是相對獨立的入口驗證。這個驗證還沒有拿到程序傳遞到page()函數(shù)當中的參數(shù)。也就是說,這個驗證這么看起來沒什么用處

  不過機制是這樣。接下來就可以研究怎樣的做法是把路由傳遞過來的請求數(shù)據(jù)進行驗證然后繼續(xù)執(zhí)行的了。

  def admincheck(func):

  def inner(arg):

  if user == 'admin':

  if arg == 'false':

  abort(403)

  return func(arg)

  return inner

  同樣的,多個參數(shù)的時候,只需要把 def inner(arg)改寫成def inner(arg1,arg2)

  n個參數(shù)的時候,則寫成def inner(*args,**kwargs) 這個需要注意一下。*args是元組,即('user',1);**kwargs是字典,即{'user':1}

  同時寫這兩個形參的話,基本上就能處理所有傳遞進來的參數(shù)類型了。

  當然。除此以外還有更復雜的裝飾器寫法。不過能處理傳遞過來的參數(shù)并且不影響被裝飾函數(shù)的正常執(zhí)行。基本上實現(xiàn)了之前的功能。

  那么回過頭來看示例當中的寫法。最外層使用def permission_required(permission): 的意義,顯然是想要實現(xiàn)復用。

  def admin_required(f):

  return permission_required(Permission.SMY)(f)

  上面的(permission)形參顯然對應permission_required(Permission.SMY)中(Permission.SMY)這個參數(shù)。把這個參數(shù)的形參傳遞到方法體內(nèi)部

  這也是為什么要在裝飾器decorator(f)外面再嵌套一層函數(shù)的原因——實現(xiàn)復用

  于是之前這個寫法的內(nèi)容就很清晰了

  def permission_required(permission):

  #通過形參實現(xiàn)了一個裝飾器類。對于不同針對性的裝飾器,都可以調(diào)用這個函數(shù)的實現(xiàn),而只需要做最小的改動(傳遞形參)

  def decorator(f):

  #這個才是裝飾器開始執(zhí)行的第一步

  @wraps(f)

  #這個裝飾器實際上是為了保證函數(shù)的原始屬性不發(fā)生改變。所謂原始屬性,指的是__name__ 這種屬性

  def decorated_function(*args, **kwargs):

  #這個裝飾器方法把原函數(shù)的形參繼承了。因此實際上相當于在原函數(shù)開頭增加了這個函數(shù)的內(nèi)容

  if not current_user.can(permission):

  #這個地方很明顯。current_user是從內(nèi)存中取(服務端),然后permission就會根據(jù)我們實際需要驗證的permission進行形參到實參的轉(zhuǎn)化

  abort(403)

  #明顯的異常處理,當然,403是一個粗暴的方法。更粗暴的方法,我會用redirect(url_for(logout))...

  return f(*args, **kwargs)

  #結(jié)束判斷,把參數(shù)傳遞給原函數(shù)(此處的f()即是原函數(shù)(更具體的權(quán)限驗證裝飾器),只是f是個丑陋的形參而已)

  return decorated_function

  return decorator

  這樣差不多就結(jié)束了。如果有人想補充,歡迎留言。