Python装饰器实现几类验证功能做法实例


Posted in Python onMay 18, 2017

最近新需求来了,要给系统增加几个资源权限。尽量减少代码的改动和程序的复杂程度。所以还是使用装饰器比较科学

之前用了一些登录验证的现成装饰器模块。然后仿写一些用户管理部分的权限装饰器。

比如下面这种

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)

调用权限的时候很好理解。直接仿写admin_required的格式就好了。然后每个页面入口用语法糖这样写: @admin_required

于是页面的入口权限就做好了。但是资源权限和页面权限不同。上面内容中提到的permission是写在model.py的静态内容里面的。

从封装来看,至少是看不出来哪个地方暴露了用户查询的方法(菜鸟水平下)。只能简单的看出来if判断的时候似乎使用了current_user这个变量的内置方法

但是current_user其实是一个第三方的包的内容,和登录模块引入的包相同,是一整套记录token信息的代码。详细内容太多。从这个地方出发去写,会go die

因为哪怕我知道其实调用的.can(permission)是model类里面定义的类方法。可是current_user是取了哪个部分的东西还是不清楚。

所以不管它。从头来梳理一下装饰器的内容。

首先一个简单的装饰器写法是很好理解的。比如原函数是这样写的:

def page():
  if user == 'admin':
    form = Form()
    
    if request.method=='POST':
      
      db.session.add(form)
      db.session.commit()
      flash("success")
      return 0

这当然是随便写的一个函数(明显有很多问题),只是用来表达一个过程。首先通过路由调用这个函数的时候,会先执行第一个if判断。这个判断即我们想要的验证内容

验证通过以后,说明用户可以访问这个页面,然后页面内容会渲染出来,交互功能也被允许……

那么装饰器,就是把这个if的功能提取出来了。那么原函数写成这样的形式:

@admin_check
def page():
    form = Form()

    if request.method=='POST':

      db.session.add(form)
      db.session.commit()
      flash("success")
      return 0

单从这个函数来说,这样写并没有任何好处,似乎本来一行代码搞定的问题,多用了几行代码。我们展开这个形式的完整代码看一下:

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()函数当中的参数。也就是说,这个验证这么看起来没什么用处

不过机制是这样。接下来就可以研究怎样的做法是把路由传递过来的请求数据进行验证然后继续执行的了。

def admincheck(func):
  def inner(arg):
    if user == 'admin':
      if arg == 'false':
        abort(403)
    return func(arg)
  return inner

同样的,多个参数的时候,只需要把 def inner(arg)改写成def inner(arg1,arg2)

n个参数的时候,则写成def inner(*args,**kwargs) 这个需要注意一下。*args是元组,即('user',1);**kwargs是字典,即{'user':1}

同时写这两个形参的话,基本上就能处理所有传递进来的参数类型了。

当然。除此以外还有更复杂的装饰器写法。不过能处理传递过来的参数并且不影响被装饰函数的正常执行。基本上实现了之前的功能。

那么回过头来看示例当中的写法。最外层使用def permission_required(permission): 的意义,显然是想要实现复用。

def admin_required(f):
  return permission_required(Permission.SMY)(f)

上面的(permission)形参显然对应permission_required(Permission.SMY)中(Permission.SMY)这个参数。把这个参数的形参传递到方法体内部

这也是为什么要在装饰器decorator(f)外面再嵌套一层函数的原因——实现复用

于是之前这个写法的内容就很清晰了

def permission_required(permission):

#通过形参实现了一个装饰器类。对于不同针对性的装饰器,都可以调用这个函数的实现,而只需要做最小的改动(传递形参)
  def decorator(f):
#这个才是装饰器开始执行的第一步
    @wraps(f)


#这个装饰器实际上是为了保证函数的原始属性不发生改变。所谓原始属性,指的是__name__ 这种属性
    def decorated_function(*args, **kwargs):


#这个装饰器方法把原函数的形参继承了。因此实际上相当于在原函数开头增加了这个函数的内容
      if not current_user.can(permission):



#这个地方很明显。current_user是从内存中取(服务端),然后permission就会根据我们实际需要验证的permission进行形参到实参的转化
        abort(403)




#明显的异常处理,当然,403是一个粗暴的方法。更粗暴的方法,我会用redirect(url_for(logout))...
      return f(*args, **kwargs)



#结束判断,把参数传递给原函数(此处的f()即是原函数(更具体的权限验证装饰器),只是f是个丑陋的形参而已)
    return decorated_function
  return decorator

