Flutter for OpenHarmony 实战:mango_shop 认证模块的状态管理与鸿蒙安全存储
·
Flutter for OpenHarmony 实战:mango_shop 认证模块的状态管理与鸿蒙安全存储

作者:爱吃大芒果
个人主页 爱吃大芒果
本文所属专栏Flutter
更多专栏
Ascend C 算子开发教程(进阶)
鸿蒙集成
OpenAgents
openJiuwen
从0到1自学C++
认证模块现状分析
通过对 mango_shop 项目的分析,我们发现:
-
认证管理器实现:项目使用了一个简单的
AuthManager类管理认证状态- 采用单例模式实现
- 仅在内存中存储用户信息
- 没有持久化存储机制,应用重启后登录状态会丢失
- 没有 token 管理和自动登录功能
-
登录流程:
- 实现了基本的登录表单和验证
- 使用模拟数据,没有实际的网络请求
- 登录成功后将用户信息存储到
AuthManager并跳转到首页
-
安全存储:
- 项目中没有使用任何安全存储方案
- 没有针对 OpenHarmony 平台的安全存储适配
认证模块优化方案
1. 状态管理方案设计
1.1 状态管理库选择
考虑到项目的复杂度和未来的可扩展性,推荐使用以下状态管理方案之一:
- Provider:轻量级状态管理,易于集成和使用
- Riverpod:Provider 的升级版,提供更好的性能和更灵活的 API
- Bloc:适合复杂的状态管理场景,提供清晰的状态流转
本文将使用 Riverpod 作为状态管理方案,因为它:
- 提供了更现代的 API
- 支持更灵活的依赖注入
- 具有更好的性能和测试ability
- 与 Flutter 3.0+ 完全兼容
1.2 认证状态管理实现
首先,添加 Riverpod 依赖:
# pubspec.yaml
dependencies:
# ... 其他依赖 ...
flutter_riverpod: ^2.5.1
riverpod_annotation: ^2.3.5
dev_dependencies:
# ... 其他依赖 ...
riverpod_generator: ^2.4.0
build_runner: ^2.4.11
然后,实现认证状态管理:
// lib/providers/auth_provider.dart
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:mango_shop/api/services/auth_service.dart';
import 'package:mango_shop/models/user.dart';
// 认证状态
enum AuthStatus {
initial,
loading,
authenticated,
unauthenticated,
error,
}
// 认证状态数据类
class AuthState {
final AuthStatus status;
final User? user;
final String? errorMessage;
const AuthState({
required this.status,
this.user,
this.errorMessage,
});
// 初始状态
const AuthState.initial() : this(status: AuthStatus.initial);
// 加载状态
const AuthState.loading() : this(status: AuthStatus.loading);
// 认证成功状态
const AuthState.authenticated(this.user)
: this.status = AuthStatus.authenticated,
this.errorMessage = null;
// 未认证状态
const AuthState.unauthenticated()
: this(status: AuthStatus.unauthenticated),
this.user = null,
this.errorMessage = null;
// 错误状态
const AuthState.error(this.errorMessage)
: this.status = AuthStatus.error,
this.user = null;
}
// 认证状态提供者
class AuthNotifier extends StateNotifier<AuthState> {
final AuthService _authService;
AuthNotifier(this._authService) : super(const AuthState.initial()) {
// 初始化时检查是否已登录
_checkAuthStatus();
}
// 检查认证状态
Future<void> _checkAuthStatus() async {
state = const AuthState.loading();
try {
if (_authService.isLoggedIn()) {
final user = _authService.getCurrentUser();
state = AuthState.authenticated(User.fromJson(user));
} else {
state = const AuthState.unauthenticated();
}
} catch (e) {
state = AuthState.error(e.toString());
}
}
// 登录
Future<void> login(String username, String password) async {
state = const AuthState.loading();
try {
final result = await _authService.login(username, password);
final user = User.fromJson(result['user']);
state = AuthState.authenticated(user);
} catch (e) {
state = AuthState.error(e.toString());
}
}
// 注册
Future<void> register(Map<String, dynamic> userData) async {
state = const AuthState.loading();
try {
final result = await _authService.register(userData);
final user = User.fromJson(result['user']);
state = AuthState.authenticated(user);
} catch (e) {
state = AuthState.error(e.toString());
}
}
// 退出登录
Future<void> logout() async {
state = const AuthState.loading();
try {
await _authService.logout();
state = const AuthState.unauthenticated();
} catch (e) {
state = AuthState.error(e.toString());
}
}
}
// 创建认证状态提供者
final authProvider = StateNotifierProvider<AuthNotifier, AuthState>((ref) {
final authService = AuthService();
return AuthNotifier(authService);
});
// 便捷获取认证状态的提供者
final authStatusProvider = Provider<AuthStatus>((ref) {
return ref.watch(authProvider).status;
});
// 便捷获取当前用户的提供者
final currentUserProvider = Provider<User?>((ref) {
return ref.watch(authProvider).user;
});
// 便捷获取是否已认证的提供者
final isAuthenticatedProvider = Provider<bool>((ref) {
return ref.watch(authProvider).status == AuthStatus.authenticated;
});

