Flutter for OpenHarmony:dio_cookie_manager 让 Dio 发挥会话管理能力,像浏览器一样自动处理 Cookie 深度解析与鸿蒙适配指南
欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.ZEEKLOG.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 依赖安装
dependencies:dio: ^5.0.0 cookie_jar: ^4.0.0 dio_cookie_manager: ^3.0.0 path_provider: ^2.0.0 # 用于持久化2.2 基础用法(内存模式)
import'package:dio/dio.dart';import'package:dio_cookie_manager/dio_cookie_manager.dart';import'package:cookie_jar/cookie_jar.dart';voidmain()async{final dio =Dio();final cookieJar =CookieJar();// 注入拦截器 dio.interceptors.add(CookieManager(cookieJar));// 第一次请求:登录,服务端 Set-Cookie: session=123await dio.post('https://api.example.com/login');// 第二次请求:自动带上 Cookie: session=123await dio.get('https://api.example.com/user/profile');}
2.3 持久化(重启不掉线)
import'package:path_provider/path_provider.dart';voidinitDio()async{// 获取应用文档目录final appDocDir =awaitgetApplicationDocumentsDirectory();// 创建持久化 Jarfinal persistJar =PersistCookieJar( storage:FileStorage("${appDocDir.path}/.cookies/"),); dio.interceptors.add(CookieManager(persistJar));}
三、常见应用场景
3.1 场景 1:SSO 单点登录
对接企业内部的老旧 SSO 系统,需要多次重定向并携带 Cookie 才能获取最终 Token。
// CookieManager 会自动处理重定向过程中的 Cookie 变化await dio.get('https://sso.company.com/auth?service=myapp');3.2 场景 2:WebView 同步
用户在 WebView 中登录了,App 原生请求也需要共享该登录状态。
// 手动提取 WebView 的 Cookie 并存入 Jarfinal cookies =await webview.getCookies();await cookieJar.saveFromResponse(Uri.parse(url), cookies);3.3 场景 3:爬虫与自动化测试
模拟浏览器行为,抓取需要登录才能访问的网页数据。
// 伪造 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 依然存在,无需再次登录。
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';// 模拟 APIconstString _loginUrl ='https://httpbin.org/cookies/set/session/abcdefg';constString _profileUrl ='https://httpbin.org/cookies';voidmain(){runApp(constMaterialApp(home:CookiePage()));}classCookiePageextendsStatefulWidget{constCookiePage({super.key});@overrideState<CookiePage>createState()=>_CookiePageState();}class _CookiePageState extendsState<CookiePage>{String _status ='未初始化'; late Dio _dio;PersistCookieJar? _cookieJar;@overridevoidinitState(){super.initState();_initDio();}Future<void>_initDio()async{try{final dir =awaitgetApplicationDocumentsDirectory();// 在 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-Cookieawait _dio.get(_loginUrl);setState(()=> _status ='登录成功,Cookie 已保存');}catch(e){setState(()=> _status ='登录错误: $e');}}Future<void>_checkProfile()async{if(_cookieJar ==null)return;try{setState(()=> _status ='正在检查会话...');// 访问此 URL 会返回当前请求带的所有 Cookiefinal response =await _dio.get(_profileUrl);setState(()=> _status ='API 返回:\n${response.data}');}catch(e){setState(()=> _status ='请求错误: $e');}}Future<void>_logout()async{if(_cookieJar ==null)return;// 清除所有 Cookieawait _cookieJar!.deleteAll();setState(()=> _status ='已退出登录 (Cookie 清除)');}@overrideWidgetbuild(BuildContext context){returnScaffold( appBar:AppBar(title:constText('Dio Cookie 示例')), body:Padding( padding:constEdgeInsets.all(16.0), child:Column( children:[Text('状态: $_status', style:constTextStyle(fontSize:16)),constSizedBox(height:20),SingleChildScrollView( scrollDirection:Axis.horizontal, child:Row( children:[ElevatedButton(onPressed: _login, child:constText('模拟登录')),constSizedBox(width:10),ElevatedButton(onPressed: _checkProfile, child:constText('检查 Session')),constSizedBox(width:10),OutlinedButton(onPressed: _logout, child:constText('清除 Cookie')),],),),],),),);}}
六、总结
dio_cookie_manager 是对接基于 Session 的旧系统的唯一正确姿势。
最佳实践:
- 单例模式:
PersistCookieJar涉及到文件锁,建议全局只创建一个实例,否则可能出现读写冲突。 - 清理策略:虽然 Session 会过期,但持久化的 Cookie 文件却不会自动删除。可以在 App 启动时调用
.deleteExpired()来清理过期的 Cookie。