java - 无法在Jersey中进行基本的HTTP身份验证

标签 java rest ssl jersey jersey-client

我正在尝试使用Jersey 1.X版本连接到安全的外部休息服务。

我用下面的代码

public class MyRestClient
{
  private static final String API_USER_NAME = "some value";
  private static final String API_PASSWORD = "some value";
  private static final String REST_URL = "https://<somevalue>";

  public static void main(String[] args)
  {
    ClientConfig config = new DefaultClientConfig();
    Client client = Client.create(config);
    client.addFilter(new HTTPBasicAuthFilter(API_USER_NAME, API_PASSWORD));
    WebResource webResource =
      client.resource(UriBuilder.fromUri(REST_URL).build());

    ClientResponse response = webResource.post(ClientResponse.class);
    System.out.println(response);
  }
}


但是我一直在碰这个例外。

com.sun.jersey.api.client.ClientHandlerException: javax.net.ssl.SSLHandshakeException: java.security.cert.CertificateException: No name matching 'somevalue' found


我检查了此外部Rest服务的API,它说它支持基本HTTP身份验证,但我不知道为什么我总是遇到此错误。

有什么想法吗?

最佳答案

由于基本身份验证本身缺乏安全性,因此通常通过SSL完成,如您在URL的https模式中所看到的。使用SSL时,使用了certificates。 SSL握手由服务器发送其证书和客户端检查其信任库组成,以查看证书是否受信任。该平台应具有其信任的证书颁发机构列表。例如,如果我们尝试访问

WebTarget target = client.target("https://wikipedia.org");


这将起作用,因为Wikipedia发送的证书是由系统中受信任的机构签名的。另一方面,如果来自服务器的证书未由这些受信任的授权机构之一签名,则SSL握手将失败。

如果是这种情况,则需要配置Client来处理SSL握手,这就是为什么您会收到异常的原因。您可以在here上看到有关如何配置Client以便与https一起使用的一些很好的答案。



更新


  您提供的链接已失效,所以我不知道“ myTrustManager”和“ hostnameVerifier”是什么...您可以分享一些有关如何提供该信息的信息吗?


import com.sun.jersey.api.client.Client;
import com.sun.jersey.api.client.ClientResponse;
import com.sun.jersey.api.client.config.ClientConfig;
import com.sun.jersey.api.client.config.DefaultClientConfig;
import com.sun.jersey.client.urlconnection.HTTPSProperties;
import java.io.FileInputStream;
import java.security.KeyStore;
import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManagerFactory;
import org.junit.Test;

public class JUnitTest {

    private static final String TRUSTSTORE_FILE = "<location-of-truststore";
    private static final String TRUSTSTORE_PASSWORD = "trustStorePassword";

    @Test
    public void test() throws Exception {
        KeyStore truststore = KeyStore.getInstance("JKS");
        truststore.load(new FileInputStream(TRUSTSTORE_FILE), 
                                            TRUSTSTORE_PASSWORD.toCharArray());
        TrustManagerFactory tmf = TrustManagerFactory.getInstance("SunX509");
        tmf.init(truststore);
        SSLContext sslContext = SSLContext.getInstance("SSL");
        sslContext.init(null, tmf.getTrustManagers(), null);

        ClientConfig config = new DefaultClientConfig();
        config.getProperties().put(HTTPSProperties.PROPERTY_HTTPS_PROPERTIES, 
                new HTTPSProperties(null, sslContext));
        Client client = Client.create(config);

        final String httpsUrl = "https://...";
        ClientResponse response = client.resource(httpsUrl).get(ClientResponse.class);
        System.out.println(response.getStatus());
        System.out.println(response.getEntity(String.class));
    } 
}


基本上,您需要从拥有API的人员那里获取X.509 Certificate(有关编程方式,请参见下面的链接)。然后,将其导入到信任库中。这就是您的客户端知道信任连接的方式。如果您相信服务器是他们所说的身份,则可以对连接进行加密。

获得证书后,可以使用Java keytool导入它。通过做这个


  keytool -import -alias serverCert -file <cert.file> -keystore <client_trust_file>


系统将要求您输入密码。然后询问您是否信任证书。输入“是”,就可以完成。这是您键入的文件(client_trust_file)和密码,应在上面的代码中使用。


Programmaticly add a remote certificate to your truststore
进一步了解Java Secure Socket Framework




更新2

