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

你可能感兴趣的:(互联网架构,websocket,django,python,https)