Flutter中Token过期未自动跳转登录页问题求助
问题根源
当前启动页仅判断token是否存在,未验证其有效性;进入主页后也没有后续校验机制,导致过期token仍能留在主页。此外登录按钮存在多余的token清空代码,可能引发异常。
解决方案
1. 修复登录按钮的冗余代码
删除登录按钮中提前清空token的代码,避免登录失败时丢失原有有效token:
// 删掉以下两行冗余代码 // final prefs = await SharedPreferences.getInstance(); // prefs.setString(Constant.token, '');
2. 启动页添加token有效性校验
添加token有效性验证逻辑(以JWT为例,可解析过期时间;或调用后端校验接口):
第一步:添加JWT解析工具函数
import 'dart:convert'; import 'dart:typed_data'; bool isTokenValid(String token) { try { // 拆分JWT的三个部分 final parts = token.split('.'); if (parts.length != 3) return false; // 解码payload部分 final normalizedPayload = base64Url.normalize(parts[1]); final payloadBytes = base64Url.decode(normalizedPayload); final payload = jsonDecode(utf8.decode(payloadBytes)) as Map<String, dynamic>; // 获取过期时间戳(单位:秒) final expTimestamp = payload['exp'] as int?; if (expTimestamp == null) return false; // 对比当前时间与过期时间 final currentTime = DateTime.now().millisecondsSinceEpoch ~/ 1000; return currentTime < expTimestamp; } catch (e) { // 解析失败则视为无效token return false; } }
第二步:修改启动页跳转逻辑
Future<void> toLogin() async { Timer( const Duration(seconds: 3), () async { SharedPreferences prefs = await SharedPreferences.getInstance(); String? token = prefs.getString(Constant.token); bool isTokenActive = false; if (token != null) { // 验证token有效性 isTokenActive = isTokenValid(token); // 若后端提供校验接口,可替换为接口调用: // isTokenActive = await userProvider.validateToken(token); } Navigator.pushReplacementNamed( context, (token != null && isTokenActive) ? AppRoute.mainRoute : AppRoute.loginRoute, arguments: token, ); }, ); }
3. 全局API拦截处理token过期
封装HTTP请求,统一捕获后端返回的token过期状态(如401状态码),自动清除token并跳转登录:
第一步:创建全局Navigator Key
在项目入口处定义全局导航键,确保在无context时也能跳转页面:
final GlobalKey<NavigatorState> navigatorKey = GlobalKey<NavigatorState>(); // 在MaterialApp中配置 MaterialApp( navigatorKey: navigatorKey, // ...其他配置 );
第二步:封装HTTP请求工具类
import 'package:http/http.dart' as http; import 'package:shared_preferences/shared_preferences.dart'; class HttpUtil { static Future<http.Response> request( String method, String url, { Map<String, String>? headers, dynamic body, }) async { SharedPreferences prefs = await SharedPreferences.getInstance(); String? token = prefs.getString(Constant.token); // 添加Authorization头 headers ??= {}; if (token != null) { headers['Authorization'] = 'Bearer $token'; } http.Response response; switch (method.toLowerCase()) { case 'post': response = await http.post(Uri.parse(url), headers: headers, body: body); break; case 'get': response = await http.get(Uri.parse(url), headers: headers); break; // 可扩展其他请求方法 default: throw Exception('Unsupported request method'); } // 处理401(token过期/无效) if (response.statusCode == 401) { await prefs.remove(Constant.token); // 跳转登录页并清除所有栈页面 navigatorKey.currentState?.pushNamedAndRemoveUntil( AppRoute.loginRoute, (route) => false, ); } return response; } }
第三步:替换原有API调用
将登录之外的API请求替换为封装后的工具类,例如主页的接口调用:
// 示例:获取用户信息 Future<User?> getUserInfo() async { try { final response = await HttpUtil.request('get', '${Constant.baseURL}/user_info'); if (response.statusCode == 200) { return User.fromJson(jsonDecode(response.body)); } return null; } catch (e) { print(e); return null; } }
4. 主页初始化时校验token(可选)
在主页的initState中添加token校验,确保进入主页时token仍有效:
@override void initState() { super.initState(); _verifyTokenOnEntry(); } Future<void> _verifyTokenOnEntry() async { SharedPreferences prefs = await SharedPreferences.getInstance(); String? token = prefs.getString(Constant.token); if (token != null && !isTokenValid(token)) { await prefs.remove(Constant.token); Navigator.pushNamedAndRemoveUntil( context, AppRoute.loginRoute, (route) => false, ); } }
内容的提问来源于stack exchange,提问作者sharon
相关产品推荐
相关产品推荐