这样差不多就结束了。如果有人想补充,欢迎留言。

以上这篇Python装饰器实现几类验证功能做法实例就是小编分享给大家的全部内容了,希望能给大家一个参考,也希望大家多多支持三水点靠木。

Python 相关文章推荐
java直接调用python脚本的例子
Feb 16 Python
简述:我为什么选择Python而不是Matlab和R语言
Nov 14 Python
你真的了解Python的random模块吗?
Dec 12 Python
TF-IDF与余弦相似性的应用(一) 自动提取关键词
Dec 21 Python
使用python判断你是青少年还是老年人
Nov 29 Python
PyQt5 QListWidget选择多项并返回的实例
Jun 17 Python
python 实现的发送邮件模板【普通邮件、带附件、带图片邮件】
Jul 06 Python
Python openpyxl读取单元格字体颜色过程解析
Sep 03 Python
python实现一个点绕另一个点旋转后的坐标
Dec 04 Python
python 两种方法修改文件的创建时间、修改时间、访问时间
Sep 26 Python
python两种注释用法的示例
Oct 09 Python
ffmpeg+Python实现B站MP4格式音频与视频的合并示例代码
Oct 21 Python
如何用itertools解决无序排列组合的问题
May 18 #Python
详解使用python的logging模块在stdout输出的两种方法
May 17 #Python
Python中正则表达式详解
May 17 #Python
python算法演练_One Rule 算法(详解)
May 17 #Python
浅谈pyhton学习中出现的各种问题(新手必看)
May 17 #Python
Python入门_学会创建并调用函数的方法
May 16 #Python
Python入门_浅谈逻辑判断与运算符
May 16 #Python
You might like
给初学PHP的5个入手程序
2006/11/23 PHP
php下统计用户在线时间的一种尝试
2010/08/26 PHP
PHP实现简单用户登录界面
2019/10/23 PHP
写出更好的JavaScript程序之undefined篇(中)
2009/11/23 Javascript
js处理json以及字符串的比较等常用操作
2013/09/08 Javascript
javascript Deferred和递归次数限制实例
2014/10/21 Javascript
jQuery给元素添加样式的方法详解
2015/12/30 Javascript
JS判断iframe是否加载完成的方法
2016/08/03 Javascript
详解JS去重及字符串奇数位小写转大写
2016/12/29 Javascript
AngularJS自定义指令详解(有分页插件代码)
2017/06/12 Javascript
vue 解决循环引用组件报错的问题
2018/09/06 Javascript
Vue使用NPM方式搭建项目
2018/10/25 Javascript
Node 搭建一个静态资源服务器的实现
2019/05/20 Javascript
vue 将多个过滤器封装到一个文件中的代码详解
2020/09/05 Javascript
[11:12]2018DOTA2国际邀请赛寻真——绿色长城OpTic
2018/08/10 DOTA
[33:33]完美世界DOTA2联赛PWL S2 FTD.C vs SZ 第二场 11.27
2020/11/30 DOTA
轻松掌握python设计模式之访问者模式
2016/11/18 Python
Pycharm+Scrapy安装并且初始化项目的方法
2019/01/15 Python
Python 变量的创建过程详解
2019/09/02 Python
python_matplotlib改变横坐标和纵坐标上的刻度(ticks)方式
2020/05/16 Python
Python实现自动整理文件的脚本
2020/12/17 Python
五个2015 年最佳HTML5 框架
2015/11/11 HTML / CSS
HTML5实现无刷新修改URL的方法
2019/11/14 HTML / CSS
中国海淘族值得信赖的海淘返利网站:55海淘
2017/01/16 全球购物
天猫国际进口超市直营:官方直采,一站购齐
2017/12/11 全球购物
我们没有写servlet的构造方法,那么容器是怎么创建servlet的实例呢
2013/04/24 面试题
银行自荐信范文
2013/10/07 职场文书
党员教师群众路线对照检查材料思想汇报
2014/09/29 职场文书
房产协议书范本
2014/10/18 职场文书
2014年小学德育工作总结
2014/12/05 职场文书
新郎结婚保证书
2015/02/26 职场文书
2015年入党积极分子评语
2015/03/26 职场文书
公司副总经理岗位职责
2015/04/08 职场文书
2016中秋节广告语
2016/01/28 职场文书
Python中os模块的简单使用及重命名操作
2021/04/17 Python
使用nginx配置访问wgcloud的方法
2021/06/26 Servers