tornado 反向代理后 獲取真實客戶端IP


首先,nginx必定會設置一個Header傳送過來真實的IP

nginx.conf

server {

  proxy_set_header X-Real-IP $remote_addr;
  location / {
    proxy_pass http://192.168.7.206:8888/;
  }
}

然后,tornado的Handler里面進行如下處理:

class Handler(BaseRequestHandler):
    
    def post(self):
        self.get()

    def get(self):
        remote_ip = self.request.headers.get("X-Real-Ip", "")

此處,下面兩行其實是沒有任何區別的,ng的header到了tornado會做一次統一整理(具體可以參考class HTTPHeaders)。

ip = self.request.headers.get("X-Real-Ip", "") 
ip = self.request.headers.get("X-Real-IP", "")

然后,就可以了。


 

但是,這里為啥還有但是呢?這段日志還有奇怪的地方,它記錄的還不對,這個是tornado的默認日志。

[I 150806 14:54:21 web:1811] 200 POST / (192.168.7.62) 2.74ms

看tornado==v4.2.0的代碼web.py文件某處一定有如下類似的部分:

    def log_request(self, handler):
        """Writes a completed HTTP request to the logs.

        By default writes to the python root logger.  To change
        this behavior either subclass Application and override this method,
        or pass a function in the application settings dictionary as
        ``log_function``.
        """
        if "log_function" in self.settings:
            self.settings["log_function"](handler)
            return
        if handler.get_status() < 400:
            log_method = access_log.info
        elif handler.get_status() < 500:
            log_method = access_log.warning
        else:
            log_method = access_log.error
        request_time = 1000.0 * handler.request.request_time()
        log_method("%d %s %.2fms", handler.get_status(),
                   handler._request_summary(), request_time)

這塊其實就是日志的實現,_request_summary 又是啥?

    def _request_summary(self):
        return "%s %s (%s)" % (self.request.method, self.request.uri,
                               self.request.remote_ip)

后續繼續看,發現,self.request.remote_ip這個其實就是tornado針對客戶端IP做的快捷訪問,這里為啥不對呢?

原來要在初始化listen的時候設置一個參數:

def main():
app = Application() app.listen(options.port, xheaders = True)

產生的效果就是

[I 150806 13:52:53 web:1908] 200 POST / (192.168.7.214) 1.86ms

真實IP反映到Tornado的基礎日志里了。

具體實現追蹤可以查看 tornado\httpserver.py 中的相關代碼。

class _ServerRequestAdapter(httputil.HTTPMessageDelegate):

    def headers_received(self, start_line, headers):
        if self.server.xheaders:
            self.connection.context._apply_xheaders(headers)


class _HTTPRequestContext(object):

    def _apply_xheaders(self, headers):
        """Rewrite the ``remote_ip`` and ``protocol`` fields."""
        # Squid uses X-Forwarded-For, others use X-Real-Ip
        ip = headers.get("X-Forwarded-For", self.remote_ip)
        ip = ip.split(',')[-1].strip()
        ip = headers.get("X-Real-Ip", ip)
        if netutil.is_valid_ip(ip):
            self.remote_ip = ip
        # AWS uses X-Forwarded-Proto
        proto_header = headers.get(
            "X-Scheme", headers.get("X-Forwarded-Proto",
                                    self.protocol))
        if proto_header in ("http", "https"):
            self.protocol = proto_header

    def _unapply_xheaders(self):
        """Undo changes from `_apply_xheaders`.

        Xheaders are per-request so they should not leak to the next
        request on the same connection.
        """
        self.remote_ip = self._orig_remote_ip
        self.protocol = self._orig_protocol

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM