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

如何在Flutter中结合JWT认证实现基于角色的路由?

Flutter 基于角色的路由配置(结合JWT)

核心步骤拆解


1. 从本地存储解析JWT获取用户角色

先封装工具方法,读取本地存储的JWT令牌,解析出用户角色,同时校验令牌有效性:

import 'package:shared_preferences/shared_preferences.dart';
import 'package:jwt_decoder/jwt_decoder.dart';

// 获取用户角色的工具方法
Future<String?> getUserRole() async {
  final prefs = await SharedPreferences.getInstance();
  final token = prefs.getString('auth_token');
  
  if (token == null || JWTDecoder.isExpired(token)) {
    // 令牌不存在或过期,返回null
    return null;
  }
  
  // 解析JWT payload,假设角色字段为'role',根据实际情况调整
  final decodedToken = JWTDecoder.decode(token);
  return decodedToken['role']?.toString();
}

2. 拆分角色专属路由到独立文件

把不同角色的路由逻辑拆分到对应文件,保持主路由文件简洁:

admin_routes.dart(管理员专属路由):

import 'package:flutter/material.dart';
import 'package:stockhive_mobile/screen/admin/users/userscreen.dart';
import 'package:stockhive_mobile/screen/admin/users/adduser.dart';
import 'package:stockhive_mobile/screen/admin/departements/Departmentad.dart';
import 'package:stockhive_mobile/screen/admin/departements/adddepartementscreen.dart';

class AdminRoutes {
  static const String usersRoute = '/usersad';
  static const String adduserRoute = '/adduser';
  static const String departmentsRoute = '/department';
  static const String adddepartmentRoute = '/adddepartment';

  static Route<dynamic>? generateAdminRoute(RouteSettings settings) {
    switch (settings.name) {
      case usersRoute:
        return MaterialPageRoute(builder: (_) => UsersManagementPage());
      case adduserRoute:
        return MaterialPageRoute(builder: (_) => AddUser());
      case departmentsRoute:
        return MaterialPageRoute(builder: (_) => DepartmentManagementPage());
      case adddepartmentRoute:
        return MaterialPageRoute(builder: (_) => AddDepartment());
      default:
        return null; // 非管理员路由返回null,交给主路由处理
    }
  }
}

collaborator_routes.dart(普通用户专属路由):

import 'package:flutter/material.dart';
// 导入普通用户对应的页面组件

class CollaboratorRoutes {
  static const String materielsRoute = '/materiel';
  static const String addmaterielRoute = '/addmateriel';

  static Route<dynamic>? generateCollaboratorRoute(RouteSettings settings) {
    switch (settings.name) {
      case materielsRoute:
        // return MaterialPageRoute(builder: (_) => MaterielPage());
        return null; // 替换为你的实际页面组件
      case addmaterielRoute:
        // return MaterialPageRoute(builder: (_) => AddMaterielPage());
        return null;
      default:
        return null;
    }
  }
}

3. 改造主路由添加角色校验逻辑

修改AppRouter的generateRoute方法,在处理路由前校验用户角色,根据角色分配访问权限:

import 'package:flutter/material.dart';
import 'package:stockhive_mobile/screen/auth/introduction_page.dart';
import 'package:stockhive_mobile/screen/auth/login_page.dart';
import 'package:stockhive_mobile/screen/auth/resetpassword.dart';
import 'package:stockhive_mobile/screen/auth/otp.dart';
import 'package:stockhive_mobile/screen/auth/newpassword.dart';
import 'package:stockhive_mobile/screen/auth/editprofile.dart';
import 'package:stockhive_mobile/screen/auth/onboarding.dart';
import 'package:stockhive_mobile/screen/auth/viewprofile.dart';
import 'package:stockhive_mobile/splash/splash_screen.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:jwt_decoder/jwt_decoder.dart';
import 'admin_routes.dart';
import 'collaborator_routes.dart';

