You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.06 11:31:14