2. 安全存储实现
2.1 依赖添加
# pubspec.yaml
dependencies:
# ... 其他依赖 ...
flutter_secure_storage: ^9.2.2
# OpenHarmony 安全存储适配
ohos_secure_storage: ^1.0.0 # 假设的包名,实际需要根据 OpenHarmony 生态选择
2.2 安全存储服务实现
// lib/services/secure_storage_service.dart
import 'dart:io';
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
class SecureStorageService {
static final SecureStorageService _instance = SecureStorageService._internal();
factory SecureStorageService() => _instance;
late FlutterSecureStorage _storage;
SecureStorageService._internal() {
// 根据平台创建不同的存储实例
if (Platform.isOpenHarmony) {
// OpenHarmony 平台特定配置
_storage = FlutterSecureStorage(
aOptions: AndroidOptions(
encryptedSharedPreferences: true,
),
);
} else {
// 其他平台配置
_storage = FlutterSecureStorage();
}
}
// 存储数据
Future<void> write(String key, String value) async {
await _storage.write(key: key, value: value);
}
// 读取数据
Future<String?> read(String key) async {
return await _storage.read(key: key);
}
// 删除数据
Future<void> delete(String key) async {
await _storage.delete(key: key);
}
// 删除所有数据
Future<void> deleteAll() async {
await _storage.deleteAll();
}
// 检查键是否存在
Future<bool> containsKey(String key) async {
final value = await _storage.read(key: key);
return value != null;
}
}
2.3 OpenHarmony 安全存储适配
对于 OpenHarmony 平台,我们需要实现特定的安全存储适配:
// lib/services/ohos_secure_storage_adapter.dart
import 'dart:io';
import 'package:flutter/foundation.dart';
import 'package:mango_shop/services/secure_storage_service.dart';
class OhosSecureStorageAdapter {
static final OhosSecureStorageAdapter _instance = OhosSecureStorageAdapter._internal();
factory OhosSecureStorageAdapter() => _instance;
OhosSecureStorageAdapter._internal();
// 检查是否为 OpenHarmony 平台
bool get isOpenHarmony {
return Platform.environment.containsKey('OHOS') ||
Platform.operatingSystem.toLowerCase() == 'openharmony';
}
// OpenHarmony 平台的安全存储实现
Future<void> writeSecure(String key, String value) async {
if (isOpenHarmony) {
// OpenHarmony 特定的安全存储实现
// 这里需要使用 OpenHarmony 的安全存储 API
try {
// 模拟 OpenHarmony 安全存储调用
print('OpenHarmony: Writing $key to secure storage');
// 实际实现需要使用 OpenHarmony 的安全存储 API
} catch (e) {
if (kDebugMode) {
print('OpenHarmony secure storage error: $e');
}
// 降级到普通存储
await SecureStorageService().write(key, value);
}
} else {
// 其他平台使用标准安全存储
await SecureStorageService().write(key, value);
}
}
// OpenHarmony 平台的安全读取实现
Future<String?> readSecure(String key) async {
if (isOpenHarmony) {
// OpenHarmony 特定的安全读取实现
try {
// 模拟 OpenHarmony 安全读取调用
print('OpenHarmony: Reading $key from secure storage');
// 实际实现需要使用 OpenHarmony 的安全存储 API
return await SecureStorageService().read(key);
} catch (e) {
if (kDebugMode) {
print('OpenHarmony secure storage error: $e');
}
// 降级到普通存储
return await SecureStorageService().read(key);
}
} else {
// 其他平台使用标准安全存储
return await SecureStorageService().read(key);
}
}
}
3. 认证服务优化
// lib/api/services/auth_service.dart
import 'package:mango_shop/api/client/mango_api_client.dart';
import 'package:mango_shop/services/secure_storage_service.dart';
import 'package:mango_shop/services/ohos_secure_storage_adapter.dart';
import 'package:mango_shop/models/user.dart';
class AuthService {
static final AuthService _instance = AuthService._internal();
factory AuthService() => _instance;
late MangoApiClient _apiClient;
final SecureStorageService _secureStorage = SecureStorageService();
final OhosSecureStorageAdapter _ohosAdapter = OhosSecureStorageAdapter();
User? _currentUser;
AuthService._internal();
// 初始化
Future<void> initialize() async {
_apiClient = MangoApiClient();
// 加载保存的用户信息
await _loadUserInfo();
}
// 加载用户信息
Future<void> _loadUserInfo() async {
final token = await _ohosAdapter.readSecure('auth_token');
if (token != null) {
final userJson = await _secureStorage.read('user');
if (userJson != null) {
_currentUser = User.fromJson(jsonDecode(userJson));
}
}
}
// 登录
Future<dynamic> login(String username, String password) async {
final result = await _apiClient.login(username, password);
if (result['token'] != null) {
// 存储 token
await _ohosAdapter.writeSecure('auth_token', result['token']);
// 存储用户信息
await _secureStorage.write('user', jsonEncode(result['user']));
_currentUser = User.fromJson(result['user']);
}
return result;
}
// 注册
Future<dynamic> register(Map<String, dynamic> userData) async {
return await _apiClient.register(userData);
}
// 退出登录
Future<void> logout() async {
await _ohosAdapter.deleteSecure('auth_token');
await _secureStorage.delete('user');
_currentUser = null;
}
// 检查是否已登录
bool isLoggedIn() {
return _currentUser != null;
}
// 获取当前用户
User? getCurrentUser() {
return _currentUser;
}
// 获取认证 token
Future<String?> getAuthToken() async {
return await _ohosAdapter.readSecure('auth_token');
}
// 刷新 token
Future<dynamic> refreshToken() async {
final currentToken = await getAuthToken();
if (currentToken == null) {
throw Exception('No token available');
}
final result = await _apiClient.refreshToken(currentToken);
if (result['token'] != null) {
await _ohosAdapter.writeSecure('auth_token', result['token']);
}
return result;
}
}
4. 用户模型实现
// lib/models/user.dart
class User {
final String id;
final String username;
final String nickname;
final String avatar;
final String phone;
final String email;
final String role;
User({
required this.id,
required this.username,
required this.nickname,
required this.avatar,
required this.phone,
required this.email,
this.role = 'user',
});
// 从 JSON 创建用户实例
factory User.fromJson(Map<String, dynamic> json) {
return User(
id: json['id'] ?? '',
username: json['username'] ?? '',
nickname: json['nickname'] ?? '',
avatar: json['avatar'] ?? '',
phone: json['phone'] ?? '',
email: json['email'] ?? '',
role: json['role'] ?? 'user',
);
}
// 转换为 JSON
Map<String, dynamic> toJson() {
return {
'id': id,
'username': username,
'nickname': nickname,
'avatar': avatar,
'phone': phone,
'email': email,
'role': role,
};
}
}
实际应用示例
1. 登录页面优化
// lib/pages/login/index.dart
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:mango_shop/providers/auth_provider.dart';
import 'package:mango_shop/routes/navigation_service.dart';
import 'package:mango_shop/routes/router.dart';
class LoginPage extends ConsumerStatefulWidget {
const LoginPage({Key? key}) : super(key: key);
_LoginPageState createState() => _LoginPageState();
}
class _LoginPageState extends ConsumerState<LoginPage> {
final _formKey = GlobalKey<FormState>();
final TextEditingController _accountController = TextEditingController();
final TextEditingController _passwordController = TextEditingController();
bool _isObscure = true;
String? _validateAccount(String? value) {
if (value == null || value.isEmpty) {
return '请输入账号';
}
if (value.length < 3) {
return '账号长度不能少于3位';
}
return null;
}
String? _validatePassword(String? value) {
if (value == null || value.isEmpty) {
return '请输入密码';
}
if (value.length < 6) {
return '密码长度不能少于6位';
}
return null;
}
Future<void> _handleLogin() async {
if (!_formKey.currentState!.validate()) {
return;
}
final authNotifier = ref.read(authProvider.notifier);
await authNotifier.login(
_accountController.text,
_passwordController.text,
);
// 检查登录状态
final authStatus = ref.read(authStatusProvider);
if (authStatus == AuthStatus.authenticated) {
// 登录成功,跳转到首页
NavigationService.replaceWith(RouteNames.home);
} else if (authStatus == AuthStatus.error) {
// 登录失败,显示错误信息
final errorMessage = ref.read(authProvider).errorMessage;
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text(errorMessage ?? '登录失败')),
);
}
}
void dispose() {
_accountController.dispose();
_passwordController.dispose();
super.dispose();
}
Widget build(BuildContext context) {
final authState = ref.watch(authProvider);
final isLoading = authState.status == AuthStatus.loading;
return Scaffold(
backgroundColor: Colors.white,
appBar: AppBar(
title: const Text('登录'),
centerTitle: true,
backgroundColor: Colors.white,
elevation: 0,
foregroundColor: Colors.black,
),
body: SingleChildScrollView(
padding: const EdgeInsets.all(30),
child: Form(
key: _formKey,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const SizedBox(height: 40),
const Text(
'欢迎回来',
style: TextStyle(
fontSize: 28,
fontWeight: FontWeight.bold,
color: Colors.black87,
),
),
const SizedBox(height: 10),
Text(
'请使用您的账号密码登录',
style: TextStyle(
fontSize: 16,
color: Colors.grey[600],
),
),
const SizedBox(height: 50),
TextFormField(
controller: _accountController,
decoration: InputDecoration(
labelText: '账号',
hintText: '请输入账号',
prefixIcon: const Icon(Icons.person_outline),
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(12),
),
filled: true,
fillColor: Colors.grey[100],
),
validator: _validateAccount,
textInputAction: TextInputAction.next,
),
const SizedBox(height: 20),
TextFormField(
controller: _passwordController,
obscureText: _isObscure,
decoration: InputDecoration(
labelText: '密码',
hintText: '请输入密码',
prefixIcon: const Icon(Icons.lock_outline),
suffixIcon: IconButton(
icon: Icon(
_isObscure ? Icons.visibility_off : Icons.visibility,
),
onPressed: () {
setState(() {
_isObscure = !_isObscure;
});
},
),
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(12),
),
filled: true,
fillColor: Colors.grey[100],
),
validator: _validatePassword,
onFieldSubmitted: (_) => _handleLogin(),
),
const SizedBox(height: 30),
SizedBox(
width: double.infinity,
height: 50,
child: ElevatedButton(
onPressed: isLoading ? null : _handleLogin,
style: ElevatedButton.styleFrom(
backgroundColor: Colors.red,
foregroundColor: Colors.white,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12),
),
),
child: isLoading
? const SizedBox(
width: 20,
height: 20,
child: CircularProgressIndicator(
color: Colors.white,
strokeWidth: 2,
),
)
: const Text(
'登录',
style: TextStyle(
fontSize: 18,
fontWeight: FontWeight.w500,
),
),
),
),
],
),
),
),
);
}
}

