如何使用带有SSL证书的Akka发送HTTP请求

时间:2017-11-30 06:37:12

标签: scala ssl configuration akka akka-http

在从Scala发出HTTP请求时,我在配置Akka HTTP以使用特定的SSL证书时遇到了一些麻烦。我可以在没有打嗝的情况下制作非基于SSL的请求,但似乎无法找到有关如何配置Akka以与一组证书一起使用的正确示例。

可悲的是,Akka HTTP网站上的文档提供了有关服务器端SSL配置的详细信息,但没有给出客户端配置的具体示例。

非常感谢任何此类用法的例子。

1 个答案:

答案 0 :(得分:0)

我在我的一个小项目中完成了这项工作,我认为即使问题很老,也可能对某些人有用。您必须创建您的 keystore.pkcs12 文件。我的 build.sbt 我正在使用:

val akkaVersion = "2.6.10"
 val akkaHttpVersion = "10.2.2"
  "com.typesafe.akka" %% "akka-actor" % akkaVersion,
  "com.typesafe.akka" %% "akka-http" % akkaHttpVersion,
  "com.typesafe.akka" %% "akka-http-spray-json" % akkaHttpVersion,
  "com.typesafe.akka" %% "akka-http-testkit" % akkaHttpVersion,
  "com.typesafe.akka" %% "akka-http2-support" % akkaHttpVersion

我有 HttpsServerContext 对象,我可以在我的项目中的任何 Akka-http api 中使用它。我通过调用 enableHttps(HttpsServerContext.httpsConnectionContext) 使用。

object HttpsServerContext {
  // Step 1 - key store
  val ks: KeyStore = KeyStore.getInstance("PKCS12")
  val keystoreFile: InputStream = getClass.getClassLoader.getResourceAsStream("certs/keystore.pkcs12")
  // alternative: new FileInputStream(new File("src/main/resources/certs/keystore.pkcs12"))
  val password = "akka-https".toCharArray // TODO: fetch the password from a secure place
  ks.load(keystoreFile, password)

  // Step 2 - initialize a key manager
  val keyManagerFactory = KeyManagerFactory.getInstance("SunX509") // PKI public key infrastructure
  keyManagerFactory.init(ks, password)

  // Step 3 - initialize a trust manager
  val trustmanagerFactory = TrustManagerFactory.getInstance("SunX509")
  trustmanagerFactory.init(ks)

  // Step 4 - initialize a SSL context
  val sslContext: SSLContext = SSLContext.getInstance("TLS")
  sslContext.init(keyManagerFactory.getKeyManagers, trustmanagerFactory.getTrustManagers, new SecureRandom())

  // Step 5 - return the HTTPS connection context
  val httpsConnectionContext: HttpsConnectionContext = ConnectionContext.httpsServer(sslContext)
}

然后我有一个异步请求处理程序

implicit val system = ActorSystem("HttpsRestApi")

import system.dispatcher
    val asyncRequestHandler: HttpRequest => Future[HttpResponse] = {
      case HttpRequest(HttpMethods.GET, Uri.Path("/home"), headers, entity, protocol) =>
        Future(HttpResponse(
          StatusCodes.OK, // HTTP 200
          entity = HttpEntity(
            ContentTypes.`text/html(UTF-8)`,
            """
              |<html>
              | <body>
              |  Async Hello Akka HTTPS
              | </body>
              |</html>
              |""".stripMargin)
        ))
      case HttpRequest(HttpMethods.GET, Uri.Path("/redirect"), headers, entity, protocol) =>
        Future(HttpResponse(
          StatusCodes.Found,
          headers = List(Location("http://www.google.com"))
        ))
      case request: HttpRequest =>
        request.discardEntityBytes()
        Future(HttpResponse(
          StatusCodes.NotFound, // HTTP 404
          entity = HttpEntity(
            ContentTypes.`text/html(UTF-8)`,
            """
              |<html>
              | <body>
              |  OOPS! async page not found =(<br>try https://localhost:8553/home
              | </body>
              |</html>
              |""".stripMargin)
        ))
    }
    val httpsBindingAsync = Http()
      .newServerAt("localhost", 8443)
      .enableHttps(HttpsServerContext.httpsConnectionContext)
      .bind(asyncRequestHandler)