简体   繁体   English

Swift-NIO 安全 websocket 服务器

[英]Swift-NIO secured websocket server

I am trying to create websocket server and client in my iOS app, which i successfully managed to do with the help of sample implementation here.我正在尝试在我的 iOS 应用程序中创建 websocket 服务器和客户端,我在示例实现的帮助下成功地做到了。 ( https://github.com/apple/swift-nio/tree/master/Sources/NIOWebSocketServer ) - so current working situation is, i run the websocket server when app launches and then I load the client in a webview which can connect to it. ( https://github.com/apple/swift-nio/tree/master/Sources/NIOWebSocketServer ) - 所以当前的工作情况是,我在应用程序启动时运行 websocket 服务器,然后在可以连接的 webview 中加载客户端到它。

Now my problem is I want my server to secured websocket server (Basically connect to the websocket server from a HTTPS html page)现在我的问题是我希望我的服务器保护 websocket 服务器(基本上从 HTTPS html 页面连接到 websocket 服务器)

I am new to network programming and Swift-nio documentation is lacking to say the least.我是网络编程的新手,至少可以说缺乏 Swift-nio 文档。 As far as I understand I could use ( https://github.com/apple/swift-nio-transport-services )据我所知,我可以使用( https://github.com/apple/swift-nio-transport-services

I found this thread which is exactly what I need -https://github.com/apple/swift-nio-transport-services/issues/39 - I could disable the TLS authentication as I dont care in my usecase as long as I could get the websocket connected.我发现这个线程正是我需要的 -https://github.com/apple/swift-nio-transport-services/issues/39 - 我可以禁用 TLS 身份验证,因为我不在乎我的用例,只要我可以连接 websocket。

So my question is how to I extend my client ( https://github.com/apple/swift-nio/tree/master/Sources/NIOWebSocketClient ) and server ( https://github.com/apple/swift-nio/tree/master/Sources/NIOWebSocketServer ) to use swift-nio-transport-service.所以我的问题是如何扩展我的客户端( https://github.com/apple/swift-nio/tree/master/Sources/NIOWebSocketClient )和服务器( https://github.com/apple/swift-nio/ tree/master/Sources/NIOWebSocketServer ) 来使用 swift-nio-transport-service。

I could add the NIOSSLContext and stuff but I think I need to add the EventLoopGroup and new bootstrap methods.我可以添加NIOSSLContext和其他东西,但我想我需要添加EventLoopGroup和新的bootstrap方法。 I know the answers is right there.... but I just cannot seem to pinpoint it.我知道答案就在那里......但我似乎无法确定它。

Any pointer would be appreciated.任何指针将不胜感激。

Thanks.谢谢。

To translate a simple NIO Server to a NIOTransportServices one, you need to make the following changes:要将简单的NIO Server 转换为NIOTransportServices ,您需要进行以下更改:

  1. Add a dependency on NIOTransportServices to your server.向您的服务器添加对NIOTransportServices的依赖。
  2. Change MultiThreadedEventLoopGroup to NIOTSEventLoopGroup .MultiThreadedEventLoopGroup更改为NIOTSEventLoopGroup
  3. Change ClientBootstrap to NIOTSConnectionBootstrap .更改ClientBootstrapNIOTSConnectionBootstrap
  4. Change ServerBootstrap to NIOTSListenerBootstrap .更改ServerBootstrapNIOTSListenerBootstrap
  5. Build and run your code.构建并运行您的代码。

Some ChannelOption s don't work in NIOTransportServices , but most do: the easiest way to confirm that things are behaving properly is to quickly test the common flow.一些ChannelOption不能在NIOTransportServices工作,但大多数可以:确认事情正常运行的最简单方法是快速测试公共流程。

This doesn't add any extra functionality to your application, but it does give you the same functionality using the iOS APIs.这不会为您的应用程序添加任何额外的功能,但它确实为您提供了使用 iOS API 的相同功能。

To add TLS to either NIOTSConnectionBootstrap or NIOTSListenerBootstrap , you use the .tlsOptions function.要将 TLS 添加到NIOTSConnectionBootstrapNIOTSListenerBootstrap ,请使用.tlsOptions函数。 For example:例如:

NIOTSListenerBootstrap(group: group)
    .tlsOptions(myTLSOptions())

Configuring a NWProtocolTLS.Options is a somewhat tricky thing to do.配置NWProtocolTLS.Options是一件有点棘手的事情。 You need to obtain a SecIdentity , which requires interacting with the keychain.您需要获得一个SecIdentity ,这需要与钥匙串进行交互。 Quinn has discussed this somewhat here .奎因已经有所讨论过这个位置

Once you have a SecIdentity , you can use it like so:拥有SecIdentity ,您可以像这样使用它:

func myTLSOptions() -> NWProtocolTLS.Options {
    let options = NWProtocolTLS.Options()
    let yourSecIdentity = // you have to implement something here
    sec_protocol_options_set_local_identity(options.securityProtocolOptions, sec_identity_create(yourSecIdentity)
    return options
}

Once you have that code written, everything should go smoothly!一旦您编写了该代码,一切都会顺利进行!


As an extension, if you wanted to secure a NIO server on Linux, you can do so using swift-nio-ssl .作为扩展,如果您想保护 Linux 上的 NIO 服务器,您可以使用swift-nio-ssl 来实现 This has separate configuration as the keychain APIs are not available, and so you do a lot more loading of keys and certificates from files.这有单独的配置,因为钥匙串 API 不可用,因此您需要从文件中加载更多的密钥和证书。

I needed a secure websocket without using SecIdentity or NIOTransportServices , so based on @Lukasa's hint about swift-nio-ssl I cobbled together an example that appears to work correctly.我需要一个不使用SecIdentityNIOTransportServices的安全 websocket,因此基于@Lukasa 关于swift-nio-ssl的提示,我拼凑了一个似乎可以正常工作的示例。

I dunno if it's correct, but I'm putting it here in case someone else can benefit.我不知道它是否正确,但我把它放在这里以防其他人可以受益。 Error-handling and aborting when the try 's fail is left out for brevity.为简洁起见,省略了try失败时的错误处理和中止。

let configuration = TLSConfiguration.forServer(certificateChain: try! NIOSSLCertificate.fromPEMFile("/path/to/your/tlsCert.pem").map { .certificate($0) }, privateKey: .file("/path/to/your/tlsKey.pem"))
let sslContext = try! NIOSSLContext(configuration: configuration)
            
let upgradePipelineHandler: (Channel, HTTPRequestHead) -> EventLoopFuture<Void> = { channel, req in
                
    WebSocket.server(on: channel) { ws in

       ws.send("You have connected to WebSocket")

       ws.onText { ws, string in
           print("Received text: \(string)")
       }
                
       ws.onBinary { ws, buffer in
           // We don't accept any Binary data
       }
                
       ws.onClose.whenSuccess { value in
           print("onClose")
       }
   }
}

self.eventLoopGroup = MultiThreadedEventLoopGroup(numberOfThreads: 2)
let port: Int = 5759
let promise = self.eventLoopGroup!.next().makePromise(of: String.self)
            
_ = try? ServerBootstrap(group: self.eventLoopGroup!)
                
    // Specify backlog and enable SO_REUSEADDR for the server itself
    .serverChannelOption(ChannelOptions.backlog, value: 256)
    .serverChannelOption(ChannelOptions.socketOption(.so_reuseaddr), value: 1)
                
    .childChannelInitializer { channel in
                    
        let handler = NIOSSLServerHandler(context: sslContext)
        _ = channel.pipeline.addHandler(handler)
              
        let webSocket = NIOWebSocketServerUpgrader(
            shouldUpgrade: { channel, req in
                return channel.eventLoop.makeSucceededFuture([:])
            },
            upgradePipelineHandler: upgradePipelineHandler
        )
              
        return channel.pipeline.configureHTTPServerPipeline(
            withServerUpgrade: (
                upgraders: [webSocket],
                completionHandler: { ctx in
                     // complete
                })
        )
    }.bind(host: "0.0.0.0", port: port).wait()

_ = try! promise.futureResult.wait()
try! server.close(mode: .all).wait()

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM