Accessing SSL secured web page from play applicati

2019-04-13 13:14发布

I am accessing SSL secured pages from play app. When the certificate of the page can't be trusted (because it has expired or is self signed or whatever other reason it might be) I would like to extract this information from my call and do some action accordingly. When I access such a page in the browser, it warns me that there is something wrong with the certificate. When I write something like this in play (the https page here has a bad certificate):

package controllers

import play.api.mvc.{Action, Controller}
import play.api.libs.ws.WS
import java.util.concurrent.TimeUnit

object CallHttpsController extends Controller{


  def callHttps() = Action {

    val url = "https://xceed.no-ip.org/"

    val response: play.api.libs.ws.Response = WS.url(url).get.await(3, TimeUnit.SECONDS).get

    response.status match {
      case OK => Ok("Got response: " + response.body)
      case _ => Ok("Something went wrong: "+response.status+" "+response.body)
    }
  }
}

The response status is OK, and I would expect it to throw an exception that there is something wrong with the certificate.

Is it possible in play to call such a page and get an exception when the certificate can't be trusted? Is it supported in play be default? Does anyone had similar problem / need?

Thanks in advance.

UPDATE #1: Thanks to for the tip with Ning. I have added dependency to the build script:

  lazy val appDependencies = Seq(
    "com.ning" % "async-http-client" % "1.7.5"
  )

The library itself is very helpful indeed. However to work with SSL as expected, apparently it needs to have SSL Context initialised. I have tried the following code:

def callHttps() = Action {

    try{

      val url = "https://xceed.no-ip.org/"

      val response: play.api.libs.ws.Response = WS.url(url).get.await(3, TimeUnit.SECONDS).get // => No problem here

      Console.println(WS.client.getConfig.getSSLContext) // => null

      val sslContext = SSLContext.getDefault;
      val playConfig = play.api.Play.maybeApplication.map(_.configuration)
      val asyncHttpConfig = new AsyncHttpClientConfig.Builder()
    .setConnectionTimeoutInMs(playConfig.flatMap(_.getMilliseconds("ws.timeout")).getOrElse(120000L).toInt)
    .setRequestTimeoutInMs(playConfig.flatMap(_.getMilliseconds("ws.timeout")).getOrElse(120000L).toInt)
    .setFollowRedirects(playConfig.flatMap(_.getBoolean("ws.followRedirects")).getOrElse(true))
    .setUseProxyProperties(playConfig.flatMap(_.getBoolean("ws.useProxyProperties")).getOrElse(true))
    .setSSLContext( sslContext )

      playConfig.flatMap(_.getString("ws.useragent")).map { useragent =>
        asyncHttpConfig.setUserAgent(useragent)
      }
      val innerClient = new AsyncHttpClient(asyncHttpConfig.build())

      val resp = innerClient.prepareGet(url).execute().get() // => exception
      Console.println("All OK")

      Ok("Got response: " + resp.getResponseBody)

    }
    catch {
      case e: Exception =>
        var cause = e.getCause
        while (cause != null) {
          Console.println("Caused by: \n" + e.getLocalizedMessage + "\n" + e.getStackTraceString)
          cause = cause.getCause
        }

        Ok("Got Exception: " + e.getLocalizedMessage + "\n" + e.getStackTraceString)
    }
  }

There is a client initialisation extracted from the WS source code, and the default SSL Context is passed to the client. The call then fails as expected. Will try to raise a ticket with play ... Cheers!

UPDATE #2: The ticket has been now resolved and will be a part of play 2.0.4 https://play.lighthouseapp.com/projects/82401/tickets/655-ws-call-wont-fail-upon-https-call-to-a-page-with-wrong-certificate

UPDATE #3: The default ssl context is now set by default. To turn that off in the application config file set ws.acceptAnyCertificate=true

See the WS.scala code for more information: https://github.com/playframework/Play20/blob/master/framework/src/play/src/main/scala/play/api/libs/ws/WS.scala

1条回答
不美不萌又怎样
2楼-- · 2019-04-13 13:30

In its play.api.libs.ws.WS class the Play! framework uses the Ning Async HTTP Client Library (version 1.7.0), which in turn uses the Netty network application framework (version 3.5). Netty is responsible for SSL verification, but its result is handled by the Async HTTP Client library.

It seems like SSL verification is only properly fixed in a recent commit of the Async HTTP Client library, which is only included in the 1.7.5 release. You could try forcing the newer version into your project (i.e. overriding the 1.7.0 version referenced by Play!), and maybe opening an issue so that the newer library version can be included in the next Play! release.

查看更多
登录 后发表回答