Flutter Keycloak认证管理插件keycloak_wrapper的使用

发布于 1周前 作者 eggper 来自 Flutter

Flutter Keycloak认证管理插件keycloak_wrapper的使用

Keycloak Wrapper

pub package likes pub points popularity style: lint

通过这个插件,可以无缝地将 Keycloak Single Sign-On (SSO) 认证集成到您的Flutter应用程序中。该插件自动管理令牌,并提供一个用户认证状态流,使应用程序能够监听以保持与认证状态的变化同步。

Getting Started

关键点

注意事项

  • AndroidX 是必需的。
  • 从 Android API 28 和 iOS 9 开始,默认禁用不安全的 HTTP 连接。如果需要允许明文连接,请参阅相关指南,但建议尽可能使用安全连接。

Keycloak 配置

前往您的 Keycloak Administration Console 并选择您的 Client ID,在访问设置部分插入 <bundle_identifier>:/ 作为有效的重定向 URI 和登出重定向 URI。

Android 配置

build.gradle 文件中指定自定义方案:

android {
    ...
    defaultConfig {
        ...
        manifestPlaceholders += [
                'appAuthRedirectScheme': '<package_name>'
        ]
    }
}

确保 <package_name> 全为小写,且不要包含任何不允许出现在主机名中的字符。

iOS/macOS 配置

Info.plist 中指定自定义方案:

<key>CFBundleURLTypes</key>
<array>
    <dict>
        <key>CFBundleTypeRole</key>
        <string>Editor</string>
        <key>CFBundleURLSchemes</key>
        <array>
            <string><bundle_identifier></string>
        </array>
    </dict>
</array>

Usage 示例代码

以下是一个完整的示例,展示了如何使用 keycloak_wrapper 插件:

import 'package:flutter/material.dart';
import 'package:keycloak_wrapper/keycloak_wrapper.dart';

final keycloakConfig = KeycloakConfig(
  bundleIdentifier: 'com.example.demo',
  clientId: '<client_id>',
  frontendUrl: '<frontend_url>',
  realm: '<realm>',
);
final keycloakWrapper = KeycloakWrapper(config: keycloakConfig);
final scaffoldMessengerKey = GlobalKey<ScaffoldMessengerState>();

void main() {
  WidgetsFlutterBinding.ensureInitialized();
  // 初始化插件
  keycloakWrapper.initialize();
  // 监听插件捕获的错误
  keycloakWrapper.onError = (message, _, __) {
    // 在snackbar中显示错误信息
    scaffoldMessengerKey.currentState
      ?..hideCurrentSnackBar()
      ..showSnackBar(SnackBar(content: Text(message)));
  };
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) => MaterialApp(
        scaffoldMessengerKey: scaffoldMessengerKey,
        // 监听用户认证状态流
        home: StreamBuilder<bool>(
          initialData: false,
          stream: keycloakWrapper.authenticationStream,
          builder: (context, snapshot) {
            if (snapshot.connectionState == ConnectionState.waiting) {
              return const LoadingScreen();
            } else if (snapshot.data!) {
              return const HomeScreen();
            } else {
              return const LoginScreen();
            }
          },
        ),
      );
}

class LoadingScreen extends StatelessWidget {
  const LoadingScreen({super.key});

  @override
  Widget build(BuildContext context) => const Scaffold(
        body: Center(
          child: CircularProgressIndicator.adaptive(),
        ),
      );
}

class LoginScreen extends StatelessWidget {
  const LoginScreen({super.key});

  // 登录
  Future<void> login() async {
    final isLoggedIn = await keycloakWrapper.login();

    if (isLoggedIn) debugPrint('User has successfully logged in.');
  }

  @override
  Widget build(BuildContext context) => Scaffold(
        body: Center(
          child: FilledButton(onPressed: login, child: const Text('Login')),
        ),
      );
}

class HomeScreen extends StatelessWidget {
  const HomeScreen({super.key});

  // 登出
  Future<void> logout() async {
    final isLoggedOut = await keycloakWrapper.logout();

    if (isLoggedOut) debugPrint('User has successfully logged out.');
  }

  @override
  Widget build(BuildContext context) => Scaffold(
        body: Center(
          child: Column(
            mainAxisSize: MainAxisSize.min,
            children: [
              FutureBuilder(
                future: keycloakWrapper.getUserInfo(),
                builder: (context, snapshot) {
                  final userInfo = snapshot.data ?? {};

                  return Column(children: [
                    ...userInfo.entries.map((entry) => Text('${entry.key}: ${entry.value}')),
                    if (userInfo.isNotEmpty) const SizedBox(height: 20),
                  ]);
                },
              ),
              FilledButton(onPressed: logout, child: const Text('Logout')),
            ],
          ),
        ),
      );
}

