简体   繁体   English

Python中的授权REST服务

[英]authorisation REST service in Python

I am in the design as well as at the deciding phase whether to choose python as a primary language to implement the software.我正在设计以及决定是否选择 python 作为主要语言来实现软件。 The task is to:任务是:

  • implement a set of restful web services实现一组restful web services
  • authorizing the http methods to certain group of users, for that it is required to use xacml for the policies definition (or can be another standard) and saml for the info.将 http 方法授权给某些用户组,因为需要使用 xacml 来定义策略(或者可以是另一个标准)和使用 saml 来获取信息。 exchange交换

If your question was what libraries you can use to implement these RESTful services, then have a look into the BaseHTTPServer module of the standard python library.如果您的问题是可以使用哪些库来实现这些 RESTful 服务,那么请查看标准 Python 库的BaseHTTPServer模块。

The following code shows how easy it is to implement a simple server accepting GET requests:以下代码显示了实现一个接受 GET 请求的简单服务器是多么容易:

class MyHandler(BaseHTTPRequestHandler):

    def do_GET(self):
        try:
            f = open(curdir + sep + self.path) #self.path has /test.html
            self.send_response(200)
            self.send_header('Content-type',    'text/html')
            self.end_headers()
            self.wfile.write(f.read())
            f.close()
        except IOError:
            self.send_error(404,'File Not Found: %s' % self.path)

def main():
    try:
        server = HTTPServer(('', 80), MyHandler)
        print 'Welcome to the machine...'
        server.serve_forever()
    except KeyboardInterrupt:
        print '^C received, shutting down server'
        server.socket.close()

if __name__ == '__main__':
    main()

Of course, the code is not from myself, I found it here .当然,代码不是我自己写的,我在这里找到的。

I agree with Constantinius that BaseHTTPServer is a great way to do RESTful in Python.我同意 Constantinius 的观点,即BaseHTTPServer是在 Python 中实现 RESTful 的好方法。 It is pre-installed with Python 2.7 and scales much better than gunicorn/flask/wsgi/gevent for that sort of thing.它预装了 Python 2.7 并且在这类事情上比 gunicorn/flask/wsgi/gevent 扩展性好得多。 And supports streaming which you might want down the road.并支持您可能想要的流媒体。

Below is an example showing how a web browser does a remote POST call to a BaseHTTPServer and gets data back.下面是一个示例,展示了 Web 浏览器如何对 BaseHTTPServer 进行远程 POST 调用并取回数据。

JS (put in static/hello.html to serve via Python): JS(放入 static/hello.html 以通过 Python 提供服务):

<html><head><meta charset="utf-8"/></head><body>
Hello.

<script>

var xhr = new XMLHttpRequest();
xhr.open("POST", "/postman", true);
xhr.setRequestHeader('Content-Type', 'application/json');
xhr.send(JSON.stringify({
    value: 'value'
}));
xhr.onload = function() {
  console.log("HELLO")
  console.log(this.responseText);
  var data = JSON.parse(this.responseText);
  console.log(data);
}

</script></body></html>

Python server (for testing): Python服务器(用于测试):

import time, threading, socket, SocketServer, BaseHTTPServer
import os, traceback, sys, json


log_lock           = threading.Lock()
log_next_thread_id = 0

# Local log functiondef


def Log(module, msg):
    with log_lock:
        thread = threading.current_thread().__name__
        msg    = "%s %s: %s" % (module, thread, msg)
        sys.stderr.write(msg + '\n')

def Log_Traceback():
    t   = traceback.format_exc().strip('\n').split('\n')
    if ', in ' in t[-3]:
        t[-3] = t[-3].replace(', in','\n***\n***  In') + '(...):'
        t[-2] += '\n***'
    err = '\n***  '.join(t[-3:]).replace('"','').replace(' File ', '')
    err = err.replace(', line',':')
    Log("Traceback", '\n'.join(t[:-3]) + '\n\n\n***\n*** ' + err + '\n***\n\n')

    os._exit(4)

def Set_Thread_Label(s):
    global log_next_thread_id
    with log_lock:
        threading.current_thread().__name__ = "%d%s" \
            % (log_next_thread_id, s)
        log_next_thread_id += 1


class Handler(BaseHTTPServer.BaseHTTPRequestHandler):

    def do_GET(self):
        Set_Thread_Label(self.path + "[get]")
        try:
            Log("HTTP", "PATH='%s'" % self.path)
            with open('static' + self.path) as f:
                data = f.read()
            Log("Static", "DATA='%s'" % data)
            self.send_response(200)
            self.send_header("Content-type", "text/html")
            self.end_headers()
            self.wfile.write(data)
        except:
            Log_Traceback()

    def do_POST(self):
        Set_Thread_Label(self.path + "[post]")
        try:
            length = int(self.headers.getheader('content-length'))
            req   = self.rfile.read(length)
            Log("HTTP", "PATH='%s'" % self.path)
            Log("URL", "request data = %s" % req)
            req = json.loads(req)
            response = {'req': req}
            response = json.dumps(response)
            Log("URL", "response data = %s" % response)
            self.send_response(200)
            self.send_header("Content-type", "application/json")
            self.send_header("content-length", str(len(response)))
            self.end_headers()
            self.wfile.write(response)
        except:
            Log_Traceback()


# Create ONE socket.
addr = ('', 8000)
sock = socket.socket (socket.AF_INET, socket.SOCK_STREAM)
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
sock.bind(addr)
sock.listen(5)

