CherryPy REST 身份验证

CherryPy REST Authentication

我对 Python 和 CherryPy 还很陌生,正在尝试构建一个基本的 Web 应用程序,它将使用 RESTful API 从服务器查询数据。我试图从一开始就以正确的方式去做。我无法弄清楚的一部分是 API 的身份验证,因为 REST 应该是无状态的,并且您不使用会话。

我希望能够将我的 API 与没有 Cookie 的 "native clients" 一起使用,因此无法使用会话 Cookie。将在 HTML 中使用 AJAX 访问数据。 OAuth 似乎是一种选择,但我不想依赖第三方提供登录服务(几周前 Facebook 离线了将近一天)

任何人都可以指出正确的方向,这可以与 CherryPy 一起使用吗?

RESTful 验证没有 "the right way"。 REST 本身并不是 API 灵丹妙药。你有它的要求,以及你需要权衡利弊的解决方案。但是,我将介绍开箱即用的适用于 CherryPy 的 HTTP 标准方法。

您在评论中链接的文章非常清楚地说明了以无状态方式进行身份验证的简单方法 -- Basic Auth on HTTPS. There's also Digest Auth,它不会像这样传输密码并防止重放攻击,所以没问题在纯 HTTP 上使用它。

#!/usr/bin/env python
# -*- coding: utf-8 -*-


import cherrypy


userpassdict  = {'user1': 'passwd'}
checkpassword = cherrypy.lib.auth_basic.checkpassword_dict(userpassdict)
get_ha1       = cherrypy.lib.auth_digest.get_ha1_dict_plain(userpassdict)

config = {
  'global' : {
    'server.socket_host' : '127.0.0.1',
    'server.socket_port' : 8080,
    'server.thread_pool' : 8
  },
  '/' : {
    # HTTP verb dispatcher
    'request.dispatch': cherrypy.dispatch.MethodDispatcher(),
    # JSON response
    'tools.json_out.on' : True,
    # Basic Auth
    'tools.auth_basic.on'            : True,
    'tools.auth_basic.realm'         : 'Walled garden',
    'tools.auth_basic.checkpassword' : checkpassword,
    # Digest Auth
    #'tools.auth_digest.on'      : True,
    #'tools.auth_digest.realm'   : 'Walled garden',
    #'tools.auth_digest.get_ha1' : get_ha1,
    #'tools.auth_digest.key'     : 'put random secret here',
  }
}


class Document:
  '''Test like:
  curl --user user1:passwd --request GET http://localhost:8080/api/document
  curl --user user1:passwd --request GET http://localhost:8080/api/document/2
  curl --user user1:passwd --request POST --data name="new entry" http://localhost:8080/api/document
  curl --user user1:passwd --request PUT --data name="new entry2" http://localhost:8080/api/document/4
  curl --user user1:passwd --request DELETE http://localhost:8080/api/document/4
  '''

  _store  = None
  exposed = True


  def __init__(self):
    self._store = {
      1 : {'id': 1, 'name': 'foo'},
      2 : {'id': 2, 'name': 'bar'},
      3 : {'id': 3, 'name': 'baz'},
      4 : {'id': 4, 'name': 'qux'},
    }

  def GET(self, id = None):
    if id:
      return self._store[int(id)]
    else:
      return self._store.values()

  def POST(self, **kwargs):
    id = max(self._store.keys()) + 1
    self._store[id] = {'id': id, 'name': kwargs['name']}
    return id    

  def PUT(self, id, **kwargs):
    self._store[int(id)].update(kwargs)

  def DELETE(self, id):
    self._store.pop(int(id))


if __name__ == '__main__':
  cherrypy.quickstart(Document(), '/api/document', config)