惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

GbyAI
GbyAI
小众软件
小众软件
The Last Watchdog
The Last Watchdog
Latest news
Latest news
P
Palo Alto Networks Blog
C
Cisco Blogs
C
Cyber Attacks, Cyber Crime and Cyber Security
L
Lohrmann on Cybersecurity
P
Privacy International News Feed
NISL@THU
NISL@THU
T
Threat Research - Cisco Blogs
T
Threatpost
T
The Exploit Database - CXSecurity.com
S
Schneier on Security
Spread Privacy
Spread Privacy
T
Tor Project blog
Cyberwarzone
Cyberwarzone
C
Cybersecurity and Infrastructure Security Agency CISA
AWS News Blog
AWS News Blog
S
Securelist
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
Simon Willison's Weblog
Simon Willison's Weblog
P
Privacy & Cybersecurity Law Blog
Hugging Face - Blog
Hugging Face - Blog
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
C
CERT Recently Published Vulnerability Notes
V
Visual Studio Blog
酷 壳 – CoolShell
酷 壳 – CoolShell
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
月光博客
月光博客
博客园 - 司徒正美
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
罗磊的独立博客
A
Arctic Wolf
腾讯CDC
WordPress大学
WordPress大学
IT之家
IT之家
Last Week in AI
Last Week in AI
博客园 - 聂微东
P
Proofpoint News Feed
Y
Y Combinator Blog
Apple Machine Learning Research
Apple Machine Learning Research
Hacker News: Ask HN
Hacker News: Ask HN
Help Net Security
Help Net Security
Blog — PlanetScale
Blog — PlanetScale
爱范儿
爱范儿
美团技术团队
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
T
Tailwind CSS Blog

博客园 - y丶innocence

临时111 RecyclerView 数据多时无法滑动:ConstraintLayout 约束高度修复笔记 Android Kotlin OkHttp3 WebSocket 长连接与 Gson 数据解析系统笔记 AI对话导出markdown格式流程 代理转发 分享文件 面向全球的app的excel导出和kotlin IO原理 安卓导出笔记(未整理) java&kotlin listener 0.7 动画 0.4 View 工作流程 0.3 view 滑动冲突 13. Jetpack 0. 安卓开发艺术探索参考资料 12. Material Design 7. 持久化技术 5. Fragment java 基础 4. UI 开发 3. Activity 2.2 Kotlin 面向对象 2.3 Kotlin高级 2.1 Kotlin基础 1. Android简介 [OpenJudge] 反正切函数的应用 (枚举)(数学) [OpenJudge] 摘花生 (模拟)
Android + Kotlin + OkHttp WebSocket 相关概念与使用流程笔记(TLS/证书 + 鉴权/会话)
y丶innocence · 2026-04-28 · via 博客园 - y丶innocence

Android Kotlin OkHttp WebSocket:TLS/证书与鉴权/会话笔记

这篇笔记用于梳理 Android 使用 OkHttp WebSocket 时,wss://、TLS 证书校验、SSLContextTrustManager、token、cookie、session 之间的关系。

核心结论:

TLS/SSL 解决:连接是否安全,服务器是否可信。
鉴权/会话解决:客户端是谁,客户端有没有权限。

wss:// 来说,完整顺序是:

TCP 连接
  -> TLS 握手与证书校验
  -> HTTP Upgrade 握手
  -> WebSocket 帧通信

1. 两类问题不要混在一起

分类 解决的问题 常见概念 出现阶段
TLS/SSL 通道加密、数据防篡改、服务器身份可信 SSLContextSSLSocketFactoryTrustManager、证书、CA HTTP Upgrade 之前
鉴权/会话 客户端是谁、有没有权限 token、cookie、session、Authorization HTTP Upgrade 握手阶段,或 WebSocket 建立后的业务消息阶段

一句话理解:

TLS 证明“我连到的是可信服务器”。
鉴权证明“我是合法客户端”。

wss:// 只能说明 WebSocket 跑在 TLS 加密通道里,不代表用户已经登录,也不代表客户端已经有业务权限。

2. TLS/SSL 相关概念

2.1 TLS/SSL

TLS 是 TCP 之上的安全层,主要提供三件事:

  • 加密:防止传输内容被直接读取。
  • 完整性校验:防止传输内容被篡改。
  • 身份认证:通常是客户端验证服务器证书是否可信。

