Android + Kotlin + OkHttp WebSocket 相关概念与使用流程笔记(TLS/证书 + 鉴权/会话)
1. 两大块问题分别解决什么
- TLS/SSL 相关(SSLContext、SSLSocketFactory、TrustManager 等)
- 解决:建立加密通道、防篡改、校验“服务器身份可信”
- 对应协议:HTTPS / WSS(WebSocket over TLS)
- 鉴权/会话相关(token、cookie、session)
- 解决:校验“客户端是谁、有没有权限”
- 主要发生在:WebSocket 的 HTTP Upgrade 握手阶段(因为握手本质是 HTTP)
2. TLS/SSL 相关概念
2.1 TLS/SSL 是什么
- TLS 是位于 TCP 之上的安全层:加密 + 完整性校验 + 身份认证(主要认证服务器)
- wss:// = 在 TLS 通道里跑 WebSocket(等价于 https:// 的安全要求)
2.2 SSLContext
- 概念:TLS 协议栈的“配置入口/上下文”
- 作用:把信任规则(TrustManager)、客户端证书(KeyManager,可选)等初始化进去
- 结果:可以从它拿到用于建连的 SSLSocketFactory
- 典型关系:SSLContext.init(...) -> sslContext.socketFactory
2.3 SSLSocketFactory
- 概念:创建 TLS Socket 的工厂
- 作用:OkHttp 建立 https/wss 连接时用于创建底层加密 socket
- 通常来源:sslContext.socketFactory
2.4 TrustManager
- 概念:TLS 握手过程中负责“信不信对方证书”的组件
- 作用:验证服务端发来的证书链是否可信(是否能链到受信任根、是否过期、用途等)
2.5 X509TrustManager
- 概念:TrustManager 的常用实现,专门验证 X.509 证书链(HTTPS/WSS 常见体系)
- 注意:所谓“信任所有证书”的实现是高危做法,生产环境不应使用
2.6 TrustManagerFactory
- 概念:根据一份信任库(trust store)生成 TrustManager 的工厂
- 作用:你想用系统默认 CA,或额外信任公司内网 CA/自签证书,都会经过它
- 常见初始化方式:
- tmf.init(null):使用系统默认信任库
- tmf.init(keyStore):使用你自定义的 KeyStore 作为信任库
2.7 KeyStore(与上面强相关)
- 概念:证书/密钥的容器
- 两种典型用途:
- 作为“信任库”:放 CA 证书,提供给 TrustManagerFactory 生成 X509TrustManager
- 作为“客户端证书库”:放客户端私钥+证书,提供给 KeyManagerFactory(用于双向 TLS / mTLS)
2.8 HostnameVerifier(非常关键)
- 概念:主机名校验器,校验证书里的域名是否匹配你访问的域名
- 与 TrustManager 的分工:
- TrustManager:证书链可信不可信
- HostnameVerifier:证书是不是“发给 api.example.com 的”
3. TLS 建连流程(以 wss:// 为例,OkHttp/Android 视角)
- 建立 TCP 连接
- 开始 TLS 握手(协商 TLS 版本/加密套件)
- 服务器发送证书链(server certificate chain)
- 校验阶段:
- X509TrustManager 校验证书链可信性(是否链到受信 CA、是否过期等)
- HostnameVerifier 校验域名匹配(证书是否对应该域名)
- TLS 握手成功后,得到加密通道
- 在加密通道内发送 HTTP 请求进行 WebSocket Upgrade
- Upgrade 成功后进入 WebSocket 帧通信(所有帧都在 TLS 加密通道内传输)
什么时候需要自定义 SSLContext/TrustManager?
- 连接自签证书、公司内网 CA、或证书链不被系统信任的服务时
- 正确方向:把 CA/证书放进 KeyStore -> TrustManagerFactory 生成 X509TrustManager -> 配到 OkHttp
4. 鉴权/会话概念(token / cookie / session)
4.1 Cookie
- 概念:服务端通过 Set-Cookie 下发、客户端保存并在后续请求中携带的小段键值信息
- 常见用途:承载 sessionId(例如 JSESSIONID)
- 特点:
- 浏览器会自动管理;App 需要 CookieJar 才能自动持久化/携带
- 有域、路径、过期、Secure、HttpOnly 等属性
4.2 Session
- 概念:通常指服务端保存的一份“会话状态/登录态”
- 典型模式:服务端存 session,客户端用 cookie 带 sessionId 来索引 session
- 特点:状态在服务端,扩容时要考虑共享(如 Redis)或粘性会话
4.3 Token
- 概念:客户端持有的访问凭证字符串,服务端用它验证身份与权限
- 常见传递方式:Authorization: Bearer
- 常见形式:
- 随机字符串 token:服务端存储并校验
- JWT:token 自包含 claims 并签名,服务端可不存储(但常需要黑名单/刷新策略)
- 特点:更易无状态化与水平扩展;需要处理过期与刷新(refresh token)
5. 鉴权在 WebSocket 中的使用流程(因为握手是 HTTP)
5.1 使用 Token 鉴权(最常见)
- App 通过登录接口获得 token
- 发起 WebSocket 连接时,在握手 HTTP Header 中携带:
- Authorization: Bearer
- Authorization: Bearer
- 服务端校验 token:
- 通过:返回 101 Switching Protocols,升级成功
- 失败:返回 401/403 或拒绝升级
- token 过期处理常见策略:
- 服务端断开连接(close code / reason)
- 客户端刷新 token 后重新 newWebSocket 重连
5.2 使用 Cookie + Session 鉴权(传统 Web 模式)
- 登录响应中服务端下发 Set-Cookie: sessionId=...
- 客户端保存 cookie(OkHttp 需要 CookieJar 才能自动带 cookie)
- WebSocket 握手时 HTTP 请求会携带 Cookie: sessionId=...
- 服务端通过 sessionId 查 session 决定是否允许升级
5.3 连接建立后再发送“鉴权消息”(业务协议选择)
- 握手允许匿名升级(或只做弱校验)
- WebSocket 建立后第一条消息发送 auth 包(例如 token)
- 服务端确认后才允许订阅/下发数据
说明:这是业务协议做法,不是 WebSocket 标准强制
6. 记忆主线
- TLS/SSL(SSLContext / SSLSocketFactory / TrustManager):解决“通道安全 + 服务器可信”
- token/cookie/session:解决“你是谁 + 你能做什么”
- 对 wss:先 TLS 成功 -> 再 HTTP Upgrade -> 然后才是 WebSocket 消息帧
7. 名词关系速查(从配置到连接)
- TrustManagerFactory -> 生成 X509TrustManager(信任规则)
- SSLContext.init(trustManagers, keyManagers?) -> 得到 SSLContext
- SSLContext.socketFactory -> SSLSocketFactory(用来创建 TLS socket)
- HostnameVerifier -> 校验域名匹配
- OkHttp 使用上述组件 -> 建立 HTTPS/WSS -> 发送 WebSocket Upgrade 握手
// 仅示意名词关系(非完整可运行代码)
val sslContext = javax.net.ssl.SSLContext.getInstance("TLS")
// sslContext.init(keyManagers, trustManagers, SecureRandom) 之后:
val sslSocketFactory = sslContext.socketFactory
// OkHttpClient.Builder().sslSocketFactory(sslSocketFactory, x509TrustManager)
