Flutter结合AWS Amplify与Cognito实现用户自定义登录时长
用AWS Amplify + Cognito实现Flutter登录时长自主选择功能
要实现用户自选登录保持时长并在有效期内自动登录,核心是结合Cognito令牌机制和客户端本地存储控制,以下是具体实现步骤:
1. 前置准备
确保你的Flutter项目已集成AWS Amplify Auth模块,且配置了Cognito用户池:
- 完成Amplify初始化(
Amplify.configure(amplifyconfig)) - 已在Cognito用户池设置中配置好令牌有效期(重点是Refresh Token的过期时间,可在用户池→应用客户端→设置中调整,比如设为365天作为最大允许时长)
2. 登录页面UI实现
添加“保持登录状态”复选框和时长选项,让用户选择登录有效期:
import 'package:flutter/material.dart'; import 'package:flutter_secure_storage/flutter_secure_storage.dart'; class LoginPage extends StatefulWidget { const LoginPage({super.key}); @override State<LoginPage> createState() => _LoginPageState(); } class _LoginPageState extends State<LoginPage> { final _emailController = TextEditingController(); final _passwordController = TextEditingController(); bool _keepLoggedIn = false; int? _selectedDuration; // 单位:毫秒 final _durationOptions = const [ {"label": "2天", "value": 2 * 24 * 60 * 60 * 1000}, {"label": "15天", "value": 15 * 24 * 60 * 60 * 1000}, {"label": "30天", "value": 30 * 24 * 60 * 60 * 1000}, {"label": "最大允许时长", "value": 365 * 24 * 60 * 60 * 1000}, // 对应Cognito Refresh Token最大有效期 ]; @override Widget build(BuildContext context) { return Scaffold( body: Padding( padding: const EdgeInsets.all(16.0), child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ TextField(controller: _emailController, hintText: "邮箱"), TextField(controller: _passwordController, hintText: "密码", obscureText: true), Row( children: [ Checkbox( value: _keepLoggedIn, onChanged: (val) => setState(() => _keepLoggedIn = val!), ), const Text("保持登录状态"), ], ), if (_keepLoggedIn) DropdownButton<int>( value: _selectedDuration, hint: const Text("选择有效期"), items: _durationOptions.map((opt) { return DropdownMenuItem( value: opt["value"] as int, child: Text(opt["label"] as String), ); }).toList(), onChanged: (val) => setState(() => _selectedDuration = val), ), ElevatedButton( onPressed: _handleLogin, child: const Text("登录"), ), ], ), ), ); } // 登录逻辑后续实现 Future<void> _handleLogin() async { // ... } }
3. 登录成功后的时长处理
用户登录成功后,将选择的有效期、令牌相关信息加密存储到本地:
import 'package:amplify_auth_cognito/amplify_auth_cognito.dart'; import 'package:amplify_flutter/amplify_flutter.dart'; import 'package:jwt_decode/jwt_decode.dart'; Future<void> _handleLogin() async { try { // 调用Amplify登录 final result = await Amplify.Auth.signIn( username: _emailController.text, password: _passwordController.text, ); if (result.isSignedIn && _keepLoggedIn && _selectedDuration != null) { final storage = const FlutterSecureStorage(); // 获取当前认证会话 final session = await Amplify.Auth.fetchAuthSession() as CognitoAuthSession; // 计算用户设置的总过期时间:当前时间 + 所选时长 final userExpTime = DateTime.now().add(Duration(milliseconds: _selectedDuration!)); // 存储关键信息(加密存储) await storage.write(key: "user_exp_time", value: userExpTime.toIso8601String()); await storage.write(key: "selected_duration", value: _selectedDuration.toString()); // 存储Refresh Token用于后续刷新 final refreshToken = session.userPoolTokens?.refreshToken ?? ""; await storage.write(key: "refresh_token", value: refreshToken); } // 登录成功跳转主页 if (mounted) { Navigator.pushReplacementNamed(context, "/home"); } } on AuthException catch (e) { ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text(e.message))); } }
4. 应用启动时的自动登录校验
在应用初始化时(比如首页initState),检查本地存储的有效期和令牌状态:
Future<void> checkAutoLogin() async { final storage = const FlutterSecureStorage(); final userExpTimeStr = await storage.read(key: "user_exp_time"); if (userExpTimeStr == null) { // 无存储信息,跳转登录页 Navigator.pushReplacementNamed(context, "/login"); return; } final userExpTime = DateTime.parse(userExpTimeStr); if (DateTime.now().isAfter(userExpTime)) { // 超过用户设置的有效期,清除存储并跳转登录 await storage.deleteAll(); await Amplify.Auth.signOut(); Navigator.pushReplacementNamed(context, "/login"); return; } try { // 检查当前会话是否有效 final session = await Amplify.Auth.fetchAuthSession() as CognitoAuthSession; if (session.isSignedIn) { // 会话有效,直接跳转主页 Navigator.pushReplacementNamed(context, "/home"); } else { // 会话过期,尝试用Refresh Token刷新 final refreshToken = await storage.read(key: "refresh_token"); if (refreshToken != null) { final result = await Amplify.Auth.refreshSession( CognitoRefreshSession(refreshToken: CognitoRefreshToken(refreshToken)), ); if (result.isSignedIn) { Navigator.pushReplacementNamed(context, "/home"); } else { // 刷新失败,清除存储 await storage.deleteAll(); Navigator.pushReplacementNamed(context, "/login"); } } else { Navigator.pushReplacementNamed(context, "/login"); } } } on AuthException catch (_) { // 认证异常,跳转登录 await storage.deleteAll(); await Amplify.Auth.signOut(); Navigator.pushReplacementNamed(context, "/login"); } }
5. 关键注意事项
- 敏感信息存储:必须用
flutter_secure_storage加密存储令牌和过期时间,禁止用SharedPreferences(明文存储不安全) - Cognito Refresh Token限制:用户选择的“最大允许时长”不能超过Cognito用户池设置的Refresh Token过期时间,否则Refresh Token过期后无法自动刷新
- 退出登录清理:用户主动退出时,要清除本地所有存储信息并调用
Amplify.Auth.signOut() - 令牌过期判断:ID Token和Access Token默认1小时过期,需依赖Refresh Token自动刷新,Amplify会自动处理部分逻辑,但手动校验更稳妥
内容的提问来源于stack exchange,提问作者GingerAle
相关产品推荐
相关产品推荐