2. 个人中心页面优化
// lib/pages/Mine/index.dart
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:mango_shop/providers/auth_provider.dart';
import 'package:mango_shop/routes/navigation_service.dart';
import 'package:mango_shop/routes/router.dart';
class MineView extends ConsumerWidget {
const MineView({Key? key}) : super(key: key);
Widget build(BuildContext context, WidgetRef ref) {
final isAuthenticated = ref.watch(isAuthenticatedProvider);
final currentUser = ref.watch(currentUserProvider);
final authNotifier = ref.read(authProvider.notifier);
// 退出登录
void _handleLogout() {
showDialog(
context: context,
builder: (context) => AlertDialog(
title: const Text('退出登录'),
content: const Text('确定要退出登录吗?'),
actions: [
TextButton(
onPressed: () => Navigator.pop(context),
child: const Text('取消'),
),
TextButton(
onPressed: () async {
await authNotifier.logout();
Navigator.pop(context);
// 跳转到登录页
NavigationService.navigateTo(RouteNames.login);
},
child: const Text('确定'),
),
],
),
);
}
return Scaffold(
backgroundColor: Colors.grey[100],
body: ListView(
children: [
// 用户信息区域
Container(
color: Colors.white,
padding: const EdgeInsets.all(20),
child: isAuthenticated
? Row(
children: [
CircleAvatar(
radius: 40,
backgroundImage: NetworkImage(currentUser?.avatar ?? ''),
),
const SizedBox(width: 20),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
currentUser?.nickname ?? '',
style: const TextStyle(
fontSize: 18,
fontWeight: FontWeight.bold,
),
),
const SizedBox(height: 8),
Text(
currentUser?.phone ?? '',
style: TextStyle(
fontSize: 14,
color: Colors.grey[600],
),
),
],
),
],
)
: GestureDetector(
onTap: () {
// 跳转到登录页
NavigationService.navigateTo(RouteNames.login);
},
child: Row(
children: [
CircleAvatar(
radius: 40,
backgroundColor: Colors.grey[200],
child: const Icon(
Icons.person,
size: 40,
color: Colors.grey,
),
),
const SizedBox(width: 20),
const Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'未登录',
style: TextStyle(
fontSize: 18,
fontWeight: FontWeight.bold,
),
),
SizedBox(height: 8),
Text(
'点击登录',
style: TextStyle(
fontSize: 14,
color: Colors.blue,
),
),
],
),
],
),
),
),
const SizedBox(height: 10),
// 功能列表
Container(
color: Colors.white,
child: Column(
children: [
// 订单相关
ListTile(
title: const Text('我的订单'),
trailing: const Icon(Icons.arrow_forward_ios),
onTap: () {
if (!isAuthenticated) {
NavigationService.navigateTo(RouteNames.login);
return;
}
// 跳转到订单页面
NavigationService.navigateTo(RouteNames.pendingPaymentOrders);
},
),
const Divider(height: 1),
// 地址管理
ListTile(
title: const Text('地址管理'),
trailing: const Icon(Icons.arrow_forward_ios),
onTap: () {
if (!isAuthenticated) {
NavigationService.navigateTo(RouteNames.login);
return;
}
// 跳转到地址管理页面
NavigationService.navigateTo(RouteNames.addressManagement);
},
),
const Divider(height: 1),
// 个人设置
ListTile(
title: const Text('个人设置'),
trailing: const Icon(Icons.arrow_forward_ios),
onTap: () {
if (!isAuthenticated) {
NavigationService.navigateTo(RouteNames.login);
return;
}
// 跳转到个人设置页面
NavigationService.navigateTo(RouteNames.settings);
},
),
],
),
),
const SizedBox(height: 10),
// 退出登录按钮
if (isAuthenticated)
Container(
color: Colors.white,
padding: const EdgeInsets.all(20),
child: ElevatedButton(
onPressed: _handleLogout,
style: ElevatedButton.styleFrom(
backgroundColor: Colors.white,
foregroundColor: Colors.red,
side: const BorderSide(color: Colors.red),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8),
),
),
child: const Text('退出登录'),
),
),
],
),
);
}
}

