HTTPS請求HTTP接口被瀏覽器阻塞,python實現websocket客戶端,websocket服務器,跨域問題,dwebsocket,https,攔截,服務端
發表時間:2020-03-05
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