Oracle APEX 系列文章11:全站啟用 HTTPS,讓你的 APEX 更安全
引言
目前主流的網站都要求 HTTPS 安全訪問,Google Chrome 瀏覽器、微信內建瀏覽器開啟非 HTTPS 的網頁,都會提示不安全。如果做微信端開發,也是必須要 HTTPS 的網址才可以,可見 HTTPS 越來越重要了。
如果你按照鋼哥之前的文章已經搭建好了 Oracle APEX 環境,那麼你的應用架構應該如下圖所示:
這裡簡單回顧一下各部分元件的作用:
* 使用者在瀏覽器位址列裡輸入URL,例如:https://apex.wangfanggang.com/ords/ (不要嘗試開啟這個網址了,我瞎寫的)
* Nginx監聽 HTTP (80
) 埠和 HTTPS (443
/i/
目錄中的內容,對於其他動態請求(如:APEX請求),進一步轉發至後端 Tomcat 伺服器做進一步處理。 * Tomcat 伺服器接收到請求後,會查詢部署在它上面的應用,就是我們之前部署的
ORDS
應用; * 如果是 APEX 請求,ORDS 進一步將請求轉發給 APEX (Oracle 資料庫) 進行處理;如果是 ORDS 請求,自身進行處理;
原理比較簡單,而我們要做的就是在 Nginx 層面講 HTTP 請求轉發到 HTTPS 上,進而實現全站 HTTPS 訪問。
申請 SSL 證書
這裡以在阿里雲上購買免費 SSL 證書為例,首先登入阿里雲控制檯,進入安全(雲盾)-> SSL證書(應用安全)
購買證書
。
進入到選擇購買頁面,提示1年需要五千多大洋,土豪直接點選付款即可。
好吧,我是窮人,只能看看有沒有免費證書。其實是有的,依次點選Symantec
-> 1個域名
-> 免費型DV SSL
,成功啟用30人:)
接下來回到控制檯,補全剛剛申請的證書資訊。
按照提示補全資訊。
鋼哥提示:由於我們申請的是阿里雲的免費證書,只能作用於一個固定域名,一般我們都不會把主域名用來放置APEX應用,所以這裡可以填寫諸如:apex.xxx.com 的二級域名。如果你想要免費萬用字元域名,可以移步這裡:使用Let’s Encrypt給網站加上免費HTTPS證書
另外需要注意的是,第二步的驗證環節,如果你選擇的是檔案驗證,請一定按照提示把對應的驗證檔案放到你的伺服器上,
正常檔案驗證一般不會超過5分鐘
,如果長時間沒驗證通過,一定是你操作有問題了。
當你的證書申請通過後,就可以點選下載
連結了。
配置 Nginx
將 SSL 證書新增進nginx.conf
,按照下載證書
頁面的提示配置 Nginx:
我的nginx.conf
檔案內容如下:
worker_processes auto;
worker_rlimit_nofile 10000;
error_log logs/error.log;
events
{
worker_connections 2048;
#==告訴nginx收到一個新連結通知後接受盡可能多的連結
multi_accept on;
#==設定用於複用客戶端執行緒的輪訓方法
use epoll;
}
http
{
include mime.types;
default_type application/octet-stream;
charset UTF-8;
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
access_log /etc/nginx/logs/access_log.log main;
server_tokens off;
sendfile on;
tcp_nopush on;
keepalive_timeout 65;
proxy_connect_timeout 600;
proxy_send_timeout 600;
proxy_read_timeout 600;
send_timeout 600;
#==設定nginx採用gzip壓縮的形式傳送資料,減少傳送資料量,但會增加請求處理時間及CPU處理時間,需要權衡
gzip on;
#==加vary給代理伺服器使用,針對有的瀏覽器支援壓縮,有個不支援,根據客戶端的HTTP頭來判斷是否需要壓縮
gzip_vary on;
gzip_http_version 1.0;
gzip_types text/plain application/javascript application/x-javascript text/css;
gzip_min_length 1024;
gzip_comp_level 3;
server
{ listen 443 default_server;
server_name apex.wangfanggang.com;
ssl on;
ssl_certificate cert/214412416080589.pem;
ssl_certificate_key cert/214412416080589.key;
ssl_session_timeout 5m;
ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:ECDHE:ECDH:AES:HIGH:!NULL:!aNULL:!MD5:!ADH:!RC4;
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_prefer_server_ciphers on;
location = /
{
# 預設開啟某個APEX應用
rewrite ^/(.*) https://apex.wangfanggang.com/ords/f?p=102 redirect;
}
location ~* \.(eot|ttf|woff|woff2)$
{
add_header Access-Control-Allow-Origin *;
}
location ^~ /i/
{
alias /u01/tomcat/webapps/i/;
}
location ^~ /ords/
{
# 將請求轉發到tomcat上
proxy_pass http://localhost:8080/ords/;
proxy_redirect off;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
client_max_body_size 20m;
}
}
server
{
listen 80 default_server;
server_name apex.wangfanggang.com;
include /etc/nginx/default.d/*.conf;
location = /
{
# 所有http請求統一重定向到https上
rewrite ^/(.*) https://apex.wangfanggang.com/ords/f?p=102 redirect;
}
location ~* \.(eot|ttf|woff|woff2)$
{
add_header Access-Control-Allow-Origin *;
}
location ^~ /i/
{
alias /u01/tomcat/webapps/i/;
}
location ^~ /ords/
{
# 所有http請求統一重定向到https上
proxy_pass https://apex.wangfanggang.com/ords/;
proxy_redirect off;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
client_max_body_size 20m;
}
error_page 404 /404.html;
location = /40x.html
{
}
error_page 500 502 503 504 /50x.html;
location = /50x.html
{
}
}
}
配置完 nginx,別忘了重啟令配置生效。再次在瀏覽器中訪問 APEX 頁面,如果能看到如下介面,恭喜你,你的 SSL 證書生效了!!
配置 Tomcat
鋼哥在配置的時候 SSL 證書時遇到了一個奇怪的問題,就是啟用 SSL 證書後,訪問 APEX 頁面時會發生重定向錯誤(302 error:too_many_redirects
),導致無法正常訪問。
經過跟同事幾天的研究,發現除了要在 Nginx 上啟用 SSL 證書以外,還必須在 Tomcat 上也啟用。還是回到阿里雲控制檯證書下載頁面,找到 Tomcat 配置證書部分。
鋼哥提示:特別要注意的是,這裡要選擇
JKS
格式證書進行安裝,否則會有問題。
在 Tomcat 的server.xml
檔案中新增如下內容:
<Valve
className = "org.apache.catalina.valves.RemoteIpValve"
remoteIpHeader = "X-Forwarded-For"
protocolHeader = "X-Forwarded-Proto"
/>
我的server.xml
檔案內容:
<?xml version="1.0" encoding="UTF-8"?>
<!--
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
this work for additional information regarding copyright ownership.
The ASF licenses this file to You under the Apache License, Version 2.0
(the "License"); you may not use this file except in compliance with
the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<!--
Note: A "Server" is not itself a "Container", so you may not
define subcomponents such as "Valves" at this level.
Documentation at /docs/config/server.html
-->
<Server port="8005" shutdown="SHUTDOWN">
<Listener className="org.apache.catalina.startup.VersionLoggerListener"
/>
<!--
Security listener. Documentation at /docs/config/listeners.html
<Listener className="org.apache.catalina.security.SecurityListener"
/>
-->
<!--
APR library loader. Documentation at /docs/apr.html
-->
<Listener className="org.apache.catalina.core.AprLifecycleListener" SSLEngine="on"
/>
<!--
Prevent memory leaks due to use of particular java/javax APIs
-->
<Listener className="org.apache.catalina.core.JreMemoryLeakPreventionListener"
/>
<Listener className="org.apache.catalina.mbeans.GlobalResourcesLifecycleListener"
/>
<Listener className="org.apache.catalina.core.ThreadLocalLeakPreventionListener"
/>
<!--
Global JNDI resources
Documentation at /docs/jndi-resources-howto.html
-->
<GlobalNamingResources>
<!--
Editable user database that can also be used by
UserDatabaseRealm to authenticate users
-->
<Resource
name = "UserDatabase" auth="Container"
type = "org.apache.catalina.UserDatabase"
description = "User database that can be updated and saved"
factory = "org.apache.catalina.users.MemoryUserDatabaseFactory"
pathname = "conf/tomcat-users.xml"
/>
</GlobalNamingResources>
<!--
A "Service" is a collection of one or more "Connectors" that share
a single "Container" Note: A "Service" is not itself a "Container",
so you may not define subcomponents such as "Valves" at this level.
Documentation at /docs/config/service.html
-->
<Service
name = "Catalina">
<!--
The connectors can use a shared executor, you can define one or more named thread pools
-->
<!--
<Executor
name = "tomcatThreadPool"
namePrefix = "catalina-exec-"
maxThreads = "150"
minSpareThreads = "4"
/>
-->
<!--
A "Connector" represents an endpoint by which requests are received
and responses are returned. Documentation at :
Java HTTP Connector: /docs/config/http.html
Java AJP Connector: /docs/config/ajp.html
APR (HTTP/AJP) Connector: /docs/apr.html
Define a non-SSL/TLS HTTP/1.1 Connector on port 8080
-->
<Connector
port = "8080"
protocol = "HTTP/1.1"
connectionTimeout = "20000"
redirectPort = "8443"
/>
<!--
A "Connector" using the shared thread pool
-->
<!--
<Connector
executor = "tomcatThreadPool"
port="8080" protocol = "HTTP/1.1"
connectionTimeout = "20000"
redirectPort = "8443"
/>
-->
<!--
Define a SSL/TLS HTTP/1.1 Connector on port 8443
This connector uses the NIO implementation. The default
SSLImplementation will depend on the presence of the APR/native
library and the useOpenSSL attribute of the
AprLifecycleListener.
Either JSSE or OpenSSL style configuration may be used regardless of
the SSLImplementation selected. JSSE style configuration is used below.
-->
<!--
<Connector
port = "8443"
protocol = "org.apache.coyote.http11.Http11NioProtocol"
maxThreads = "150"
SSLEnabled = "true">
<Certificate
certificateKeystoreFile = "conf/localhost-rsa.jks"
type = "RSA"
/>
</Connector>
-->
<Connector
port="8443"
protocol = "HTTP/1.1"
SSLEnabled = "true"
scheme = "https"
secure = "true"
keystoreFile = "cert/214412416080589.jks"
keystorePass = "abc123"
clientAuth = "false"
SSLProtocol = "TLSv1+TLSv1.1+TLSv1.2"
ciphers = "TLS_RSA_WITH_AES_128_CBC_SHA,TLS_RSA_WITH_AES_256_CBC_SHA,TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256,TLS_RSA_WITH_AES_128_CBC_SHA256,TLS_RSA_WITH_AES_256_CBC_SHA256"
/>
<!--
Define a SSL/TLS HTTP/1.1 Connector on port 8443 with HTTP/2
This connector uses the APR/native implementation which always uses
OpenSSL for TLS.
Either JSSE or OpenSSL style configuration may be used. OpenSSL style
configuration is used below.
-->
<!--
<Connector
port = "8443"
protocol = "org.apache.coyote.http11.Http11AprProtocol"
maxThreads = "150"
SSLEnabled = "true" >
<UpgradeProtocol
className="org.apache.coyote.http2.Http2Protocol"
/>
<SSLHostConfig>
<Certificate
certificateKeyFile = "conf/localhost-rsa-key.pem"
certificateFile = "conf/localhost-rsa-cert.pem"
certificateChainFile = "conf/localhost-rsa-chain.pem"
type = "RSA"
/>
</SSLHostConfig>
</Connector>
-->
<!--
Define an AJP 1.3 Connector on port 8009
-->
<Connector
port = "8009"
protocol = "AJP/1.3"
redirectPort = "8443"
/>
<!--
An Engine represents the entry point (within Catalina) that processes
every request. The Engine implementation for Tomcat stand alone
analyzes the HTTP headers included with the request, and passes them
on to the appropriate Host (virtual host).
Documentation at /docs/config/engine.html
-->
<!--
You should set jvmRoute to support load-balancing via AJP ie :
<Engine
name = "Catalina"
defaultHost = "localhost"
jvmRoute = "jvm1">
-->
<Engine
name = "Catalina"
defaultHost = "localhost">
<!--
For clustering, please take a look at documentation at:
/docs/cluster-howto.html (simple how to)
/docs/config/cluster.html (reference documentation)
-->
<!--
<Cluster className="org.apache.catalina.ha.tcp.SimpleTcpCluster"
/>
-->
<!--
Use the LockOutRealm to prevent attempts to guess user passwords
via a brute-force attack
-->
<Realm className="org.apache.catalina.realm.LockOutRealm">
<!--
This Realm uses the UserDatabase configured in the global JNDI
resources under the key "UserDatabase". Any edits
that are performed against this UserDatabase are immediately
available for use by the Realm.
-->
<Realm
className = "org.apache.catalina.realm.UserDatabaseRealm"
resourceName = "UserDatabase"
/>
</Realm>
<Host
name = "localhost"
appBase = "webapps"
unpackWARs = "true"
autoDeploy = "true">
<Valve
className = "org.apache.catalina.valves.RemoteIpValve"
remoteIpHeader = "X-Forwarded-For"
protocolHeader = "X-Forwarded-Proto"
/>
</Host>
</Engine>
</Service>
</Server>
重啟 Tomcat 伺服器,再次訪問 APEX,煩人的重定向問題終於得以解決。
結語
用 HTTPS 協議來安全地訪問你的 APEX 應用,這一點特別是對企業應用特別重要,相信你現在已經掌握瞭如何在 APEX 上全站啟用 SSL 證書。本文如有遺漏或不足的地方也請隨時跟鋼哥交流,讓我們共同學習,共同進步!
王方鋼 | APEX Evangelist