如何在Flutter项目(Go Router+Riverpod)中实现基于角色的访问控制
基于Go Router + Riverpod实现角色权限控制方案
1. 全局暴露用户状态
确保你的User模型状态已通过Riverpod Provider全局可监听,比如:
// 状态管理文件中定义 final currentUserProvider = StateProvider<User?>((ref) => null);
如果是从Firebase Auth异步获取用户数据,改用AsyncNotifierProvider即可,核心是让Go Router能拿到当前用户的角色信息。
2. 在Go Router中实现权限校验
在app_router.dart里,利用Go Router的redirect回调结合Riverpod的ref做权限拦截,同时监听用户状态变化自动刷新路由:
完整改造示例
import 'package:go_router/go_router.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter/material.dart'; import 'package:your_app/models/user.dart'; import 'package:your_app/screens/filiais_screen.dart'; import 'package:your_app/screens/home_screen.dart'; import 'package:your_app/screens/login_screen.dart'; enum UserRole { admin, accountant } class AppRouter { static GoRouter createRouter(WidgetRef ref) { return GoRouter( initialLocation: '/login', // 全局路由拦截逻辑 redirect: (context, state) { final currentUser = ref.watch(currentUserProvider); // 未登录用户强制跳转到登录页 if (currentUser == null) { return state.location != '/login' ? '/login' : null; } // 权限校验:accountant角色禁止访问/filiais分支 final isAccountant = currentUser.role == UserRole.accountant; final isAccessingFiliais = state.location.startsWith('/filiais'); if (isAccountant && isAccessingFiliais) { return '/home'; // 重定向到允许访问的首页 } // 其他情况放行 return null; }, // 监听用户状态变化,自动刷新路由 refreshListenable: _UserRefreshListenable(ref), routes: [ GoRoute( path: '/login', builder: (context, state) => const LoginScreen(), ), GoRoute( path: '/home', builder: (context, state) => const HomeScreen(), ), GoRoute( path: '/filiais', builder: (context, state) => const FiliaisScreen(), // 子路由会继承父路由的权限拦截,无需额外配置 routes: [], ), // 其他路由定义... ], ); } } // 自定义监听类,用户状态变化时通知Go Router刷新 class _UserRefreshListenable extends ChangeNotifier { _UserRefreshListenable(WidgetRef ref) { // 监听用户状态流 _subscription = ref.watch(currentUserProvider.stream).listen((_) { notifyListeners(); }); } late final StreamSubscription<User?> _subscription; @override void dispose() { _subscription.cancel(); super.dispose(); } }
3. 在app.dart中传递Riverpod Ref
在ProviderScope范围内获取ref,并初始化Router:
import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:your_app/router/app_router.dart'; class App extends ConsumerWidget { const App({super.key}); @override Widget build(BuildContext context, WidgetRef ref) { final router = AppRouter.createRouter(ref); return MaterialApp.router( title: 'Your App', theme: ThemeData(primarySwatch: Colors.blue), routerConfig: router, ); } }
额外优化建议
- 把权限规则封装成单独函数,让拦截逻辑更清晰:
bool _hasPermission(User user, String location) { if (user.role == UserRole.admin) return true; if (user.role == UserRole.accountant) { return !location.startsWith('/filiais'); } return false; }
直接在redirect中调用_hasPermission(currentUser!, state.location)判断即可。
- 多角色多路由场景下,用配置Map管理权限:
final roleRouteAllowList = { UserRole.admin: ['*'], UserRole.accountant: ['/login', '/home', '/reports'], };
通过匹配当前路由是否在允许列表中来做校验。
内容的提问来源于stack exchange,提问作者user3780425
相关产品推荐
相关产品推荐