3. 路由守卫实现
// lib/routes/route_guard.dart
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:mango_shop/providers/auth_provider.dart';
import 'package:mango_shop/routes/router.dart';
class RouteGuard extends ConsumerWidget {
final Widget child;
final bool requireAuth;
const RouteGuard({
Key? key,
required this.child,
this.requireAuth = false,
}) : super(key: key);
Widget build(BuildContext context, WidgetRef ref) {
final authStatus = ref.watch(authStatusProvider);
// 如果页面需要认证且用户未认证,重定向到登录页
if (requireAuth && authStatus != AuthStatus.authenticated) {
WidgetsBinding.instance.addPostFrameCallback((_) {
Navigator.pushReplacementNamed(context, RouteNames.login);
});
return const SizedBox();
}
return child;
}
}
4. 应用启动认证检查
// lib/main.dart
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:mango_shop/routes/index.dart';
import 'package:mango_shop/providers/auth_provider.dart';
void main(List<String> args) {
runApp(
ProviderScope(
child: MyApp(),
),
);
}
class MyApp extends ConsumerWidget {
Widget build(BuildContext context, WidgetRef ref) {
// 监听认证状态
final authStatus = ref.watch(authStatusProvider);
// 显示启动加载
if (authStatus == AuthStatus.initial || authStatus == AuthStatus.loading) {
return MaterialApp(
home: Scaffold(
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Image.asset('lib/assets/爱吃大芒果.png', width: 100, height: 100),
const SizedBox(height: 20),
const CircularProgressIndicator(),
const SizedBox(height: 20),
const Text('加载中...'),
],
),
),
),
);
}
// 返回应用根组件
return getRootWidget();
}
}
测试与调试
1. 认证功能测试
- 单元测试:测试认证状态管理和安全存储功能
- 集成测试:测试完整的认证流程
- 跨平台测试:确保在所有平台上认证功能表现一致
- 安全测试:测试安全存储的安全性和可靠性
2. 调试工具
- 认证状态日志:添加认证状态变化的日志,便于调试
- 安全存储调试:添加安全存储操作的日志,了解存储状态
- 网络请求调试:使用网络调试工具监控认证相关的网络请求
3. 性能优化
- 认证状态缓存:避免重复检查认证状态
- 安全存储优化:减少安全存储的读写操作
- 启动性能:优化应用启动时的认证检查流程
- 内存管理:及时释放认证相关的资源
总结与展望
通过本文介绍的认证模块优化方案,我们可以:
- 提高代码可维护性:使用 Riverpod 进行状态管理,代码结构更清晰
- 增强安全性:实现跨平台的安全存储,特别是 OpenHarmony 平台的适配
- 提升用户体验:实现自动登录、token 管理等功能
- 提高应用可靠性:添加错误处理和异常捕获
- 增强跨平台兼容性:针对 OpenHarmony 平台进行特殊适配
未来,可以考虑:
- 添加生物识别认证:如指纹、面部识别等
- 实现多因素认证:提高账户安全性
- 添加社交登录:如微信、支付宝等第三方登录
- 实现单点登录:在多个应用间共享登录状态
- 添加登录风险评估:提高账户安全性
Flutter for OpenHarmony 为跨平台应用开发提供了新的可能性,通过合理的认证模块设计和安全存储适配,可以构建出在所有平台上表现出色的应用。
欢迎加入开源鸿蒙跨平台社区:开源鸿蒙跨平台开发者社区
AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐
所有评论(0)