https請求http接口被瀏覽器攔截問題,python實現websocket客戶端websocket服務端,跨域問題,dwebsocket

1. 背景:由於公司前端的頁面部署在以https(加了證書)協議的域名下,去請求http協議的域名的某個服務,並且該http域名下的服務,不僅要處理普通請求(POST、GET),還需要處理websocket請求。由於瀏覽器禁止https域名的內容請求http的服務,甚至嵌入子頁面都禁止,因爲瀏覽器會認爲http的內容是不安全的,所以爲解決該問題,研究出如下解決方案。

2. 解決辦法:由於瀏覽器禁止,但是服務器並不會禁止,所以,我在https的域名下,解析一臺代理服務器,由該代理服務器去代替https去請求,再把結果返給前端。

3. 開發環境:PyCharm,語言:Python3,服務框架:django

4. 用到的庫: pip3 install Django==1.11.3 django-cors-headers==3.2.1 dwebsocket==0.5.12 websocket==0.2.1 websocket-client==0.57.0

5. settings.py配置:

# 允許訪問的host爲所有
ALLOWED_HOSTS = ['*']

# 配置跨域請求問題
CORS_ALLOW_CREDENTIALS = True
CORS_ORIGIN_ALLOW_ALL = True

CORS_ALLOW_METHODS = (
    'DELETE',
    'GET',
    'OPTIONS',
    'PATCH',
    'POST',
    'PUT',
    'VIEW',
)
CORS_ALLOW_HEADERS = (
    'XMLHttpRequest',
    'X_FILENAME',
    'accept-encoding',
    'authorization',
    'content-type',
    'dnt',
    'origin',
    'user-agent',
    'x-csrftoken',
    'x-requested-with',
)
INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    #'myapp.apps.MyappConfig',
    # 添加當前的app
    'myapp',
]

MIDDLEWARE = [
    'django.middleware.security.SecurityMiddleware',
    'django.contrib.sessions.middleware.SessionMiddleware',
    'django.middleware.common.CommonMiddleware',
    #  去除csrf校驗
    # 'django.middleware.csrf.CsrfViewMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'django.contrib.messages.middleware.MessageMiddleware',
    'django.middleware.clickjacking.XFrameOptionsMiddleware',
]

6. urls.py配置:

from django.contrib import admin
from django.urls import path
from django.conf.urls import url
from myapp import views

urlpatterns = [
    path('admin/', admin.site.urls),
    # 配置攔截/proxy 映射到 views.py中的方法
    url(r'^proxy$',views.proxy),
]

7. views.py代碼:仔細看註釋

from django.shortcuts import render
from django.shortcuts import HttpResponse
import requests
from dwebsocket.decorators import accept_websocket
import logging
from websocket import create_connection
import time

#  設置日誌格式
LOG_FORMAT = "%(asctime)s - %(levelname)s - %(message)s"
DATE_FORMAT = "%m/%d/%Y %H:%M:%S %p"
fs = logging.StreamHandler()
logging.basicConfig(level=logging.INFO, format=LOG_FORMAT, datefmt=DATE_FORMAT, handlers=[fs])
#  Create your views here.

# 添加dwebsocket的裝飾器,該裝飾器會對request屬性添加websocket屬性
# 並添加 send()推數據  close()關閉連接  is_websocket()是否是websocket請求 等方法
@accept_websocket
def proxy(request):
    # 判斷請求是否是websocket
    if not request.is_websocket():
        # 收到的請求的格式是  https://devlab.edu.huaweicloud.com/proxy?url=http://ip:8000/senddata?model=workload-read-mostly
        # 我們將這個服務部署到加了證書的服務器上,即可。
        # 其中,參數url=後面是要代理的url ,我們將其截取下來,並重新請求。
        url = request.get_full_path().split('url=')[1]
        logging.info('proxy url : {}'.format(url))
        # 進行GET請求和POST請求的分別處理
        # if request.method == 'GET':
        #     logging.info('processing a url GET request !')
            # 其中省略了將請求的 header/cookie 傳遞給代理請求的 header/cookie ,由於我們業務不需要這些,我就不加了。
            # res = requests.get(url)
        # else:
        #     logging.info('processing a url POST request !')
        #     res = requests.post(url)
        # 由於我們的業務都是g GET 請求,所以就直接處理就好了
        res = requests.get(url)
        # 將代理請求的結果返回給真正的請求
        logging.info('revc : {}'.format(res.content.decode()))
        return HttpResponse(res.content.decode())
    else:
        # 收到的請求的格式是  wss://devlab.edu.huaweicloud.com/proxy?url=ws://ip:8000/senddata?model=workload-read-mostly
        # 其中,參數url=後面是要代理的url ,我們將其截取下來,並重新請求。
        url = request.get_full_path().split('url=')[1]
        logging.info('proxy url : {}'.format(url))
        try:
            # 這裏的header 必須要加上, 或者將 request中的header拿出來逐一存入list中,
            # 由於request中保存的headers的格式是dict,websocket中需要的headers是list,所以需要自行轉化,
            # websocket的header的格式如下, 由於業務沒有強制header 是什麼,所以我就自己寫的,但是注意,
            # 需要將以下的header的內容都要補充完整,否則會報 header...相關的錯誤,返回200或者其他狀態碼的錯誤,
            # 200在websocket是錯誤,在http裏面是成功,需要注意一下。
            header = ['Accept-Encoding: gzip, deflate', 'Connection: Upgrade', 'Pragma: no-cache',
                      'Sec-WebSocket-Extensions: permessage-deflate; client_max_window_bits',
                      'Sec - WebSocket - Version: 13',
                      'Upgrade: websocket',
                      'Access-Control-Allow-Origin: *',
                      'Access-Control-Allow-Headers: X-Requested-With',
                      'Access-Control-Allow-Methods: GET,POST,OPTIONS',
                      'User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.122 Safari/537.36']
            # 創建長連接/websocket
            ws = create_connection(url,header=header)
            # 每1秒就收一下消息
            while True:
                # 接收消息,如果消息中帶有Finished,表示消息都接收完畢,跳出循環。
                res = ws.recv()
                if 'Finished' in res:
                    logging.info('revc : {}'.format(res))
                    # 該服務,即作爲服務端(接收並處理請求並響應方),又做爲客戶端(發送請求並接收另一個服務的響應)
                    # 所以將代理服務推過來的結果再推給瀏覽器
                    request.websocket.send(res)
                    # 調試的時候,瀏覽器端好像收不到最後一條消息,所以我在以這種方式再發送一遍吧
                    # return HttpResponse(res)
                    # websocket 相當佔用網絡資源,請及時關閉,由於生產環境中,網絡資源的不穩定性,
                    # 可能數據還沒推出去,就把websocket關閉了,會造成數據的丟失。
                    time.sleep(5)
                    ws.close()
                    request.websocket.close()
                    break
                if not res == None:
                    logging.info('revc : {}'.format(res))
                    # 如果有數據, 將代理服務推過來的結果再推給瀏覽器
                    request.websocket.send(res)
                time.sleep(1)
            # websocket 相當佔用網絡資源,請及時關閉,其實邏輯是走不到這裏的,但是還是加上保險
            ws.close()
            request.websocket.close()
        except BaseException as e:
            logging.error(e)

8. 部署 python3 manage.py runserver 0.0.0.0:8080

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章