如何在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
相关产品推荐
相关产品推荐