# Launch 10 listener threads.
class Thread(threading.Thread):
    def __init__(self, i):
        threading.Thread.__init__(self)
        self.i = i
        self.daemon = True
        self.start()
    def run(self):
        httpd = BaseHTTPServer.HTTPServer(addr, Handler, False)

        # Prevent the HTTP server from re-binding every handler.
        # https://stackoverflow.com/questions/46210672/
        httpd.socket = sock
        httpd.server_bind = self.server_close = lambda self: None

        httpd.serve_forever()
[Thread(i) for i in range(10)]
time.sleep(9e9)

Console log (chrome):控制台日志(镀铬):

HELLO
hello.html:14 {"req": {"value": "value"}}
hello.html:16 
{req: {…}}
req
:
{value: "value"}
__proto__
:
Object

Console log (firefox):控制台日志(火狐):

GET 
http://XXXXX:8000/hello.html [HTTP/1.0 200 OK 0ms]
POST 
XHR 
http://XXXXX:8000/postman [HTTP/1.0 200 OK 0ms]
HELLO hello.html:13:3
{"req": {"value": "value"}} hello.html:14:3
Object { req: Object }

Console log (Edge):控制台日志(边缘):

HTML1300: Navigation occurred.
hello.html
HTML1527: DOCTYPE expected. Consider adding a valid HTML5 doctype: "<!DOCTYPE html>".
hello.html (1,1)
Current window: XXXXX/hello.html
HELLO
hello.html (13,3)
{"req": {"value": "value"}}
hello.html (14,3)
[object Object]
hello.html (16,3)
   {
      [functions]: ,
      __proto__: { },
      req: {
         [functions]: ,
         __proto__: { },
         value: "value"
      }
   }

Python log: Python日志:

HTTP 8/postman[post]: PATH='/postman'
URL 8/postman[post]: request data = {"value":"value"}
URL 8/postman[post]: response data = {"req": {"value": "value"}}

Also you can easily add SSL by wrapping the socket before passing it to BaseHTTPServer.您还可以通过在将套接字传递给 BaseHTTPServer 之前包装套接字来轻松添加 SSL。

Using Python you would want to create a XACML request that would include your user's id (you would get that from the authentication phase that typically takes place before you authenticate) and add information about the WS the user is targetting.使用 Python,您可能希望创建一个 XACML 请求,该请求将包含您的用户 ID(您将从通常在您进行身份验证之前进行的身份验证阶段获得该 ID)并添加有关用户所针对的 WS 的信息。 It could be the URI, the HTTP method too...它可能是 URI,也可能是 HTTP 方法......

In the end you may get sthg like:最后你可能会得到这样的东西:

<?xml version="1.0" encoding="UTF-8"?><xacml-ctx:Request xmlns:xacml-ctx="urn:oasis:names:tc:xacml:2.0:context:schema:os">
   <xacml-ctx:Subject SubjectCategory="urn:oasis:names:tc:xacml:1.0:subject-category:access-subject">
      <xacml-ctx:Attribute AttributeId="urn:oasis:names:tc:xacml:1.0:subject:subject-id" DataType="http://www.w3.org/2001/XMLSchema#string">
         <xacml-ctx:AttributeValue>Alice</xacml-ctx:AttributeValue>
      </xacml-ctx:Attribute>
   </xacml-ctx:Subject>
   <xacml-ctx:Resource>
      <xacml-ctx:Attribute AttributeId="urn:oasis:names:tc:xacml:1.0:resource:resource-id" DataType="http://www.w3.org/2001/XMLSchema#string">
         <xacml-ctx:AttributeValue>/someuri/myapi/target.py</xacml-ctx:AttributeValue>
      </xacml-ctx:Attribute>
   </xacml-ctx:Resource>
   <xacml-ctx:Action>
      <xacml-ctx:Attribute AttributeId="urn:oasis:names:tc:xacml:1.0:action:action-id" DataType="http://www.w3.org/2001/XMLSchema#string">
         <xacml-ctx:AttributeValue>GET</xacml-ctx:AttributeValue>
      </xacml-ctx:Attribute>
   </xacml-ctx:Action>
   <xacml-ctx:Environment>
   </xacml-ctx:Environment>
</xacml-ctx:Request>

You need to construct the request using Python and lxml for instance.例如,您需要使用 Python 和 lxml 构建请求。

The response will look like响应看起来像

<xacml-ctx:Response xmlns:xacml-ctx="urn:oasis:names:tc:xacml:2.0:context:schema:os">
  <xacml-ctx:Result>
    <xacml-ctx:Decision>Permit</xacml-ctx:Decision>
    <xacml-ctx:Status>
      <xacml-ctx:StatusCode Value="urn:oasis:names:tc:xacml:1.0:status:ok"/>
    </xacml-ctx:Status>
  </xacml-ctx:Result>
</xacml-ctx:Response>

So again you need to parse the XML to extract the decision eg Permit.因此,您再次需要解析 XML 以提取决定,例如 Permit。 I wrote a basic REST-like interface to a XACML PDP where all you have to do is send an HTTP GET to a URI passing variables as GET variables eg http://www.xacml.eu/AuthZ/?a=alice&b=/someuri/myapi/target.py&c=GET我为 XACML PDP 编写了一个类似 REST 的基本接口,其中您要做的就是将 HTTP GET 发送到 URI,将变量作为 GET 变量传递,例如http://www.xacml.eu/AuthZ/?a=alice&b=/ someuri/myapi/target.py&c=GET

Does that help?这有帮助吗?

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM