Content Table

Nginx + Tomcat 使用 Https

Nginx 作为前端反向代理或者负载均衡,Tomcat 不需要自己处理 https,https 由 Nginx 处理:

  • 用户首先和 Nginx 建立连接,完成 SSL 握手
  • 而后 Nginx 作为代理以 http 协议将请求转发给 Tomcat 处理
  • Nginx 再把 Tomcat 的输出通过 SSL 加密发回给用户

Tomcat 只是在处理 http 请求而已 (默认监听 8080 端口)。因此,这种情况下不需要配置 Tomcat 的 SSL,只需要配置 Nginx 的 SSL,Tomcat 和 Nginx 需要配置以下几项:

  • Nginx 中启用 https:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    http {
    include mime.types;
    default_type text/html;
    gzip on;
    gzip_types text/css text/x-component application/x-javascript application/javascript text/javascript text/x-js text/richtext image/svg+xml text/plain text/xsd text/xsl text/xml image/x-icon;
    sendfile on;

    # Tomcat 服务器集群
    upstream app_server {
    server 127.0.0.1:8080 weight=4;
    server 127.0.0.1:8081 weight=2;
    server 127.0.0.1:8082 weight=1;
    }

    server {
    listen 443 ssl; # https 的默认端口是 443
    charset utf-8;
    server_name www.xtuer.com; # host_name of URL

    # 启用 https
    ssl_certificate /Users/Biao/Desktop/cert/server.crt;
    ssl_certificate_key /Users/Biao/Desktop/cert/server.key;

    location / {
    proxy_redirect off;
    proxy_set_header Host $host;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;

    # 把 https 的协议告知 Tomcat,否则 Tomcat 可能认为是 http 的请求
    proxy_set_header X-Forwarded-Proto $scheme;

    # 请求转发给 Tomcat 集群处理
    proxy_pass http://app_server;
    }
    }
    }

    关键是以下几项:

    • listen port ssl
    • ssl_certificate
    • ssl_certificate_key
    • X-Forwarded-Proto
  • Tomcat 的 server.xml 的 Host 中配置 Valve:

    1
    2
    3
    <Host name="localhost" appBase="webapps" unpackWARs="true" autoDeploy="true">
    <Valve className="org.apache.catalina.valves.RemoteIpValve" remoteIpHeader="X-Forwarded-For" protocolHeader="X-Forwarded-Proto" protocolHeaderHttpsValue="https"/>
    </Host>
    • X-Forwarded-Proto 是为了正确地识别实际用户发出的协议是 http 还是 https
    • X-Forwarded-For 是为了获得实际用户的 IP
    • 如果不配置它们,则在 redirect 的时候仍然会使用 http 而不是 https
  • 注意: 当使用非 443 端口,如 9443 时,需要修改 2 个地方:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    Nginx: Host 中增加 $server_port
    listen 9443 ssl;
    proxy_set_header Host $host:$server_port;

    Tomcat: 必须配置 httpsServerPort,否则 request.getServerPort() 仍然返回 443
    <Valve className="org.apache.catalina.valves.RemoteIpValve"
    remoteIpHeader="X-Forwarded-For"
    protocolHeader="X-Forwarded-Proto"
    protocolHeaderHttpsValue="https"
    httpsServerPort="9443"
    />

自签证书

https 需要的证书可以去购买,也可以申请免费的证书,不过我们也可以自签一个证书在本地进行测试 (自签的证书在访问时需要用户手动确认信任证书才能继续访问),步骤如下:

  1. openssl genrsa -des3 -out server.key 1024

    1
    2
    3
    4
    5
    6
    Generating RSA private key, 1024 bit long modulus
    .....++++++
    ..++++++
    e is 65537 (0x10001)
    Enter pass phrase for server.key: # 输入密码 changeit
    Verifying - Enter pass phrase for server.key: # 输入密码 changeit
  2. openssl req -new -key server.key -out server.csr

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    Enter pass phrase for server.key:             # 输入上面设置的密码 changeit
    You are about to be asked to enter information that will be incorporated
    into your certificate request.
    What you are about to enter is what is called a Distinguished Name or a DN.
    There are quite a few fields but you can leave some blank
    For some fields there will be a default value,
    If you enter '.', the field will be left blank.
    -----
    Country Name (2 letter code) [AU]:CN # 国家的代码,中国输入 CN 即可
    State or Province Name (full name) [Some-State]:Beijing # 省
    Locality Name (eg, city) []:Beijing # 城市
    Organization Name (eg, company) [Internet Widgits Pty Ltd]:xo # 公司,随意
    Organizational Unit Name (eg, section) []:xo # 部门,随意
    Common Name (e.g. server FQDN or YOUR name) []:xtuer.com # 可以随意输入,不一定要是域名
    Email Address []: # 回车,不需要输入

    Please enter the following 'extra' attributes
    to be sent with your certificate request
    A challenge password []: # 回车,不需要输入
    An optional company name []: # 回车,不需要输入
  3. cp server.key server.key.org

  4. openssl rsa -in server.key.org -out server.key

    1
    2
    Enter pass phrase for server.key.org: # 输入上面设置的密码 changeit
    writing RSA key
  5. openssl x509 -req -days 365 -in server.csr -signkey server.key -out server.crt

    1
    2
    3
    Signature ok
    subject=/C=CN/ST=Beijing/L=Beijing/O=xo/OU=xo/CN=xtuer.com
    Getting Private key
  6. 把生成的 server.crt 和 server.key 配置到 Nginx

    1
    2
    3
    4
    5
    6
    7
    8
    # [1]
    listen 443 ssl; # https 的默认端口是 443
    # ssl on;
    ssl_certificate /Users/Biao/Desktop/cert/server.crt;
    ssl_certificate_key /Users/Biao/Desktop/cert/server.key;

    # [2] 把 https 的协议告知 Tomcat,否则 Tomcat 可能认为是 http 的请求
    proxy_set_header X-Forwarded-Proto $scheme;
  7. 加载 Nginx 配置 (以 Mac 下 MAMP 为例)

    • 测试配置文件语法:sudo /Applications/MAMP/Library/bin/nginxctl -t
    • 重新加载配置文件:sudo /Applications/MAMP/Library/bin/nginxctl -s reload
  8. 使用 https 访问网站,例如 https://www.xtuer.com

  9. 浏览器会提示证书不可信任,选择信任即可

    1
    2
    3
    Your connection is not private

    Attackers might be trying to steal your information from www.xtuer.com (for example, passwords, messages, or credit cards). NET::ERR_CERT_AUTHORITY_INVALID

参考资料