如何让 Play Framework WS 使用我的 SSLContext

How to make Play Framework WS use my SSLContext

我正在使用 Play 2.3.7 和 Scala 2.11.4,Java 7. 我想使用 Play WS 连接到 HTTPS 端点,这需要客户端提供其证书。为此,我创建了自己的 SSLContext:

  val sslContext = {
    val keyStore = KeyStore.getInstance("pkcs12")
    keyStore.load(new FileInputStream(clientKey), clientKeyPass.to[Array])
    val kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm)
    kmf.init(keyStore, clientKeyPass.to[Array])

    val trustStore = KeyStore.getInstance("jks")
    trustStore.load(new FileInputStream(trustStoreFile), trustStorePass.to[Array])
    val tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm)
    tmf.init(trustStore)

    val ctx = SSLContext.getInstance("TLSv1.2")
    ctx.init(kmf.getKeyManagers, tmf.getTrustManagers, new SecureRandom())
    ctx
  }

我知道,SSLContext 是有效的,因为我可以成功地将它与 URLConnection 一起使用:

  def urlConnection = Action {
    val conn = new URL(url).openConnection()
    conn.asInstanceOf[HttpsURLConnection].setSSLSocketFactory(sslContext.getSocketFactory)
    conn.connect()
    Ok(scala.io.Source.fromInputStream(conn.getInputStream).getLines().mkString("\n"))
  }

但是当我尝试以下两种方法之一时,我得到 java.nio.channels.ClosedChannelException。

  def ning = Action.async {
    val builder = new AsyncHttpClientConfig.Builder()
    builder.setSSLContext(sslContext)
    val client = new NingWSClient(builder.build())
    client.url(url).get() map { _ => Ok("ok") }
  }

  def asyncHttpClient = Action {
    val builder = new AsyncHttpClientConfig.Builder()
    builder.setSSLContext(sslContext)
    val httpClient = new AsyncHttpClient(builder.build())
    httpClient.prepareGet(url).execute().get(10, TimeUnit.SECONDS)
    Ok("ok")
  }

当我听从 Will Sargent 的建议并将 NingAsyncHttpClientConfigBuilder 与解析的配置一起使用时,我也遇到了同样的异常(请注意,该配置引用完全相同的值,手工制作的 sslContext 确实如此)。

  def ningFromConfig = Action.async {
    val config = play.api.Configuration(ConfigFactory.parseString(
      s"""
          |ws.ssl {
          |  keyManager = {
          |    stores = [
          |      { type: "PKCS12", path: "$clientKey", password: "$clientKeyPass" }
          |    ]
          |  }
          |  trustManager = {
          |    stores = [
          |      { type: "JKS", path: "$trustStoreFile", password: "$trustStorePass" },
          |    ]
          |  }
          |}
          |# Without this one I get InvalidAlgorithmParameterException: the trustAnchors parameter must be non-empty
          |ws.ssl.disabledKeyAlgorithms="RSA keySize < 1024"
       """.stripMargin))
    val parser = new DefaultWSConfigParser(config, play.api.Play.application.classloader)
    val builder = new NingAsyncHttpClientConfigBuilder(parser.parse())
    val client = new NingWSClient(builder.build())
    client.url(url).get() map { _ => Ok("ok") }
  }

如何让它与 Play WS 一起使用?

您应该使用 https://www.playframework.com/documentation/2.3.x/KeyStores and https://www.playframework.com/documentation/2.3.x/ExampleSSLConfig 中定义的来自 application.conf 的客户端证书——如果您需要使用自定义配置,您应该直接使用解析器:

https://github.com/playframework/playframework/blob/2.3.x/framework/src/play-ws/src/test/scala/play/api/libs/ws/DefaultWSConfigParserSpec.scala#L14

创建配置,然后是 Ning 构建器:

https://github.com/playframework/playframework/blob/2.3.x/framework/src/play-ws/src/test/scala/play/api/libs/ws/ning/NingAsyncHttpClientConfigBuilderSpec.scala#L33

这将为您提供可以传入的 AHCConfig 对象。在 https://www.playframework.com/documentation/2.3.x/ScalaWS 的 "Using WSClient" 部分中有示例。