
























这篇笔记用于梳理 Android 使用 OkHttp WebSocket 时,wss://、TLS 证书校验、SSLContext、TrustManager、token、cookie、session 之间的关系。
核心结论:
TLS/SSL 解决:连接是否安全,服务器是否可信。
鉴权/会话解决:客户端是谁,客户端有没有权限。
对 wss:// 来说,完整顺序是:
TCP 连接
-> TLS 握手与证书校验
-> HTTP Upgrade 握手
-> WebSocket 帧通信
| 分类 | 解决的问题 | 常见概念 | 出现阶段 |
|---|---|---|---|
| TLS/SSL | 通道加密、数据防篡改、服务器身份可信 | SSLContext、SSLSocketFactory、TrustManager、证书、CA |
HTTP Upgrade 之前 |
| 鉴权/会话 | 客户端是谁、有没有权限 | token、cookie、session、Authorization |
HTTP Upgrade 握手阶段,或 WebSocket 建立后的业务消息阶段 |
一句话理解:
TLS 证明“我连到的是可信服务器”。
鉴权证明“我是合法客户端”。
wss:// 只能说明 WebSocket 跑在 TLS 加密通道里,不代表用户已经登录,也不代表客户端已经有业务权限。
TLS 是 TCP 之上的安全层,主要提供三件事:
wss:// 可以理解为:
WebSocket over TLS
它和 https:// 一样,都需要先完成 TLS 握手,再在加密通道里传输应用层数据。
SSLContext 是 TLS 配置入口。
它通常负责整合:
TrustManager:决定信任哪些服务器证书。KeyManager:决定客户端使用什么客户端证书,主要用于双向 TLS,也叫 mTLS。TLS 实例。典型关系:
TrustManager / KeyManager
-> SSLContext.init(...)
-> SSLContext.socketFactory
-> OkHttpClient
SSLSocketFactory 是创建 TLS socket 的工厂。
OkHttp 在建立 https:// 或 wss:// 连接时,会用它创建底层加密 socket。
通常来源:
val sslSocketFactory = sslContext.socketFactory
OkHttp 配置时通常还需要同时传入对应的 X509TrustManager:
OkHttpClient.Builder()
.sslSocketFactory(sslSocketFactory, x509TrustManager)
.build()
TrustManager 决定“是否信任对方证书”。
在 WebSocket wss:// 场景里,它主要验证服务端发来的证书链,例如:
X509TrustManager 是 HTTPS/WSS 常见的 TrustManager 类型,用来验证 X.509 证书链。
生产环境不要使用“信任所有证书”的实现,例如:
override fun checkServerTrusted(chain: Array<X509Certificate>, authType: String) {
// 什么都不做,等于信任所有证书。生产环境禁止这样写。
}
这种写法会让 TLS 失去服务器身份校验能力,容易受到中间人攻击。
TrustManagerFactory 用来根据某个信任库生成 TrustManager。
常见初始化方式:
| 写法 | 含义 |
|---|---|
tmf.init(null as KeyStore?) |
使用系统默认信任库 |
tmf.init(keyStore) |
使用自定义 KeyStore 作为信任库 |
如果服务器使用的是公网 CA 签发的合法证书,通常不需要自定义 SSLContext。
如果服务器使用的是自签证书、公司内网 CA、测试环境 CA,就需要把对应 CA 或证书放进 KeyStore,再通过 TrustManagerFactory 生成 X509TrustManager。
KeyStore 是证书和密钥的容器。
常见用途有两种:
| 用途 | 放什么 | 提供给谁 |
|---|---|---|
| 信任库 trust store | CA 证书或服务端证书 | TrustManagerFactory |
| 客户端证书库 key store | 客户端私钥和客户端证书 | KeyManagerFactory |
普通 HTTPS/WSS 通常只需要服务器证书校验,也就是使用 TrustManager。
双向 TLS,也叫 mTLS,才需要客户端证书和 KeyManager。
HostnameVerifier 负责校验证书里的域名是否匹配当前访问的域名。
它和 TrustManager 的分工不同:
| 组件 | 负责什么 |
|---|---|
TrustManager |
证书链是否可信 |
HostnameVerifier |
证书是否签发给当前访问的域名 |
例如访问:
wss://api.example.com/socket
即使证书链本身可信,也必须确认这张证书确实属于 api.example.com。
生产环境不要这样写:
hostnameVerifier { _, _ -> true }
这会跳过域名校验,同样会削弱 TLS 安全性。
以 OkHttp 连接 wss:// 为例,流程如下:
1. 建立 TCP 连接
2. 开始 TLS 握手
3. 服务器发送证书链
4. X509TrustManager 校验证书链可信性
5. HostnameVerifier 校验证书域名匹配
6. TLS 握手成功,得到加密通道
7. 在加密通道内发送 HTTP Upgrade 请求
8. 服务端返回 101 Switching Protocols
9. WebSocket 建立成功,开始收发 WebSocket 帧
也就是:
先保证通道安全,再进行 WebSocket 升级,再进入业务通信。
不需要自定义的情况:
需要自定义的情况:
正确方向:
CA 证书或服务端证书
-> KeyStore
-> TrustManagerFactory
-> X509TrustManager
-> SSLContext
-> SSLSocketFactory
-> OkHttpClient
下面代码只用于理解名词关系,不是完整生产封装。
import java.security.KeyStore
import javax.net.ssl.SSLContext
import javax.net.ssl.TrustManagerFactory
import javax.net.ssl.X509TrustManager
val keyStore: KeyStore = KeyStore.getInstance(KeyStore.getDefaultType()).apply {
load(null, null)
// 示例:把自定义 CA 证书放入 KeyStore。
// setCertificateEntry("custom_ca", caCertificate)
}
val trustManagerFactory = TrustManagerFactory.getInstance(
TrustManagerFactory.getDefaultAlgorithm()
).apply {
init(keyStore)
}
val trustManagers = trustManagerFactory.trustManagers
val x509TrustManager = trustManagers
.filterIsInstance<X509TrustManager>()
.first()
val sslContext = SSLContext.getInstance("TLS").apply {
init(null, arrayOf(x509TrustManager), null)
}
val okHttpClient = OkHttpClient.Builder()
.sslSocketFactory(sslContext.socketFactory, x509TrustManager)
.build()
如果使用系统默认 CA,一般不需要手动配置上面这些,OkHttp 和 Android 系统会处理默认 TLS 校验。
TLS 只解决连接安全,不解决业务登录态。
WebSocket 的鉴权通常发生在 HTTP Upgrade 握手阶段,因为 WebSocket 连接最开始就是一个 HTTP 请求。
token 是客户端持有的访问凭证。
常见传递方式:
Authorization: Bearer <token>
常见类型:
| 类型 | 特点 |
|---|---|
| 随机字符串 token | 服务端存储 token 并校验 |
| JWT | token 自包含 claims,并通过签名防篡改 |
token 更适合 App、前后端分离、微服务和无状态扩展场景。
需要注意:
cookie 是服务端通过 Set-Cookie 下发,客户端后续请求自动携带的小段键值信息。
常见形式:
Set-Cookie: JSESSIONID=abc123; Path=/; Secure; HttpOnly
后续请求携带:
Cookie: JSESSIONID=abc123
浏览器会自动管理 cookie。
Android App 使用 OkHttp 时,如果希望自动保存和携带 cookie,需要配置 CookieJar。
session 通常指服务端保存的一份会话状态。
传统模式是:
客户端 cookie 里保存 sessionId
服务端根据 sessionId 查询 session
服务端判断用户是谁、是否已登录、有没有权限
session 的状态在服务端。
服务端横向扩容时,需要考虑:
这是 App 中最常见的方式。
流程:
1. App 调登录接口,获得 access token
2. 创建 WebSocket Request
3. 在 HTTP Upgrade 握手 header 中携带 Authorization
4. 服务端校验 token
5. 校验通过,返回 101 Switching Protocols
6. 校验失败,返回 401/403 或直接拒绝升级
Kotlin 示例:
val request = Request.Builder()
.url("wss://api.example.com/socket")
.addHeader("Authorization", "Bearer $token")
.build()
val webSocket = okHttpClient.newWebSocket(request, webSocketListener)
token 过期时的常见处理:
服务端关闭 WebSocket
-> 客户端收到 onClosing/onClosed/onFailure
-> 客户端刷新 token
-> 使用新 token 重新 newWebSocket
这是传统 Web 登录态模式。
流程:
1. 登录接口返回 Set-Cookie
2. OkHttp CookieJar 保存 cookie
3. WebSocket 握手请求携带 Cookie
4. 服务端根据 sessionId 查询 session
5. session 有效则允许升级,失效则拒绝升级
示意代码:
val okHttpClient = OkHttpClient.Builder()
.cookieJar(myCookieJar)
.build()
val request = Request.Builder()
.url("wss://api.example.com/socket")
.build()
okHttpClient.newWebSocket(request, webSocketListener)
如果没有配置 CookieJar,OkHttp 默认不会帮你做持久化 cookie 管理。
有些业务协议会选择 WebSocket 建立后,再发送第一条 auth 消息。
流程:
1. WebSocket 握手允许升级
2. onOpen 回调触发
3. 客户端发送 auth 消息,例如 {"type":"auth","token":"..."}
4. 服务端校验 auth 消息
5. 校验成功后才允许订阅、推送或收发业务数据
这种方式属于业务协议设计,不是 WebSocket 标准强制要求。
它的特点:
一个典型的 wss:// token 鉴权流程如下:
App 已拿到 token
|
v
newWebSocket(request with Authorization header)
|
v
建立 TCP 连接
|
v
TLS 握手,校验证书和域名
|
v
TLS 成功,得到加密通道
|
v
发送 HTTP Upgrade 请求,header 中带 token
|
v
服务端校验 token
|
v
返回 101,WebSocket 建立成功
|
v
在 TLS 加密通道内收发 WebSocket 帧
关键点:
wss:// 不等于已登录,它只代表通道被 TLS 保护。| 误区 | 正确认识 |
|---|---|
wss:// 就等于已经鉴权 |
错,wss:// 只代表 TLS 加密通道 |
| token 可以替代 TLS | 错,token 证明身份,TLS 保护传输 |
| TLS 可以替代 token | 错,TLS 通常只认证服务器,不认证具体用户 |
TrustManager 会校验登录态 |
错,它只负责证书链信任 |
HostnameVerifier 和 TrustManager 是一回事 |
错,一个校验证书链,一个校验域名 |
| 生产环境可以信任所有证书 | 错,这会破坏 TLS 安全性 |
| WebSocket 建立后可以随时改握手 header | 错,header 只在握手请求中发送 |
TLS 证书相关:
KeyStore
-> TrustManagerFactory
-> X509TrustManager
-> SSLContext.init(...)
-> SSLSocketFactory
-> OkHttpClient.Builder.sslSocketFactory(...)
-> HTTPS/WSS 连接
域名校验相关:
访问域名
-> 服务端证书 SAN/CN
-> HostnameVerifier
-> 判断证书是否匹配当前域名
token 鉴权相关:
登录接口
-> access token
-> Authorization: Bearer <token>
-> WebSocket HTTP Upgrade
-> 服务端校验
-> 101 或 401/403
cookie/session 鉴权相关:
登录接口
-> Set-Cookie: sessionId=...
-> OkHttp CookieJar 保存 cookie
-> WebSocket HTTP Upgrade 携带 Cookie
-> 服务端查 session
-> 101 或 401/403
TLS/SSL 负责把路修安全,证书负责确认服务器是真的。
token/cookie/session 负责确认客户端是谁,有没有权限。
wss:// 的顺序是:先 TLS,再 HTTP Upgrade,最后 WebSocket 消息。
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。