由于系統(tǒng)是WAMP窒盐,由外包過渡過來(lái)的。在Apache設(shè)置反向代理比較不友善钢拧;且自chrome4.7以后蟹漓,大部分socket請(qǐng)求必須基于SSL協(xié)議傳輸。
前提:給項(xiàng)目加個(gè)長(zhǎng)連接源内,做個(gè)進(jìn)度條葡粒。
困境:請(qǐng)求協(xié)議為HTTP, 如果加入SSL協(xié)議,這不僅增加一筆費(fèi)用膜钓;對(duì)舊項(xiàng)目并不友好嗽交,php項(xiàng)目全是基于http的,而已Apache設(shè)置SSL估計(jì)又是折磨人颂斜。
解決:使用Apache設(shè)置反向代理夫壁,解決瀏覽器無(wú)法連接websocket的問題;使用localhost之前請(qǐng)求來(lái)解決安全性問題沃疮。
本文內(nèi)容
- 項(xiàng)目環(huán)境技術(shù)棧
- 后端Socket服務(wù)設(shè)計(jì)
- 試錯(cuò)過程
- 提交方案
- 運(yùn)行項(xiàng)目
- 參考網(wǎng)站
1.項(xiàng)目環(huán)境技術(shù)棧
- 構(gòu)架時(shí)盒让,后端代理前端壓縮包文件。
- 長(zhǎng)連接使用socket.io忿磅。
2.后端Socket服務(wù)設(shè)計(jì)
后端對(duì)外不開放糯彬,不需要使用反向代理。內(nèi)容如下所示(不全):
const express = require('express');
const http = require('http');
const socketio = require('socket.io');
const app = express();
const server = http.Server(app);
const io = socketio(server);
//加載前端頁(yè)面
app.use(express.static(path.join(__dirname, 'build')));
io.on('connection', (sock) => {
console.log('Client connected');
sock.on('heartbeat', (payload) => {
payload.nodeName = name;
sock.emit('heartbeat', payload);
});
sock.on('disconnect', () => {
console.log('Socket Disconnected');
});
});
server.listen(+port, '0.0.0.0', (err) => {
console.log(`Node [${name}] listens on http://127.0.0.1:${port}.`);
});
- Socket.io和Express一起使用葱她。
- 命令空間設(shè)計(jì)
3.試錯(cuò)過程
由于之前沒做過撩扒,好尷尬了《中看了幾個(gè)小時(shí)socket.io文檔直接使用了搓谆,所以踩的坑會(huì)比較多。
- 由于chrome4.7后豪墅,對(duì)安全策略進(jìn)行變化 泉手。先了解瀏覽器對(duì)websocket的長(zhǎng)連接的策略。
node代理
使用node http-proxy-middleware代理socket過程偶器,在本地(都是基本localhost)完美運(yùn)行斩萌。然后上傳運(yùn)行代理,并開放外網(wǎng)端口屏轰,運(yùn)行結(jié)果如下所示:
[HPM] Proxy created: / -> http://localhost:8787
[HPM] Subscribed to http-proxy events: [ 'error', 'close' ]
[HPM] Proxy created: / -> http://localhost:8787
[HPM] Subscribed to http-proxy events: [ 'error', 'close' ]
- 顯示代理成功颊郎,在服務(wù)器測(cè)試socket連接正常。但使用外網(wǎng)訪問時(shí)霎苗,如下的錯(cuò)誤:
Uncaught (in promise) DOMException: Only secure origins are allowed. http://goo.gl/lq4gCo
- 分析過程:
- 在服務(wù)器端測(cè)試時(shí),可以通過是因?yàn)椋核鼈冎g是localhost請(qǐng)求姆吭,能過瀏覽器安全策略。
- 服務(wù)器外網(wǎng)地址跟localhost在服務(wù)器在有網(wǎng)卡代理唁盏,localhost與外網(wǎng)交互内狸,沒安全策略的检眯,它也相當(dāng)于一個(gè)本地請(qǐng)求。
- 當(dāng)在外網(wǎng)訪問網(wǎng)頁(yè)端時(shí)昆淡,網(wǎng)頁(yè)在"/"鏈接中锰瘸,地址是外網(wǎng)地址。外網(wǎng)與localhost將無(wú)法通過瀏覽器安全策略瘪撇。
使用Apache代理過程
- 先實(shí)現(xiàn)一個(gè)小目標(biāo)获茬,實(shí)現(xiàn)一個(gè)簡(jiǎn)單的Http反向代理。
#不給別人get到你的代理
ProxyRequests off
<Location />
ProxyPass http://127.0.0.1:30003/
ProxyPassReverse /
</Location>
- 發(fā)現(xiàn)沒反應(yīng)倔既,ngnix直接用的尷尬恕曲,why not ok? 查下資料,才知道要加載 proxy_module, proxy_http_module渤涌。
- 那么接下去代理佩谣,想下Apache代理比較全的帖子,我擦实蓬,完全沒有提到Socket茸俭,就加入個(gè)mod_proxy_connect,說(shuō)不定以后能使用SSL呢。
- 那只有去官網(wǎng)找安皱,意淫術(shù)调鬓,socket的請(qǐng)求協(xié)議為WS,去httpd.conf搜下(找下)proxy-ws*酌伊,果然找到mod_proxy_wstunnel,就是這貨了腾窝。
- 接下去的看,只能在2.4.5以上版本使用居砖。呸城豁,然后查下2.4.3才發(fā)行揩慕。直接用別管它署浩。
Compatibility: |Available in httpd 2.4.5 and later - 在httpd.conf文件中未巫,加載這模塊
LoadModule proxy_wstunnel_module modules/mod_proxy_wstunnel.so
WAMP
- 有時(shí),沒什么毛病蔗草,它就是不給你運(yùn)行咒彤。這個(gè)迷之原因,炸了沒咒精。別扯什么底層代理實(shí)現(xiàn)過程蔼紧。
- 使用顏色分析過程,一般是:紅色->橙色->綠色狠轻。不行的話,紅色那狀態(tài)會(huì)比較快彬犯,然后停到橙色向楼。
- 使用重復(fù)啟動(dòng)法查吊,重復(fù)重啟。當(dāng)紅色停留比較久時(shí)湖蜕,那就可能成功了逻卖。
4.提交方案
下面將使用Apache反向代理,使網(wǎng)頁(yè)端與API接口變成localhost間的請(qǐng)求昭抒,原理圖如下所示评也,網(wǎng)頁(yè)端服務(wù)跟API服務(wù)將在內(nèi)部服務(wù)器中:
1. 將網(wǎng)頁(yè)端服務(wù)使用反向代理出來(lái)
- Apache 配置如下所示:
標(biāo)志RewriteEngine, Rewrite*, 規(guī)則NC, N* P*
//httpd.conf
Listen 30003
LoadModule proxy_http_module modules/mod_proxy_http.so
LoadModule proxy_connect_module modules/mod_proxy_connect.so
LoadModule proxy_wstunnel_module modules/mod_proxy_wstunnel.so
LoadModule proxy_http_module modules/mod_proxy_http.so
//httpd-vhosts.conf
<VirtualHost *:30003>
RewriteEngine On
RewriteCond %{HTTP:Connection} Upgrade [NC]
RewriteCond %{HTTP:Upgrade} websocket [NC]
RewriteRule /(.*) ws://127.0.0.1:3030/$1 [P,L]
ProxyRequests off
<Location />
ProxyPass http://127.0.0.1:3030/
ProxyPassReverse /
</Location>
</VirtualHost>
2.前后端分離,前端使用socket.io-client,代碼如下所示:
import openSocket from 'socket.io-client';
const socket = openSocket(); //不用設(shè)置任意參數(shù)
socket.on('heartbeat', function (data) {
console.log(data);
socket.emit('my other event', { my: 'data' });
});
5.運(yùn)行項(xiàng)目
- 訪問服務(wù)器端口30003地址灭返,其實(shí)是反向訪問localhost:3000盗迟。localhost地址之間socket長(zhǎng)連接無(wú)fuck。
-
不安全是Http協(xié)議的長(zhǎng)連接熙含。
6.參考網(wǎng)站
- chrome安全策略罚缕,解決方案,本文參考方案:https://webrtchacks.com/chrome-secure-origin-https/
- 反向代理:https://www.cnblogs.com/anruy/p/4989161.html
- 代理Http所需的模塊:https://stackoverflow.com/questions/9831594/apache-and-node-js-on-the-same-server
- Apache代理講解:http://www.apachetutor.org/admin/reverseproxies
- 標(biāo)志Rewrite*: https://httpd.apache.org/docs/2.4/mod/mod_proxy_wstunnel.html
- 規(guī)則N, P : https://httpd.apache.org/docs/2.4/rewrite/flags.html
- mod_proxy_wstunnel模塊用于配置socket:https://httpd.apache.org/docs/2.4/mod/mod_proxy_wstunnel.html
- Apache httpd.conf配置:https://stackoverflow.com/questions/29792372/apache2-websockets-reverse-proxy-on-same-url
如果socket請(qǐng)求跨源怎静,一定要有SSL設(shè)置邮弹?,不太清楚