class AppRouter {
  static const String introRoute = '/intro';
  static const String loginRoute = '/login';
  static const String onboardingRoute = '/onboarding';
  static const String resetPasswordRoute = '/forgetpassword';
  static const String otpRoute = '/otp';
  static const String newPasswordRoute = '/newpassword';
  static const String editProfileRoute = '/editprofile';
  static const String viewProfileRoute = '/viewprofile';
  static const String splashRoute = '/splash';

  // 同步获取用户角色
  static Future<String?> _getUserRole() async {
    final prefs = await SharedPreferences.getInstance();
    final token = prefs.getString('auth_token');
    if (token == null || JWTDecoder.isExpired(token)) return null;
    final decoded = JWTDecoder.decode(token);
    return decoded['role']?.toString();
  }

  static Route<dynamic> generateRoute(RouteSettings settings) async {
    print('🔍 Navigation vers : ${settings.name} avec arguments : ${settings.arguments}');
    
    // 获取当前用户角色
    final userRole = await _getUserRole();

    // 处理无需权限的公共路由
    switch (settings.name) {
      case introRoute:
        return MaterialPageRoute(builder: (_) => Introduction_page());
      case onboardingRoute:
        return MaterialPageRoute(builder: (_) => IntroScreen());
      case resetPasswordRoute:
        return MaterialPageRoute(builder: (_) => ResetPassword());
      case otpRoute:
        if (settings.arguments is String) {
          return MaterialPageRoute(
              builder: (_) => OtpScreen(email: settings.arguments as String));
        }
        return _errorRoute();
      case newPasswordRoute:
        if (settings.arguments is Map<String, String>) {
          final args = settings.arguments as Map<String, String>;
          return MaterialPageRoute(
              builder: (_) => Newpassword(email: args['email']!, otp: args['otp']!));
        }
        return _errorRoute();
      case splashRoute:
        return MaterialPageRoute(builder: (_) => const SplashScreen());
      case loginRoute:
        return MaterialPageRoute(builder: (_) => LoginPage());
      case editProfileRoute:
        if (settings.arguments is Map<String, dynamic>) {
          return MaterialPageRoute(
              builder: (_) => EditProfilePage(userData: settings.arguments as Map<String, dynamic>));
        }
        return _errorRoute();
      case viewProfileRoute:
        return MaterialPageRoute(builder: (_) => ViewProfilePage());
    }

    // 处理权限路由:管理员专属
    if (userRole == 'admin') {
      final adminRoute = AdminRoutes.generateAdminRoute(settings);
      if (adminRoute != null) {
        return adminRoute;
      }
    }

    // 处理权限路由:普通用户专属
    if (userRole == 'collaborator') {
      final collaboratorRoute = CollaboratorRoutes.generateCollaboratorRoute(settings);
      if (collaboratorRoute != null) {
        return collaboratorRoute;
      }
    }

    // 无权限或路由不存在,跳转到登录页
    return MaterialPageRoute(builder: (_) => LoginPage());
    // 也可返回错误页:return _errorRoute();
  }

  static Route<dynamic> _errorRoute() {
    print('❌ Erreur de navigation : Route non trouvée');
    return MaterialPageRoute(
      builder: (_) => Scaffold(
        appBar: AppBar(title: Text("Page introuvable")),
        body: Center(child: Text("Erreur 404 - Page introuvable")),
      ),
    );
  }
}

4. 额外注意事项

  • 令牌过期处理:在_getUserRole中已判断令牌过期,此时可以清除本地存储的过期令牌并直接跳转到登录页。
  • 角色字段匹配:确保JWT payload中的角色字段名与代码中使用的一致(示例用'role',需根据实际JWT结构调整)。
  • 全局路由守卫:如果需要更全面的权限控制,可以结合MaterialApp的onGenerateRoute和navigatorObservers实现全局拦截。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.13 15:24:53