最近项目需要使用https,架构上使用了 Nginx +tomcat 集群,找了网上一些别人的方法,经过多次失败,终于搭建成功。特记录一下,供以后再次使用及他人使用参考。
由于我是在本地做测试,所以通过Openssl自己生成证书,正式项目使用需要向管理机构申请正式的证书。首先我们安装生成证书的Openssl软件。
步骤:
1. 安装Openssl
下载地址:http://slproweb.com/products/Win32OpenSSL.html (根据系统选择32位或者64位版本下载安装)。
下载完成后,进行安装,我安装在了 C:\wnmp\OpenSSL-Win64文件夹中。
2. 安装ActivePerl (此软件目的为了解析pl文件,部分系统不安装也可以实现本教程的功能,安装该软件目的为了学习perl。)。
下载地址:http://www.activestate.com/activeperl/downloads/ (根据系统选择win32或者win64版本下载安装)。
3. 配置环境变量
在环境变量中添加环境变量
变量名: OPENSSL_HOME 变量值:C:\wnmp\OpenSSL-Win64\bin; (变量值为openssl安装位置)
在path变量结尾添加如下 : %OPENSSL_HOME%;
4. 生成证书
(1) 首先在 nginx安装目录中创建ssl文件夹用于存放证书。比如我的文件目录为 C:\wnmp\nginx\ssl
以管理员身份进入命令行模式,进入ssl文件夹。 命令为: cd c:/wnmp/nginx/ssl
(2) 创建私钥
在命令行中执行命令: openssl genrsa -des3 -out lee.key 1024 (lee文件名可以自定义),如下图所示:
输入密码后,再次重复输入确认密码。记住此密码,后面会用到。
(3)创建csr证书
在命令行中执行命令: openssl req -new -key lee.key -out lee.csr (key文件为刚才生成的文件,lee为自定义文件名)
如上图所示,执行上述命令后,需要输入信息。输入的信息中最重要的为 Common Name,这里输入的域名即为我们要使用https访问的域名。
(4)去除密码。
在加载SSL支持的Nginx并使用上述私钥时除去必须的口令,否则会在启动nginx的时候需要输入密码。
复制lee.key并重命名为lee.key.org
可以使用此命令行,也可以使用鼠标操作 copy lee.key lee.key.org
去除口令,在命令行中执行此命令: openssl rsa -in lee.key.org -out lee.key (lee为自定义文件名)
如下图所示,此命令需要输入刚才设置的密码。
(5)生成crt证书
在命令行中执行此命令: openssl x509 -req -days 365 -in lee.csr -signkey lee.key -out lee.crt (lee为自定义文件名)
证书生成完毕,ssl文件夹中一共生成如下4个文件,我们需要使用到的是lee.crt和lee.key。
5. 修改位于nginx安装目录下的conf目录的nginx.conf文件
# HTTPS server
#
#server {
# listen 443 ssl;
# server_name localhost;
# ssl_certificate cert.pem;
# ssl_certificate_key cert.key;
# ssl_session_cache shared:SSL:1m;
# ssl_session_timeout 5m;
# ssl_ciphers HIGH:!aNULL:!MD5;
# ssl_prefer_server_ciphers on;
# location / {
# root html;
# index index.html index.htm;
# }
#}
修改为:
upstream www.test.com{
server www.test.com:8080;
server www.test.com:8088;
}
server {
listen 80;
server_name www.test.com;
#为一个server{......}开启ssl支持
ssl on;
ssl_certificate ../ssl/lee.crt;
ssl_certificate_key ../ssl/lee.key;
#ssl_session_cache shared:SSL:1m;
#ssl_session_timeout 5m;
#ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
#ssl_ciphers ECDH:AESGCM:HIGH:!RC4:!DH:!MD5:!3DES:!aNULL:!eNULL;
#ssl_prefer_server_ciphers on;
#让http请求重定向到https请求
#error_page 497 https://$host$uri?$args;
location / {
root html;
index index.html index.htm;
proxy_pass http://www.test.com;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_connect_timeout 1;
proxy_read_timeout 1;
proxy_send_timeout 1;
}
}
由于此处在本地做实验,我在C:\Windows\System32\drivers\etc的hosts.ics文件添加了映射:127.0.0.1 www.jyfeng.com,将www.jyfeng.com当做域名。也可直接使用localhost,而不需在hosts文件添加映射。但如果是服务器正式使用,那么需配置服务器申请的域名。
6. 修改tomcat的conf目录下的server.xml文件
修改为
解析:
https配置:在server标签里配置以下信息
listen 80;
server_name www.test.com;
#为一个server{......}开启ssl支持
ssl on;
ssl_certificate ../ssl/lee.crt;
ssl_certificate_key ../ssl/lee.key;
这里我将lee.crt和lee.key放在nginx安装目录下,也可放其它位置并使用绝对路径访问。
负载均衡配置:在http标签里配置以下信息
upstream www.test.com{
server www.test.com:8080;
server www.test.com:8088;
}
本地配置了端口分别为8080、8088的两个tomcat,以上配置会让nginx将www.test.com的请求轮询转发到两台tomcat上。注意,upstream后边的www.test.com要跟proxy_pass的http:www.test.com一致。假如proxy_pass为http:localhost,那么upstream为localhost。
如果tomcat 和nginx 双方没有配置X-Forwarded-Proto tomcat就不能正确区分实际用户是http 还是https,导致tomcat 里配置的静态资源被认为是http而被浏览器拦截,request.getScheme()总是 http,而不是实际的http或https。所以需要给nginx跟tomcat分别做一下配置:
nginx配置:在location标签里配置以下信息
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
tomcat配置:配置server.xml的Engine模块下host标签添加一个value
在测试中发现一个问题,如果有一台tomcat关闭了,那么nginx仍然在转发请求时,有部分请求会失败。原因是nginx部分请求转发到这个tomcat,但没有响应,而nginx默认等待响应时间为60秒,过了等待响应时间没有收到响应,才转发给另一台。所以这里需要在nginx的conf文件的location做如下配置:
proxy_connect_timeout 1;
proxy_read_timeout 1;
proxy_send_timeout 1;
这样如果超过一秒未响应,会转发给另一台tomcat。但这样假设一台服务器挂了,会有部分请求的响应时间增加一秒,体验上有些不好。但目前能找到的解决办法只有这个,不知为什么nginx不会直接剔除这个tomcat,目前还没找到原因,可能还需要其它配置,如有知道的道友,望不吝赐教。
参考:
https搭建: https://www.cnblogs.com/vincent-li666/p/5851463.html
Nginx SSL+tomcat集群: http://blog.csdn.net/vfush/article/details/51086274