无法在泽西岛进行基本的 http 身份验证工作

Cannot make basic http authentication work in Jersey

我正在尝试使用 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

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

有什么想法吗?

由于 Basic Auth 本身缺乏安全性,因此通常通过 SSL 完成,如您在 URL 中的 https 架构中所见。使用 SSL,使用 certificates。 SSL 握手包括服务器发送其证书和客户端检查其信任库以查看证书是否受信任。该平台应该有一个它信任的证书颁发机构列表。例如,如果我们尝试访问

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

这将起作用,因为维基百科发送的证书是由系统中受信任的机构签署的。另一方面,如果来自服务器的证书不是由其中一个受信任的机构签署的,则 SSL 握手将失败。

如果是这种情况,则需要配置 Client 来处理 SSL 握手,这就是您收到异常的原因。您可以看到一些关于如何配置 Client 以与 https

一起工作的好答案 here

更新

The link you have provided is dead so I dont know what 'myTrustManager' and 'hostnameVerifier' is...can you share some info on how can I supply that?

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(有关编程方式,请参阅下面的 link)。然后将其导入到您的信任库中。这就是您的客户知道信任连接的方式。如果您相信服务器就是他们所说的那样,那么可以对连接进行加密。

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

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

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


更新 2

有关创建简单应用程序的说明,该应用程序通过 Tomcat 的安全 SSL 连接。使用上面的客户端代码来访问它。我将使用 Netbeans 8,但也会尝试包含以一般方式执行此操作的说明。我还将使用 Tomcat 8(配置可能与 Tomcat 7 略有不同。您应该查阅文档以了解任何差异)。我将使用 Maven,因此希望您能自如地使用它。

第 1 步:

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

File → New Project → Maven → Project from Archetype → Search "jersey-quickstart-webapp"; choose the one with groupId "org.glassfish.jersey.archetypes" → Next → Name the project "secured-rest-app" → Hopefully you can complete the rest. You should end up with a Maven app.

在支持 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>

第 3 步:

现在我们只需要在Tomcat中设置领域。默认情况下 Tomcat 使用域名 UserDatabaseRealm。它基本上只是从 xml 文件中读取。这可能不是生产中最理想的方式,但这是最容易使用的示例。有关领域的更多信息,请参阅 Realm Configuration HOW-TO。对于这个特定的领域,文件已经设置好了。我们只需要添加我们的用户。打开<tomcat-home>/conf/tomcat-users.xml然后在里面输入以下用户<tomcat-users>

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

第 4 步:

现在我们可以测试了。如果您已经在 Netbeans 上设置了 Tomcat,我们需要做的就是 Run 和 select 服务器。这应该会自动打开浏览器到我们的 index.jsp。该文件不安全,因为它不符合安全约束的 <url-pattern>/webapi/*</url-pattern>。单击 Jersey Resource link,您将看到 Basic Auth 登录。分别输入 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

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

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

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

从命令行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> 元素内 . (注Tomcat7可能有点不同)

<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" />

最后,在我们的 webapp 中,我们应该通过更改 <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,因此代码会略有不同。在应用程序的任何地方,只需创建一个 class 和一个 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 中,只需按照上述步骤操作即可,但是 select com.sun.jersey.archetypes 版本的 jersey-quickstart-webapp.

对于 Jersey 1,您可以使用原始答案中的代码,只需添加基本身份验证过滤器,就像 OP 在原始 post 中所做的那样,设置用户名和密码并更改 url当然。

post如有错误请告知。我还没有机会校对这篇文章:-)

一些资源