有关创建通过与Tomcat进行安全SSL连接的简单应用的说明。使用上面的客户端代码来访问它。我将使用Netbeans 8,但也会尝试以一般方式包括执行此操作的说明。我还将使用Tomcat 8(Tomcat 7的配置可能有所不同。有关任何区别,请查阅文档)。我将使用Maven,因此希望您习惯使用它。

步骤1:

创建一个新应用。我将从Maven原型创建一个简单的Jersey应用程序。在Netbeans中


  文件→新项目→Maven→来自原型的项目→搜索“ jersey-quickstart-webapp”;选择一个具有groupId“ org.glassfish.jersey.archetypes”的项目→下一步→将项目命名为“ secured-rest-app”→希望您可以完成其余工作。您应该最终得到一个Maven应用程序。


在任何其他支持Maven的IDE中,只需查找具有以下坐标的原型:


groupId:org.glassfish.jersey.archetypes
artifactId:jersey-quickstart-webapp
版本:2.13


从命令行:执行

mvn archetype:generate -DarchetypeArtifactId=jersey-quickstart-grizzly2 \
    -DarchetypeGroupId=org.glassfish.jersey.archetypes -DinteractiveMode=false \
    -DgroupId=com.example -DartifactId=secured-rest-app -Dpackage=secured.rest.app \
    -DarchetypeVersion=2.13 


第2步:

我们需要在应用程序中设置基本身份验证。这可以在web.xm l中完成。打开项目的web.xml,并将其添加到</servlet-mapping>

