0. 環境信息
1. 問題描述
我們開發的客服系統,因為消息的到來,有的谷歌瀏覽器(V62)不支持http的消息提醒,要求https,故而,我們的系統,要將系統改造成https模式,另外,我們的系統,也有必要轉化為https,為後續推廣做準備。
2. 系統架構
LB+nginx+tomcat集群
3. 當前配置情況
SSL證書配置在LB上,nginx和tomcat服務器上,任然採用http協議通訊。即LB在接收到客戶瀏覽器https請求消息後,將轉發給LB下掛載的nginx上,都是以http的方式轉發,nginx對這些請求進行反向代理,代理到後面的tomcat服務器上。
4. 遇到的問題
客服系統,有權限控制,基於tomcat的web應用,用戶登錄後,執行redirect跳轉到指定的服務頁面。就是這個跳轉,遇到了問題,redirect在這裡都被當做http跳轉了。
登錄前的樣子:
登錄後的樣子:
5. 如何解決
針對Tomcat7的這個問題,思路很簡單,重點是解決redirect的時候,通知客戶端瀏覽器以正確的scheme(https還是http)進行再次發起請求。
問題是, nginx這個時候收到的請求是來自LB的http請求了,怎麼弄?其實是有辦法的,可以利用HttpRequest中的referer字段,這個字段的含義,自行科普吧。將referer的請求scheme信息,用來作為當前請求的scheme,如此可以保證所有的請求都是同一個scheme,不會因為redirect而遺漏信息。
nginx裡面相應的配置如下:
location /CSS/websocket {
proxy_pass http://css_ws_svr;
proxy_set_header Host $host;
proxy_set_header Remote_Addr $remote_addr;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}
location /CSS {
proxy_pass http://css_svr;
proxy_set_header Host $host;
proxy_set_header Remote_Addr $remote_addr;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
set $mscheme $scheme;
if ($http_referer ~* ^https.*) {
set $mscheme "https";
}
proxy_set_header X-Forwarded-Proto $mscheme;
}
如上配置,經過nginx反向代理後的HttpServletRequest中header部分就帶上了字段X-Forwarded-Proto。
另外一方面,就是tomcat裡面,要做一個配置,讓tomcat在解析請求和做重定向的時候,知道用什麼協議。主要的配置在server.xml裡面的Engine下,定義一個Value元素。
具體配置如下:
<engine>
<realm>
<realm>
resourceName="UserDatabase"/>
/<realm>
<host>
unpackWARs="true" autoDeploy="true">
<valve>
prefix="localhost_access_log." suffix=".txt"
pattern="%h %l %u %t "%r" %s %b" />
<valve>
<context>
/<valve>/<host>
這個配置裡面,重點是protocolHeader字段,意思就是說,當protocolHeader字段的值為protocolHeaderHttpsValue的https的時候,認為是安全連接,否則就是http的非安全連接。
對應的代碼邏輯,可以看org.apache.catalina.valves.RemoteIpValve這個類的源碼:
public void invoke(org.apache.catalina.connector.Request request, Response response)
throws IOException, ServletException
{
......
if (protocolHeader != null) {
String protocolHeaderValue = request.getHeader(protocolHeader);
if (protocolHeaderValue != null)
{
if (protocolHeaderHttpsValue.equalsIgnoreCase(protocolHeaderValue)) {
request.setSecure(true);
request.getCoyoteRequest().scheme().setString("https");
setPorts(request, httpsServerPort);
} else {
request.setSecure(false);
request.getCoyoteRequest().scheme().setString("http");
setPorts(request, httpServerPort);
}
}
}
......
}
經過上面的分析和配置修改,最終很靈活的實現https和http同時工作。搞定這個問題,重點還是要對Http協議工作的流程有所瞭解,
Nginx,linux ,c++,服務器,ffmpeg,流媒體,嵌入式,等…………資料可私信後臺,〈資料〉兩字免費領取
閱讀更多 編程資料庫 的文章