SSL / TLS REST服务器–带有Spring和TomEE的客户端

在构建系统时,开发人员通常会忽略安全性方面。 安全一直是令人担忧的重要问题,但是它比以前吸引了更高的关注。 就在今年,我们发生了像Heartbleed Bug或CelebrityGate丑闻这样的案件。 这与帖子无关,只是安全真正重要的示例,我们应该意识到这一点。

随着REST服务的日益普及,有必要以某种方式确保这些安全。 几周前,我不得不将客户端与https后面的REST服务集成。 我以前从未做过,这就是这篇文章的原因。 我必须承认自己不是安全专家,所以如果我写任何愚蠢的文章,请纠正我。

设置

对于此示例,我使用了以下设置:

我不会讨论有关SSL和TSL的许多详细信息,因此请在此处查看其他内容。 请注意,TLS是SSL演进的新名称。 有时两者之间会产生混淆,人们通常会说SSL,但使用的是TSL的最新版本。 记住这一点。

不要忘记按照下一页上的说明为Tomcat设置SSL: SSL Configuration HOW-TO 。 服务器需要向客户端提供一组凭据(证书),以保护服务器与客户端之间的连接。

编码

服务

让我们创建一个简单的Spring REST服务:

RestService.java
@Controller
@RequestMapping("/")
public class RestService {
    @RequestMapping(method = RequestMethod.GET)
    @ResponseBody
    public String get() {
        return "Called the get Rest Service";
    }
}

而且,我们还需要一些接线来使其工作:

RestConfig.java
@Configuration
@EnableWebMvc
@ComponentScan(basePackages = "com.radcortez.rest.ssl")
public class RestConfig {}
web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app
    version="3.1"
    xmlns="http://xmlns.jcp.org/xml/ns/javaee"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd">

    <servlet>
        <servlet-name>rest</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <init-param>
            <param-name>contextClass</param-name>
            <param-value>org.springframework.web.context.support.AnnotationConfigWebApplicationContext</param-value>
        </init-param>
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>com.radcortez.rest.ssl</param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
    </servlet>

    <servlet-mapping>
        <servlet-name>rest</servlet-name>
        <url-pattern>/</url-pattern>
    </servlet-mapping>

    <security-constraint>
        <web-resource-collection>
            <web-resource-name>Rest Application</web-resource-name>
            <url-pattern>/*</url-pattern>
        </web-resource-collection>
        <user-data-constraint>
            <!-- Needed for our application to respond to https requests -->
            <transport-guarantee>CONFIDENTIAL</transport-guarantee>
        </user-data-constraint>
    </security-constraint>
</web-app>

请注意元素security-constraintuser-data-constraint<transport-guarantee>CONFIDENTIAL</transport-guarantee> 。 需要这些来指定应用程序需要安全连接。 选中“ 保护 Java应用程序的Web应用程序安全”

运行服务

只需使用您喜欢的IDE环境在TomEE服务器上部署应用程序,然后访问https://localhost:8443/ 。 您应该获得以下信息(您可能需要先接受服务器证书):

其余服务本地主机

请注意,浏览器协议为https ,端口为8443 (假设您将默认设置保留在SSL Configuration HOW-TO中)

客户

现在,如果您尝试使用Java客户端调用此REST服务,则很可能将获得以下消息和Exception(或类似信息):

消息: GET请求“ https:// localhost:8443 /”上的I / O错误:sun.security.validator.ValidatorException:

异常: 以下原因 引起:javax.net.ssl.SSLHandshakeException:sun.security.validator.ValidatorException:PKIX路径构建失败:sun.security.provider.certpath.SunCertPathBuilderException:无法找到到请求目标的有效证书路径

发生这种情况是因为正在运行的JDK没有针对您的服务器的有效证书。 您可以导入它,并解决该问题,但是让我们做一些更有趣的事情。 我们将以编程方式为受信任的密钥库提供服务器证书。

这在以下情况下特别有用:

  • 您正在将代码运行到多个环境中
  • 您不必每次都手动将证书导入JDK
  • 如果您升级JDK,则必须记住有关证书的信息
  • 由于某些奇怪的原因,您无权访问JDK本身来导入证书

让我们写一些代码:

RestClientConfig.java
@Configuration
@PropertySource("classpath:config.properties")
public class RestClientConfig {
    @Bean
    public RestOperations restOperations(ClientHttpRequestFactory clientHttpRequestFactory) throws Exception {
        return new RestTemplate(clientHttpRequestFactory);
    }

    @Bean
    public ClientHttpRequestFactory clientHttpRequestFactory(HttpClient httpClient) {
        return new HttpComponentsClientHttpRequestFactory(httpClient);
    }

    @Bean
    public HttpClient httpClient(@Value("${keystore.file}") String file,
                                 @Value("${keystore.pass}") String password) throws Exception {
        KeyStore trustStore = KeyStore.getInstance(KeyStore.getDefaultType());
        FileInputStream instream = new FileInputStream(new File(file));
        try {
            trustStore.load(instream, password.toCharArray());
        } finally {
            instream.close();
        }

        SSLContext sslcontext =
                SSLContexts.custom().loadTrustMaterial(trustStore, new TrustSelfSignedStrategy()).build();
        SSLConnectionSocketFactory sslsf =
                new SSLConnectionSocketFactory(sslcontext, new String[]{"TLSv1.2"}, null,
                                               BROWSER_COMPATIBLE_HOSTNAME_VERIFIER);
        return HttpClients.custom().setSSLSocketFactory(sslsf).build();
    }

    @Bean
    public static PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer() {
        return new PropertySourcesPlaceholderConfigurer();
    }
}

在这里,我们使用Spring RestOperations接口,该接口指定了一组基本的RESTful操作。 接下来,我们使用Apache HTTP组件SSLConnectionSocketFactory ,它使我们能够根据可信证书列表来验证服务器的身份。 证书从KeyStore从服务器使用的同一文件中加载

RestServiceClientIT.java
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = RestClientConfig.class)
public class RestServiceClientIT {
    @Autowired
    private RestOperations rest;

    @Test
    public void testRestRequest() throws Exception {
        ResponseEntity response = rest.getForEntity("https://localhost:8443/", String.class);
        System.out.println("response = " + response);
        System.out.println("response.getBody() = " + response.getBody());
    }
}

一个简单的测试类。 我们还需要一个具有密钥库文件位置和密码的属性文件:

config.properties
keystore.file=${user.home}/.keystore
keystore.pass=changeit

如果您使用了所有默认值,这应该可以正常工作。

运行测试

如果现在运行在Java客户端中调用REST服务的测试,则应获得以下输出:

响应<200 OK,调用了get Rest服务,{服务器= [Apache-土狼/1.1],缓存控制= [私有],到期时间= [星期四,1970年1月1日,周四,01:00:00 WET],内容类型= ,Content-Length = [27],Date = [Tue,2014年12月23日,格林尼治标准时间]]>

身体叫得到休息服务

结论

而已! 现在,您可以以安全的方式与客户端调用REST服务。 如果您希望将证书添加到JDK密钥库,请检查此文章

敬请期待与Java EE JAX-RS等效的等效产品。

资源资源

您可以从我的github仓库REST SSL克隆完整的工作副本。

翻译自: https://www.javacodegeeks.com/2014/12/ssl-tls-rest-server-client-with-spring-and-tomee.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值