此示例展示了如何配置和使用 keycloak_wrapper 插件进行登录、获取用户信息、以及登出操作。请根据您的实际需求替换配置项中的占位符(如 <client_id><frontend_url> 等)。


更多关于Flutter Keycloak认证管理插件keycloak_wrapper的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html

1 回复

更多关于Flutter Keycloak认证管理插件keycloak_wrapper的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html


当然,以下是如何在Flutter项目中使用keycloak_wrapper插件进行Keycloak认证管理的示例代码。这个插件通常用于与Keycloak服务器进行集成,以实现用户认证和授权。

首先,确保你已经将keycloak_wrapper插件添加到你的Flutter项目中。在你的pubspec.yaml文件中添加以下依赖项:

dependencies:
  flutter:
    sdk: flutter
  keycloak_wrapper: ^最新版本号  # 替换为实际最新版本号

然后,运行flutter pub get以安装依赖项。

示例代码

以下是一个基本的Flutter应用示例,展示了如何使用keycloak_wrapper插件进行Keycloak认证。

1. 初始化Keycloak配置

创建一个新的Dart文件,比如keycloak_config.dart,用于存储Keycloak服务器的配置信息。

// keycloak_config.dart
import 'package:keycloak_wrapper/keycloak_wrapper.dart';

class KeycloakConfig {
  static final String realm = "your-realm";
  static final String clientId = "your-client-id";
  static final String url = "https://your-keycloak-server/auth";
  
  static final KeycloakConfig instance = KeycloakConfig._();
  
  KeycloakConfig._();
  
  Keycloak getKeycloak() {
    return Keycloak(
      realm: realm,
      clientId: clientId,
      url: url,
    );
  }
}

2. 认证流程

在你的主应用文件(通常是main.dart)中,使用keycloak_wrapper插件进行认证。

// main.dart
import 'package:flutter/material.dart';
import 'package:keycloak_wrapper/keycloak_wrapper.dart';
import 'keycloak_config.dart';

void main() {
  runApp(MyApp());
}

class MyApp extends StatefulWidget {
  @override
  _MyAppState createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
  Keycloak? keycloak;
  bool isAuthenticated = false;

  @override
  void initState() {
    super.initState();
    keycloak = KeycloakConfig.instance.getKeycloak();
    
    // 尝试从存储中获取现有的token
    keycloak!.onInit().then((isAuthenticated) {
      setState(() {
        this.isAuthenticated = isAuthenticated;
      });
    });
  }

  void login() async {
    try {
      bool authenticated = await keycloak!.login();
      setState(() {
        isAuthenticated = authenticated;
      });
    } catch (e) {
      print("Login failed: $e");
    }
  }

  void logout() async {
    try {
      bool loggedOut = await keycloak!.logout();
      setState(() {
        isAuthenticated = !loggedOut; // Logout should return true if successful
      });
    } catch (e) {
      print("Logout failed: $e");
    }
  }

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: Text('Keycloak Auth Example'),
        ),
        body: Center(
          child: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: <Widget>[
              Text('Authenticated: $isAuthenticated'),
              SizedBox(height: 20),
              ElevatedButton(
                onPressed: login,
                child: Text('Login'),
              ),
              SizedBox(height: 20),
              ElevatedButton(
                onPressed: logout,
                child: Text('Logout'),
              ),
            ],
          ),
        ),
      ),
    );
  }
}

说明

  1. KeycloakConfig:这是一个单例类,用于存储Keycloak服务器的配置信息,并返回一个配置好的Keycloak实例。

  2. MyApp:这是Flutter应用的主类。在initState方法中,我们初始化keycloak实例,并尝试从存储中获取现有的token。如果成功,将isAuthenticated设置为true

  3. loginlogout 方法:这两个方法分别用于登录和注销。它们调用keycloak实例的loginlogout方法,并更新isAuthenticated状态。

  4. UI:UI部分显示当前的认证状态,并提供登录和注销按钮。

这个示例展示了如何使用keycloak_wrapper插件进行基本的Keycloak认证管理。根据你的实际需求,你可能需要进一步扩展这个示例,比如处理认证后的用户信息、刷新token等。

回到顶部