<security-constraint>
    <web-resource-collection>
        <web-resource-name>Secured Rest App</web-resource-name>
        <url-pattern>/webapi/*</url-pattern>
    </web-resource-collection>
    <auth-constraint>
        <role-name>*</role-name>
    </auth-constraint>
</security-constraint>

<login-config>
    <auth-method>BASIC</auth-method>
    <realm-name>secured-rest-app.com</realm-name>
</login-config>

<security-role>
    <role-name>user</role-name>
</security-role>


第三步:

现在我们只需要在Tomcat中设置领域。缺省情况下,Tomcat使用领域名称UserDatabaseRealm。它基本上只是从xml文件读取。这可能不是生产中最期望的方式,但这是最容易使用的示例。有关领域的更多信息,请参见Realm Configuration HOW-TO。对于此特定领域,该文件已设置。我们只需要添加我们的用户。打开<tomcat-home>/conf/tomcat-users.xml并在<tomcat-users>中输入以下用户

<user password="secret" roles="user" username="peeskillet"/>


第四步:

现在我们可以对其进行测试。如果您已经在Netbeans上安装了Tomcat,那么我们要做的就是Run并选择服务器。这应该会自动打开浏览器到我们的index.jsp。该文件不受保护,因为它不在out安全约束的<url-pattern>/webapi/*</url-pattern>中。单击泽西资源链接,您将看到基本身份验证登录。分别输入peeskilletsecret作为用户名和密码。现在您可以访问资源。

步骤5:

以上所有内容都为我们设置了基本身份验证,但是由于所有基本身份验证只是对我们的用户名和密码进行base64编码,因此可以轻松解码,因此我们需要通过安全连接进行身份验证。

我们需要做的第一件事是为我们的服务器创建一个密钥库。我们将要在这里创建一个自签名证书,该证书只能在开发中完成。在生产中,您将需要从受信任的CA权威机构获得证书。

cd<tomcat-home>/conf并键入以下内容(全部一行)

keytool -genkey -alias localhost -keyalg RSA -keysize 1024
        -dname "CN=localhost"
        -keypass supersecret
        -keystore tomcat-keystore.jks
        -storepass supersecret


现在,您应该在tomcat-keystore.jks目录中看到文件conf。现在我们可以导出证书了。

keytool -export -alias localhost -rfc -keystore ./tomcat-keystore.jks > ./tomcat.cert


系统将提示您输入密码,键入supersecret。现在,您应该看到在tomcat.cert目录中创建的conf文件。将该文件复制到上面创建的应用程序的项目根目录中。

从命令行cd到项目根目录,tomcat.cert的位置并键入以下内容

keytool -import -alias tomcatCert -file ./tomcat.cert -keystore ./client-truststore.jks


系统将提示您输入信任库的密码。使用trustpass。您将需要输入两次。完成后,它将提示您信任证书,键入yes并输入。现在,您应该在项目根目录中看到一个client-truststore.jks文件。这就是我们将用于客户端应用程序的内容。

现在,我们只需要配置Tomcat以使用我们的密钥库进行连接。在<tomcat-home>/conf/server.xml<Service>元素内
。 (请注意,Tomcat 7可能有所不同)

<Connector port="8443" 
    protocol="org.apache.coyote.http11.Http11NioProtocol"
    maxThreads="150" 
    SSLEnabled="true" 
    scheme="https" 
    secure="true"
    keystoreFile="absolute/path/to/tomcat-keystore.jks"
    keystorePass="supersecret"
    clientAuth="false" 
    sslProtocol="TLS" />


最后,在我们的网络应用中,我们应该通过更改<security-constraint>来添加安全连接支持

<security-constraint>
    <web-resource-collection>
        <web-resource-name>Secured Rest App</web-resource-name>
        <url-pattern>/webapi/*</url-pattern>
    </web-resource-collection>
    <auth-constraint>
        <role-name>*</role-name>
    </auth-constraint>
    <user-data-constraint>
        <transport-guarantee>CONFIDENTIAL</transport-guarantee>
    </user-data-constraint>
</security-constraint>


现在我们准备使用我们的客户代码。

步骤6:

上面的代码使用Jersey 1客户端。我们正在使用Jersey 2,因此代码将略有不同。在应用程序中的任何位置,只需使用main方法创建一个类即可运行我们的客户端。这是我使用的:

import java.io.FileInputStream;
import java.security.KeyStore;
import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManagerFactory;
import javax.ws.rs.client.Client;
import javax.ws.rs.client.ClientBuilder;
import javax.ws.rs.core.Response;
import org.glassfish.jersey.client.authentication.HttpAuthenticationFeature;

public class SimpleClientApp {

    private static final String TRUSTSTORE_FILE = "client-truststore.jks";
    private static final String TRUSTSTORE_PASSWORD = "trustpass";
    private static final String APP_URL 
            = "https://localhost:8443/secured-rest-app/webapi/myresource";

    public static void main(String[] args) throws Exception {
        KeyStore truststore = KeyStore.getInstance("JKS");
        truststore.load(new FileInputStream(TRUSTSTORE_FILE), 
                                            TRUSTSTORE_PASSWORD.toCharArray());
        TrustManagerFactory tmf = TrustManagerFactory.getInstance("SunX509");
        tmf.init(truststore);
        SSLContext sslContext = SSLContext.getInstance("TLS");
        sslContext.init(null, tmf.getTrustManagers(), null);

        Client client = ClientBuilder.newBuilder()
                .sslContext(sslContext).build();
        client.register(HttpAuthenticationFeature.basic("peeskillet", "secret"));

        Response response = client.target(APP_URL).request().get();
        System.out.println(response.readEntity(String.class));

    }
}


您应该能够运行它,并且它应该打印出Got it!。大功告成!

由于这个特定问题是关于Jersey 1的,因此我只想提一下您可以轻松创建Jersey 1应用程序。从第一步开始,只需使用Maven原型的坐标


groupId:com.sun.jersey.archetypes
artifactId:jersey-quickstart-webapp
版本:1.18.1


在Netbeans中,只需遵循上述步骤,但是选择com.sun.jersey.archetypesjersey-quickstart-webapp版本。

使用Jersey 1,您可以使用原始答案中的代码,只需添加Basic Auth过滤器即可,就像OP在原始帖子中所做的那样,设置用户名和密码并更改url。

如果帖子中有任何错误,请告诉我。我还没有机会对此进行校对:-)

一些资源


Tomcat 8 SSL/TLS Configuration HOW-TO
Tomcat 8 Realm Configuration HOW-TO
Tomcat 7 Realm Configuration HOW-TO
Tomcat 7 SSL/TLS Configuration HOW-TO

关于java - 无法在Jersey中进行基本的HTTP身份验证,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/28471625/

相关文章:

java - 在 Google Appengine 上配置 https ://yourdomain. com

java - 如何保存checkedTextViews的文本

java - Java 中枚举类型初始化困惑

java - RabbitMQ 连接重置

java - 如何在 Java 中使用 Elasticsearch Ingest Attachment Processor 插件

actionscript-3 - 来自 Flex/AIR iPad 应用程序的双向 SSL

java - 如何区分 get 和 post call..?

c# - 返回 XML 而非 JSON 的 RESTful Web 服务

apache - 让我们在 AWS Lightsail 的 bitnami.conf 中加密 SSL 设置

java - 无法获得 Go Daddy ssl 证书来与 Spring Boot 一起使用