欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.csdn.net

前言
在移动端开发中,我们通常使用 JWT (Authorization Header) 进行身份验证。但如果你的后端是基于 Session/Cookie 的老系统(如 PHP/Java JSP),或者你需要对接网页爬虫,那么 Cookie 的管理就变得至关重要。
dio 本身是不存储 Cookie 的。dio_cookie_manager 是一个官方推荐的拦截器,它结合 cookie_jar 库,能自动从响应头提取 Set-Cookie,并在下次请求时带上 Cookie,完全模拟浏览器的行为。
一、概念介绍/原理解析
1.1 基础概念
- CookieManager: Dio 的拦截器,负责提取和注入 Cookie。
- CookieJar: 内存中的 Cookie 存储器(App 重启丢失)。
- PersistCookieJar: 持久化的 Cookie 存储器(存在文件系统,重启保留)。
请求前拦截
加载 Cookie
注入请求头
发送
带有 Set-Cookie
保存 Cookie
写入存储
Dio 请求
CookieManager
CookieJar
服务端
Dio 响应
1.2 进阶概念
它可以处理 Cookie 的 Path, Domain, Secure, HttpOnly 属性,确保 cookie 不会泄露给错误的域名。还可以通过自定义 Storage 实现将 Cookie 存入加密数据库。
二、核心 API/组件详解
2.1 依赖安装
yaml
dependencies:
dio: ^5.0.0
cookie_jar: ^4.0.0
dio_cookie_manager: ^3.0.0
path_provider: ^2.0.0 # 用于持久化
2.2 基础用法(内存模式)
dart
import 'package:dio/dio.dart';
import 'package:dio_cookie_manager/dio_cookie_manager.dart';
import 'package:cookie_jar/cookie_jar.dart';
void main() async {
final dio = Dio();
final cookieJar = CookieJar();
// 注入拦截器
dio.interceptors.add(CookieManager(cookieJar));
// 第一次请求:登录,服务端 Set-Cookie: session=123
await dio.post('https://api.example.com/login');
// 第二次请求:自动带上 Cookie: session=123
await dio.get('https://api.example.com/user/profile');
}

2.3 持久化(重启不掉线)
dart
import 'package:path_provider/path_provider.dart';
void initDio() async {
// 获取应用文档目录
final appDocDir = await getApplicationDocumentsDirectory();
// 创建持久化 Jar
final persistJar = PersistCookieJar(
storage: FileStorage("${appDocDir.path}/.cookies/"),
);
dio.interceptors.add(CookieManager(persistJar));
}

三、常见应用场景
3.1 场景 1:SSO 单点登录
对接企业内部的老旧 SSO 系统,需要多次重定向并携带 Cookie 才能获取最终 Token。
dart
// CookieManager 会自动处理重定向过程中的 Cookie 变化
await dio.get('https://sso.company.com/auth?service=myapp');
3.2 场景 2:WebView 同步
用户在 WebView 中登录了,App 原生请求也需要共享该登录状态。
dart
// 手动提取 WebView 的 Cookie 并存入 Jar
final cookies = await webview.getCookies();
await cookieJar.saveFromResponse(Uri.parse(url), cookies);
3.3 场景 3:爬虫与自动化测试
模拟浏览器行为,抓取需要登录才能访问的网页数据。
dart
// 伪造 User-Agent 和 Cookie
dio.options.headers['User-Agent'] = 'Mozilla/5.0...';
await dio.get('https://www.zhihu.com');
四、OpenHarmony 平台适配
4.1 文件路径
path_provider 已完整适配 OpenHarmony,通过它获取的 ApplicationDocumentsDirectory 是完全符合鸿蒙沙箱规范的,因此 PersistCookieJar 可以放心使用。
4.2 跨域与安全
虽然 Dio 不是浏览器,不受同源策略限制,但鸿蒙系统的网络权限管理通过 module.json5 控制。如果 Cookie 中包含 HttpOnly,dio_cookie_manager 依然能处理(因为它是在 Dart 层模拟),但要注意不要将敏感 Cookie 打印到日志中。
五、完整示例代码
本示例展示在鸿蒙应用中实现一个"记住登录状态"的功能。首次登录后,即使重启 App,Cookie 依然存在,无需再次登录。
dart
import 'package:flutter/material.dart';
import 'package:dio/dio.dart';
import 'package:cookie_jar/cookie_jar.dart';
import 'package:dio_cookie_manager/dio_cookie_manager.dart';
import 'package:path_provider/path_provider.dart';
// 模拟 API
const String _loginUrl = 'https://httpbin.org/cookies/set/session/abcdefg';
const String _profileUrl = 'https://httpbin.org/cookies';
void main() {
runApp(const MaterialApp(home: CookiePage()));
}
class CookiePage extends StatefulWidget {
const CookiePage({super.key});
@override
State<CookiePage> createState() => _CookiePageState();
}
class _CookiePageState extends State<CookiePage> {
String _status = '未初始化';
late Dio _dio;
PersistCookieJar? _cookieJar;
@override
void initState() {
super.initState();
_initDio();
}
Future<void> _initDio() async {
try {
final dir = await getApplicationDocumentsDirectory();
// 在 OpenHarmony 沙箱目录下存储 Cookie
_cookieJar = PersistCookieJar(storage: FileStorage("${dir.path}/cookies"));
_dio = Dio();
_dio.interceptors.add(CookieManager(_cookieJar!));
setState(() => _status = '已就绪 (路径: ${dir.path})');
} catch (e) {
setState(() => _status = '初始化失败: $e');
}
}
Future<void> _login() async {
if (_cookieJar == null) return;
try {
setState(() => _status = '正在登录...');
// 访问此 URL 会使服务器设置 Set-Cookie
await _dio.get(_loginUrl);
setState(() => _status = '登录成功,Cookie 已保存');
} catch (e) {
setState(() => _status = '登录错误: $e');
}
}
Future<void> _checkProfile() async {
if (_cookieJar == null) return;
try {
setState(() => _status = '正在检查会话...');
// 访问此 URL 会返回当前请求带的所有 Cookie
final response = await _dio.get(_profileUrl);
setState(() => _status = 'API 返回:\n${response.data}');
} catch (e) {
setState(() => _status = '请求错误: $e');
}
}
Future<void> _logout() async {
if (_cookieJar == null) return;
// 清除所有 Cookie
await _cookieJar!.deleteAll();
setState(() => _status = '已退出登录 (Cookie 清除)');
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('Dio Cookie 示例')),
body: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
children: [
Text('状态: $_status', style: const TextStyle(fontSize: 16)),
const SizedBox(height: 20),
SingleChildScrollView(
scrollDirection: Axis.horizontal,
child: Row(
children: [
ElevatedButton(onPressed: _login, child: const Text('模拟登录')),
const SizedBox(width: 10),
ElevatedButton(onPressed: _checkProfile, child: const Text('检查 Session')),
const SizedBox(width: 10),
OutlinedButton(onPressed: _logout, child: const Text('清除 Cookie')),
],
),
),
],
),
),
);
}
}

六、总结
dio_cookie_manager 是对接基于 Session 的旧系统的唯一正确姿势。
最佳实践:
- 单例模式 :
PersistCookieJar涉及到文件锁,建议全局只创建一个实例,否则可能出现读写冲突。 - 清理策略 :虽然 Session 会过期,但持久化的 Cookie 文件却不会自动删除。可以在 App 启动时调用
.deleteExpired()来清理过期的 Cookie。