wss:// 可以理解为:

WebSocket over TLS

它和 https:// 一样,都需要先完成 TLS 握手,再在加密通道里传输应用层数据。

2.2 SSLContext

SSLContext 是 TLS 配置入口。

它通常负责整合:

  • TrustManager:决定信任哪些服务器证书。
  • KeyManager:决定客户端使用什么客户端证书,主要用于双向 TLS,也叫 mTLS。
  • TLS 协议栈配置:例如使用 TLS 实例。

典型关系:

TrustManager / KeyManager
        -> SSLContext.init(...)
        -> SSLContext.socketFactory
        -> OkHttpClient

2.3 SSLSocketFactory

SSLSocketFactory 是创建 TLS socket 的工厂。

OkHttp 在建立 https://wss:// 连接时,会用它创建底层加密 socket。

通常来源:

val sslSocketFactory = sslContext.socketFactory

OkHttp 配置时通常还需要同时传入对应的 X509TrustManager

OkHttpClient.Builder()
    .sslSocketFactory(sslSocketFactory, x509TrustManager)
    .build()

2.4 TrustManager

TrustManager 决定“是否信任对方证书”。

在 WebSocket wss:// 场景里,它主要验证服务端发来的证书链,例如:

  • 证书链能否连接到受信任 CA。
  • 证书是否过期。
  • 证书用途是否符合要求。
  • 证书签名是否有效。

2.5 X509TrustManager

X509TrustManager 是 HTTPS/WSS 常见的 TrustManager 类型,用来验证 X.509 证书链。

生产环境不要使用“信任所有证书”的实现,例如:

override fun checkServerTrusted(chain: Array<X509Certificate>, authType: String) {
    // 什么都不做,等于信任所有证书。生产环境禁止这样写。
}

这种写法会让 TLS 失去服务器身份校验能力,容易受到中间人攻击。

2.6 TrustManagerFactory

TrustManagerFactory 用来根据某个信任库生成 TrustManager

常见初始化方式:

写法 含义
tmf.init(null as KeyStore?) 使用系统默认信任库
tmf.init(keyStore) 使用自定义 KeyStore 作为信任库

如果服务器使用的是公网 CA 签发的合法证书,通常不需要自定义 SSLContext

如果服务器使用的是自签证书、公司内网 CA、测试环境 CA,就需要把对应 CA 或证书放进 KeyStore,再通过 TrustManagerFactory 生成 X509TrustManager

2.7 KeyStore

KeyStore 是证书和密钥的容器。

常见用途有两种:

用途 放什么 提供给谁
信任库 trust store CA 证书或服务端证书 TrustManagerFactory
客户端证书库 key store 客户端私钥和客户端证书 KeyManagerFactory

普通 HTTPS/WSS 通常只需要服务器证书校验,也就是使用 TrustManager

双向 TLS,也叫 mTLS,才需要客户端证书和 KeyManager

2.8 HostnameVerifier

HostnameVerifier 负责校验证书里的域名是否匹配当前访问的域名。

它和 TrustManager 的分工不同:

组件 负责什么
TrustManager 证书链是否可信
HostnameVerifier 证书是否签发给当前访问的域名

例如访问:

wss://api.example.com/socket

即使证书链本身可信,也必须确认这张证书确实属于 api.example.com

生产环境不要这样写:

hostnameVerifier { _, _ -> true }

这会跳过域名校验,同样会削弱 TLS 安全性。

3. WSS 建连流程

以 OkHttp 连接 wss:// 为例,流程如下:

1. 建立 TCP 连接
2. 开始 TLS 握手
3. 服务器发送证书链
4. X509TrustManager 校验证书链可信性
5. HostnameVerifier 校验证书域名匹配
6. TLS 握手成功,得到加密通道
7. 在加密通道内发送 HTTP Upgrade 请求
8. 服务端返回 101 Switching Protocols
9. WebSocket 建立成功,开始收发 WebSocket 帧

也就是:

先保证通道安全,再进行 WebSocket 升级,再进入业务通信。

4. 什么时候需要自定义 TLS 配置

不需要自定义的情况:

  • 服务端证书由系统信任的公网 CA 签发。
  • 域名和证书完全匹配。
  • 不需要客户端证书。

