保护 Java 服务的服务远程处理通信Secure service remoting communications in a Java service

安全是通信最为重视的要素之一。Security is one of the most important aspects of communication. Reliable Services 应用程序框架提供了一些预先构建的通信堆栈和工具,可用来提高安全性。The Reliable Services application framework provides a few prebuilt communication stacks and tools that you can use to improve security. 本文介绍如何在 Java 服务中使用服务远程处理时提高安全性。This article discusses how to improve security when you're using service remoting in a Java service. 它基于现有的示例构建,该示例解释了如何为使用 Java 编写的可靠服务设置远程处理。It builds on an existing example that explains how to set up remoting for reliable services written in Java.

若要在 Java 服务中使用服务远程处理时帮助保护服务,请遵循以下步骤:To help secure a service when you're using service remoting with Java services, follow these steps:

  1. 创建接口 HelloWorldStateless,用于定义可供服务的远程过程调用使用的方法。Create an interface, HelloWorldStateless, that defines the methods that will be available for a remote procedure call on your service. 服务将使用 microsoft.serviceFabric.services.remoting.fabricTransport.runtime 包中声明的 FabricTransportServiceRemotingListenerYour service will use FabricTransportServiceRemotingListener, which is declared in the microsoft.serviceFabric.services.remoting.fabricTransport.runtime package. 这是可以提供远程处理功能的 CommunicationListener 实现。This is an CommunicationListener implementation that provides remoting capabilities.

    public interface HelloWorldStateless extends Service {
        CompletableFuture<String> getHelloWorld();
    }
    
    class HelloWorldStatelessImpl extends StatelessService implements HelloWorldStateless {
        @Override
        protected List<ServiceInstanceListener> createServiceInstanceListeners() {
            ArrayList<ServiceInstanceListener> listeners = new ArrayList<>();
            listeners.add(new ServiceInstanceListener((context) -> {
                return new FabricTransportServiceRemotingListener(context,this);
            }));
        return listeners;
        }
    
        public CompletableFuture<String> getHelloWorld() {
            return CompletableFuture.completedFuture("Hello World!");
        }
    }
    
  2. 添加侦听器设置和安全凭据。Add listener settings and security credentials.

    确保要用来帮助保护服务通信的证书安装在群集中的所有节点上。Make sure the certificate that you want to use to help secure your service communication is installed on all the nodes in the cluster. 对于在 Linux 上运行的服务,证书必须以 PEM 格式的文件提供,要么是包含证书和私钥的 .pem 文件,要么是包含证书的 .crt 文件和包含私钥的 .key 文件。For services running on Linux, the certificate must be available as a PEM-formmatted file; either a .pem file that contains the certificate and private key or a .crt file that contains the certificate and a .key file that contains the private key. 有关详细信息,请参阅 Linux 节点上的 X.509 证书的位置和格式To learn more, see Location and format of X.509 certificates on Linux nodes.

    有两种方式可用于提供侦听器设置和安全凭据:There are two ways that you can provide listener settings and security credentials:

    1. 使用配置包提供:Provide them by using a config package:

      在 settings.xml 文件中添加名为 TransportSettings 的节。Add a named TransportSettings section in the settings.xml file.

      <!--Section name should always end with "TransportSettings".-->
      <!--Here we are using a prefix "HelloWorldStateless".-->
      <Section Name="HelloWorldStatelessTransportSettings">
          <Parameter Name="MaxMessageSize" Value="10000000" />
          <Parameter Name="SecurityCredentialsType" Value="X509_2" />
          <Parameter Name="CertificatePath" Value="/path/to/cert/BD1C71E248B8C6834C151174DECDBDC02DE1D954.crt" />
          <Parameter Name="CertificateProtectionLevel" Value="EncryptandSign" />
          <Parameter Name="CertificateRemoteThumbprints" Value="BD1C71E248B8C6834C151174DECDBDC02DE1D954" />
      </Section>
      
      

      在这种情况下,createServiceInstanceListeners 方法如下所示:In this case, the createServiceInstanceListeners method will look like this:

      protected List<ServiceInstanceListener> createServiceInstanceListeners() {
          ArrayList<ServiceInstanceListener> listeners = new ArrayList<>();
          listeners.add(new ServiceInstanceListener((context) -> {
              return new FabricTransportServiceRemotingListener(context,this, FabricTransportRemotingListenerSettings.loadFrom(HelloWorldStatelessTransportSettings));
          }));
          return listeners;
      }
      

      如果将在 settings.xml 中添加 TransportSettings 节而不添加任何前缀,则 FabricTransportListenerSettings 将按默认加载此节中的所有设置。If you add a TransportSettings section in the settings.xml file without any prefix, FabricTransportListenerSettings will load all the settings from this section by default.

      <!--"TransportSettings" section without any prefix.-->
      <Section Name="TransportSettings">
          ...
      </Section>
      

      在这种情况下,CreateServiceInstanceListeners 方法如下所示:In this case, the CreateServiceInstanceListeners method will look like this:

      protected List<ServiceInstanceListener> createServiceInstanceListeners() {
          ArrayList<ServiceInstanceListener> listeners = new ArrayList<>();
          listeners.add(new ServiceInstanceListener((context) -> {
              return new FabricTransportServiceRemotingListener(context,this);
          }));
          return listeners;
      }
      
  3. 在安全服务上使用远程堆栈(而不是使用 microsoft.serviceFabric.services.remoting.client.ServiceProxyBase 类)调用方法来创建服务代理时,请使用 microsoft.serviceFabric.services.remoting.client.FabricServiceProxyFactoryWhen you call methods on a secured service by using the remoting stack, instead of using the microsoft.serviceFabric.services.remoting.client.ServiceProxyBase class to create a service proxy, use microsoft.serviceFabric.services.remoting.client.FabricServiceProxyFactory.

    如果客户端代码正在作为服务的一部分运行,则可以从 settings.xml 文件中加载 FabricTransportSettingsIf the client code is running as part of a service, you can load FabricTransportSettings from the settings.xml file. 创建与服务代码类似的 TransportSettings 节,如上所示。Create a TransportSettings section that is similar to the service code, as shown earlier. 对客户端代码进行以下更改。Make the following changes to the client code:

    
    FabricServiceProxyFactory serviceProxyFactory = new FabricServiceProxyFactory(c -> {
            return new FabricTransportServiceRemotingClientFactory(FabricTransportRemotingSettings.loadFrom("TransportPrefixTransportSettings"), null, null, null, null);
        }, null)
    
    HelloWorldStateless client = serviceProxyFactory.createServiceProxy(HelloWorldStateless.class,
        new URI("fabric:/MyApplication/MyHelloWorldService"));
    
    CompletableFuture<String> message = client.getHelloWorld();