如何使用 Kotlin 的 ktor 构建一个安全的 (wss://...) websocket 服务器?



我发现了一个不安全的ktor-websocket服务器(ws://…(的文档:

https://ktor.io/docs/creating-web-socket-chat.html#creating-聊天客户端

我找到了一个安全的ktor http服务器的文档(https://...)

https://github.com/ktorio/ktor-documentation/tree/main/codeSnippets/snippets/ssl-embedded-server

但我似乎找不到或不知道如何为一个安全的ktor websocket服务器提供服务(wss://...)

我宁愿不在它前面使用像nginx这样的SSL反向代理

编辑:这里的代码:

import io.ktor.application.*
import io.ktor.http.cio.websocket.*
import io.ktor.network.tls.certificates.*
import io.ktor.response.*
import io.ktor.routing.*
import io.ktor.server.engine.*
import io.ktor.server.netty.*
import io.ktor.websocket.*
import java.io.*
fun main() {
    val keyStoreFile = File("build/keystore.jks")
    val keystore = generateCertificate(
        file = keyStoreFile,
        keyAlias = "sampleAlias",
        keyPassword = "foobar",
        jksPassword = "foobar"
    )
    val environment = applicationEngineEnvironment {
        sslConnector(
            keyStore = keystore,
            keyAlias = "sampleAlias",
            keyStorePassword = { "foobar".toCharArray() },
            privateKeyPassword = { "foobar".toCharArray() }) {
            port = 8443
            keyStorePath = keyStoreFile
        }
        module(Application::module)
    }
    embeddedServer(Netty, environment).start(wait = true)
}
private fun Application.module() {
    install(WebSockets)
    routing {
        get("/") { // works at https://localhost:8443 in Firefox after approving cert
            call.respondText("This is https")
        }
        webSocket("/chat") { // fails at wss://localhost:8443/chat in Websocket js client with "Firefox can’t establish a connection to the server"
            send("This is wss")
        }
    }
}

问题是使用自签名证书。我尝试了Chrome,并将其设置为接受localhost上的自签名证书,这是有效的。Firebox提供的错误消息表明无法建立连接,而实际上它拒绝了证书。

事实上,使用ktor的安全websocket服务器确实可以像@AleksiTierman建议的那样工作。它的配置方式与https服务器相同。

我已经用poster测试过了,效果很好
您不能在浏览器中使用ws://或wss://协议,而是使用poster或其他websocket客户端。

Application.kt:

import com.example.plugins.configureRouting
import com.example.plugins.configureSerialization
import com.example.plugins.configureSockets
import io.ktor.network.tls.certificates.*
import io.ktor.server.engine.*
import io.ktor.server.netty.*
import org.slf4j.LoggerFactory
import java.io.File

fun main() {
    val keyStoreFile = File("build/keystore.jks")
    val keystore = generateCertificate(
        file = keyStoreFile,
        keyAlias = "sampleAlias",
        keyPassword = "foobar",
        jksPassword = "foobar"
    )
    val environment = applicationEngineEnvironment {
        log = LoggerFactory.getLogger("ktor.application")
        
        // no ssl, ws://127.0.0.1:6751
        connector {
            host = "127.0.0.1"
            port = 6751
        }
        
        // with ssl, wss://127.0.0.1:6752
        sslConnector(
            keyStore = keystore,
            keyAlias = "sampleAlias",
            keyStorePassword = { "foobar".toCharArray() },
            privateKeyPassword = { "foobar".toCharArray() }) {
            keyStorePath = keyStoreFile
            host = "127.0.0.1"
            port = 6752
        }
        this.module {
            configureSockets()
            configureRouting()
        }
    }
    embeddedServer(Netty, environment).start(wait = true)
/*
    embeddedServer(Netty, port = 6752, host = "127.0.0.1") {
        configureSockets()
        configureRouting()
    }.start(wait = true)
 */
}

Sockets.kt:

import io.ktor.serialization.kotlinx.*
import io.ktor.server.application.*
import io.ktor.server.routing.*
import io.ktor.server.websocket.*
import io.ktor.websocket.*
import kotlinx.coroutines.channels.ClosedReceiveChannelException
import kotlinx.serialization.json.Json
import java.time.Duration

fun Application.configureSockets() {
    routing {
        this@configureSockets.install(WebSockets) {
            contentConverter = KotlinxWebsocketSerializationConverter(Json)
            pingPeriod = Duration.ofSeconds(3)
            timeout = Duration.ofSeconds(5)
            maxFrameSize = Long.MAX_VALUE
            masking = false
        }
        webSocket("/echo") {
            println("onConnect")
            try {
                for (frame in incoming) {
                    val text = (frame as Frame.Text).readText()
                    println("onMessage")
                    send(Frame.Text(text))
                }
            } catch (e: ClosedReceiveChannelException) {
                e.printStackTrace()
                println("onClose ${closeReason.await()}")
            } catch (e: Throwable) {
                e.printStackTrace()
                println("onError ${closeReason.await()}")
            }
        }
    }
}

相关内容

  • 没有找到相关文章

最新更新