用于GAE网络服务安全检查的Python装饰器
在这篇文章中,Nick 提出了一个装饰器的建议:
Python/WebApp Google App Engine - 测试请求头中的用户名和密码
我正在编写一个 API,准备把几十个方法作为网络服务提供出来,所以这个装饰器听起来是个不错的主意。
我尝试根据这个示例开始编写代码: http://groups.google.com/group/google-appengine/browse_thread/thread/ac51cc32196d62f8/aa6ccd47f217cb9a?lnk=gst&q=timeout#aa6ccd47f217cb9a
我需要它与 Python 2.5 兼容,以便在 Google App Engine (GAE) 上运行。
这是我的尝试。请告诉我我是不是在正确的方向上。 目前在这一行出现了“无效语法”的错误: class WSTest(webapp.RequestHandler):
我的想法是将一个角色数组传递给装饰器。这些角色是我数据库中唯一可以访问每个不同网络服务的角色。
def BasicAuthentication(roles=[]):
def _decorator(func):
def _wrapper(*args, **kwds):
logging.info("\n\n BasicAuthentication:START:__call__ \n\n")
auth = None
if 'Authorization' in self.request.headers:
auth = self.request.headers['Authorization']
if not auth:
self.response.headers['WWW-Authenticate'] = 'Basic realm="MYREALM"'
self.response.set_status(401)
self.response.out.write("Authorization required")
logging.info ("\n\n Authorization required \n\n")
return
(username, password) = base64.b64decode(auth.split(' ')[1]).split(':')
logging.info ("\n\n username = " + username + " password=" + password + "\n\n")
isValidUserPass = False
usersSimulatedRole = "Admin"
#check against database here...
if user == "test12" and password == "test34":
isValidUserPass = True
isValidRole = False
if usersSimulatedRole in roles:
isValidRole = True
#next check that user has one of the roles
# TODO
if not isValidUserPass:
self.response.set_status(403)
self.response.out.write("Forbidden: Userid/password combination failed")
logging.info("\n\n BasicAuthentication:END:__call__ \n\n")
return func(*args, **kwds)
return _wrapper
return _decorator
@BasicAuthentication(["Admin","Worker"]) #list of roles that can run this function
class WSTest(webapp.RequestHandler):
def get(self):
logging.info("\n\n\n WSTest \n\n")
...etc...
谢谢, Neal Walters
2 个回答
类装饰器是在 Python 2.6 版本中新增的功能。
如果你使用的是 2.5 版本,你需要手动包装这个类,或者想其他办法来解决这个问题。那不如试着为 get
方法写一个装饰器呢?
你需要写一个方法装饰器,而不是类装饰器:正如lost-theory所说,Python 2.5中没有类装饰器,而且即使有也不太好用,因为RequestHandler类在构造后才会用请求数据进行初始化。使用方法装饰器可以让你有更多的控制权,比如你可以允许GET请求不需要认证,但对POST请求却要求认证。
除此之外,你的装饰器看起来没问题,只需要把它应用到相关的方法上。我唯一建议的改变是把.set_status()的调用换成.error()的调用,并且去掉response.write的调用;这样你就可以在RequestHandler类中重写.error(),为每种可能的状态码输出一个漂亮的错误页面。