使用Nginx代理restful实现SSL链路加密

时间:2022-04-27
本文章向大家介绍使用Nginx代理restful实现SSL链路加密,主要内容包括1 目标说明、1.2 目标网络模型、1.3 SSL说明、2 调研过程、2.2 测试https 加密restful、3 总结、基本概念、基础应用、原理机制和需要注意的事项等,并结合实例形式分析了其使用技巧,希望通过本文能帮助到大家理解应用这部分内容。

1 目标说明

1.1 调研目的

本次调研主要为了解决两个问题:

  • 不需要对restful的web容器做任何配置,实现对restful链路进行加密;
  • 方便restful应用进行扩展,采用多个服务进行负载均衡,以提升吞吐量。

1.2 目标网络模型

    希望达到的目标网络模型如下:

1.3 SSL说明

    通过对SSL的学习,结合自身业务的考虑,对SSL的使用做如下说明:

    我这里SSL使用TLSv1,并且服务端不需要校验客户端的身份合法性,则使用SSL单向认证方式,只需要服务端证书。另外我们只需要用到SSL的链路加密,所以可以设置客户端对服务端证书保持永久信任

2 调研过程

这里restful使用jersey来实现,使用jetty作为javaee容器。

2.1 测试非加密restful

通过jetty发布非加密restful服务,url为 http://localhost:8080/api/v1/....

2.1.1 服务端代码

web.xml

    <servlet>
        <servlet-name>RestApplication</servlet-name>
        <servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class>
        <init-param>
            <param-name>javax.ws.rs.Application</param-name>
            <param-value>com.spiro.test.jersey.MyApplication</param-value>
        </init-param>
    </servlet>
    <servlet-mapping>
        <servlet-name>RestApplication</servlet-name>
        <url-pattern>/api/v1/*</url-pattern>
    </servlet-mapping>

Resource:

import com.spiro.test.jersey.entity.Terminal;

import javax.inject.Singleton;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import java.util.ArrayList;
import java.util.List;

@Path("terminals")
@Singleton
public class TerminalsResource {

    @GET
    @Produces(MediaType.APPLICATION_JSON)
    public Response getAll() {

        List<Terminal> terminals = new ArrayList<Terminal>();

        Terminal ter1 = new Terminal();
        ter1.setId("101");
        ter1.setDesc("I'm 101");
        terminals.add(ter1);

        Terminal ter2 = new Terminal();
        ter2.setId("102");
        ter2.setDesc("I'm 102");
        terminals.add(ter2);


//        if(true) {
//            return Response.status(Response.Status.UNAUTHORIZED).build();
//        }

        return Response.ok(terminals).build();
    }
}

ResourceConfig:

import com.spiro.test.jersey.resources.TerminalsResource;
import org.glassfish.jersey.jackson.JacksonFeature;
import org.glassfish.jersey.server.ResourceConfig;

public class MyApplication extends ResourceConfig {

    public MyApplication() {
        register(TerminalsResource.class);
        register(JacksonFeature.class);
    }
}

2.1.2 客户端代码

    public static void testHttp() {

        ClientConfig clientConfig = new ClientConfig();
        Client client = ClientBuilder.newClient(clientConfig);

        String url = "http://localhost:8080/api/v1/";

        String entity = client.target(url)
                .path("terminals")
                .request(MediaType.APPLICATION_JSON)
                .get(String.class);

        System.out.println(entity);
    }

经测试成功打印:[{"id":"101","desc":"I'm 101"},{"id":"102","desc":"I'm 102"}]

2.2 测试https 加密restful

2.2.1 nginx安装配置

    在windows7机器上安装nginx-1.10.1,配置如下:

worker_processes  1;

events {
    worker_connections  1024;
}

http {
    include       mime.types;
    default_type  application/octet-stream;

    server {
        listen       443 ssl;
        server_name  localhost;

		ssl_certificate       D:/server.crt;
		ssl_certificate_key   D:/_server.key;

        location / {
            proxy_pass   http://127.0.0.1:8080;
        }
    }
}

2.2.2 服务端代码

   同2.1.1

2.2.3 客户端代码

// Create a trust manager that does not validate certificate chains
TrustManager[] trustAllCerts = new TrustManager[] {new X509TrustManager() {
    public X509Certificate[] getAcceptedIssuers() {
        return null;
    }
    public void checkClientTrusted(X509Certificate[] certs, String authType) {
    }
    public void checkServerTrusted(X509Certificate[] certs, String authType) {
    }
}
};

// Install the all-trusting trust manager
SSLContext sslContext = SSLContext.getInstance("SSL");
sslContext.init(null, trustAllCerts, new java.security.SecureRandom());
HttpsURLConnection.setDefaultSSLSocketFactory(sslContext.getSocketFactory());

// Create all-trusting host name verifier
HostnameVerifier allHostsValid = new HostnameVerifier() {
    public boolean verify(String hostname, SSLSession session) {
        return true;
    }
};

HttpsURLConnection.setDefaultHostnameVerifier(allHostsValid);

Client client = ClientBuilder.newBuilder().sslContext(sslContext).build();

String entity = client.target("https://127.0.0.1:443/api/v1/")
        .path("terminals")
        .request(MediaType.APPLICATION_JSON)
        .get(String.class);

System.out.println(entity);

设置客户端请求连接为ssl加密,并且客户端永久信任服务端,不对服务端证书进行验证。

经测试成功打印:[{"id":"101","desc":"I'm 101"},{"id":"102","desc":"I'm 102"}]

3 总结

    经测试,可以通过nginx https代理restful 实现链路加密,后续可通过nginx upstream实现负载均衡。

完整代码请查看