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

Token过期/刷新时仅返回文本错误,如何实现自动登出跳转登录页?

问题:Token过期时未自动跳转登录页

当前问题:用户处于登录状态且Token过期时,应用未自动跳转至登录页,仅返回文本形式的错误,无跳转行为。

相关代码

调用API代码

class UserProvider with ChangeNotifier {
  late User _user;
  User get user => _user;

  set user(User newUser) {
    _user = newUser;
    notifyListeners();
  }

  Future<User?> login(String nim, String password) async {
    String url = Constant.baseURL;
    try {
      var body = {
        'username': nim,
        'password': password,
      };
      var response = await http.post(
        Uri.parse(
          '$url/login_mhs',
        ),
        body: body,
      );
      if (response.statusCode == 200) {
        final token = jsonDecode(response.body)['data']['access_token'];
        await UtilSharedPreferences.setToken(token);
        print(token);
        return User.fromJson(jsonDecode(response.body));
      } else {
        return null;
      }
    } catch (e) {
      print(e);
      throw Exception();
    }
  }
}

启动页(SplashScreen)代码

class _SplashScreenState extends State<SplashScreen> {
  Future<void> toLogin() async {
    Timer(
      const Duration(seconds: 3),
      () async {
        SharedPreferences prefs = await SharedPreferences.getInstance();
        String? token = prefs.getString(Constant.token);
        // ignore: use_build_context_synchronously
        Navigator.pushReplacementNamed(
          context,
          token != null ? AppRoute.mainRoute : AppRoute.loginRoute,
          arguments: token,
        );
      },
    );
  }

  @override
  void initState() {
    super.initState();
    toLogin();
  }
}

错误返回示例

文本形式的错误返回


解决方案

1. 全局拦截API请求,处理Token过期

创建HTTP拦截器,统一处理所有请求的响应,识别Token过期状态(通常为401状态码):

// 先定义全局导航键,用于在拦截器中获取上下文
final GlobalKey<NavigatorState> navigatorKey = GlobalKey<NavigatorState>();

class AuthInterceptor extends Interceptor {
  @override
  void onResponse(Response response, ResponseInterceptorHandler handler) {
    if (response.statusCode == 401) {
      // 清除本地过期Token
      UtilSharedPreferences.removeToken();
      // 跳转登录页
      navigatorKey.currentState?.pushReplacementNamed(AppRoute.loginRoute);
    }
    super.onResponse(response, handler);
  }
}

// 初始化Dio时挂载拦截器
final dio = Dio()..interceptors.add(AuthInterceptor());

2. 启动页增加Token有效性校验

当前启动页仅判断Token是否存在,未校验是否过期,新增校验逻辑:

Future<void> toLogin() async {
  Timer(
    const Duration(seconds: 3),
    () async {
      SharedPreferences prefs = await SharedPreferences.getInstance();
      String? token = prefs.getString(Constant.token);
      
      if (token != null) {
        // 调用后端校验Token接口
        bool isValid = await _checkToken(token);
        if (!isValid) {
          await prefs.remove(Constant.token);
          Navigator.pushReplacementNamed(context, AppRoute.loginRoute);
          return;
        }
        Navigator.pushReplacementNamed(context, AppRoute.mainRoute, arguments: token);
      } else {
        Navigator.pushReplacementNamed(context, AppRoute.loginRoute);
      }
    },
  );
}

// 校验Token有效性的方法
Future<bool> _checkToken(String token) async {
  try {
    final response = await http.get(
      Uri.parse('${Constant.baseURL}/validate_token'),
      headers: {'Authorization': 'Bearer $token'},
    );
    return response.statusCode == 200;
  } catch (e) {
    return false;
  }
}

3. 在状态管理中处理Token过期事件

在UserProvider中新增Token过期处理方法,供页面或请求失败时调用:

class UserProvider with ChangeNotifier {
  // ...原有代码...

  void handleTokenExpired() async {
    await UtilSharedPreferences.removeToken();
    notifyListeners();
    navigatorKey.currentState?.pushReplacementNamed(AppRoute.loginRoute);
  }
}

内容的提问来源于stack exchange,提问作者Meikisihka

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.28 15:18:11