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

如何在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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.10 10:22:20