需要自定义的情况:

  • 服务端使用自签证书。
  • 服务端使用公司内网 CA。
  • 测试环境证书链不被系统信任。
  • 需要双向 TLS,也就是客户端也要提供证书。
  • 需要证书锁定,也就是 certificate pinning。

正确方向:

CA 证书或服务端证书
  -> KeyStore
  -> TrustManagerFactory
  -> X509TrustManager
  -> SSLContext
  -> SSLSocketFactory
  -> OkHttpClient

5. 自定义 TrustManager 的示意代码

下面代码只用于理解名词关系,不是完整生产封装。

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 校验。

6. 鉴权与会话概念

TLS 只解决连接安全,不解决业务登录态。

WebSocket 的鉴权通常发生在 HTTP Upgrade 握手阶段,因为 WebSocket 连接最开始就是一个 HTTP 请求。

6.1 Token

token 是客户端持有的访问凭证。

常见传递方式:

Authorization: Bearer <token>

常见类型:

类型 特点
随机字符串 token 服务端存储 token 并校验
JWT token 自包含 claims,并通过签名防篡改

token 更适合 App、前后端分离、微服务和无状态扩展场景。

需要注意:

  • token 要有过期时间。
  • access token 过期后通常用 refresh token 换新。
  • WebSocket 建立后不能直接修改握手 header。
  • 如果握手 header 里的 token 过期,常见做法是刷新 token 后重新连接。

cookie 是服务端通过 Set-Cookie 下发,客户端后续请求自动携带的小段键值信息。

常见形式:

Set-Cookie: JSESSIONID=abc123; Path=/; Secure; HttpOnly

后续请求携带:

Cookie: JSESSIONID=abc123

浏览器会自动管理 cookie。

Android App 使用 OkHttp 时,如果希望自动保存和携带 cookie,需要配置 CookieJar

6.3 Session

session 通常指服务端保存的一份会话状态。

传统模式是:

客户端 cookie 里保存 sessionId
服务端根据 sessionId 查询 session
服务端判断用户是谁、是否已登录、有没有权限

session 的状态在服务端。

服务端横向扩容时,需要考虑:

  • session 共享,例如 Redis。
  • 粘性会话,也就是同一用户固定打到同一台服务。
  • session 过期和清理策略。

7. WebSocket 中的鉴权方式

7.1 使用 Token 鉴权

这是 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 管理。

7.3 建立连接后发送鉴权消息

有些业务协议会选择 WebSocket 建立后,再发送第一条 auth 消息。

流程:

1. WebSocket 握手允许升级
2. onOpen 回调触发
3. 客户端发送 auth 消息,例如 {"type":"auth","token":"..."}
4. 服务端校验 auth 消息
5. 校验成功后才允许订阅、推送或收发业务数据

这种方式属于业务协议设计,不是 WebSocket 标准强制要求。

它的特点:

  • 优点:业务层更灵活,可以在连接内做重新鉴权。
  • 缺点:连接已经建立,如果 auth 失败,需要服务端主动关闭或限制该连接。

8. TLS 与鉴权的组合流程

一个典型的 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 帧

关键点:

  • TLS 在 HTTP Upgrade 之前完成。
  • token/cookie/session 通常在 HTTP Upgrade 阶段校验。
  • WebSocket 消息帧是在 TLS 加密通道内传输。
  • wss:// 不等于已登录,它只代表通道被 TLS 保护。

9. 常见误区

误区 正确认识
wss:// 就等于已经鉴权 错,wss:// 只代表 TLS 加密通道
token 可以替代 TLS 错,token 证明身份,TLS 保护传输
TLS 可以替代 token 错,TLS 通常只认证服务器,不认证具体用户
TrustManager 会校验登录态 错,它只负责证书链信任
HostnameVerifierTrustManager 是一回事 错,一个校验证书链,一个校验域名
生产环境可以信任所有证书 错,这会破坏 TLS 安全性
WebSocket 建立后可以随时改握手 header 错,header 只在握手请求中发送

10. 名词关系速查

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

11. 一句话记忆

TLS/SSL 负责把路修安全,证书负责确认服务器是真的。
token/cookie/session 负责确认客户端是谁,有没有权限。
wss:// 的顺序是:先 TLS,再 HTTP Upgrade,最后 WebSocket 消息。