Flutter凭证管理插件credential_manager的使用

Flutter凭证管理插件credential_manager的使用

Credential Manager

Android iOS

Credential Manager 是一个Jetpack API,支持多种登录方式,如用户名和密码、passkeys以及联合登录解决方案(如Google登录),在单个API中简化了Android开发者的集成。对于iOS,它使用Keychain来存储凭证。对于用户来说,Credential Manager统一了不同认证方法的登录界面,使用户能够更清晰、更方便地登录应用,无论选择哪种方法。

Getting Started

添加依赖到您的pubspec.yaml文件:

dependencies:
  credential_manager: <latest_version>

或者运行:

flutter pub add credential_manager

文档

请访问我们的 文档页面 查看综合指南和详细文档,包括API参考、使用示例以及将Credential Manager集成到应用程序的最佳实践。

示例代码

以下是一个完整的示例demo,展示了如何使用credential_manager插件进行注册、登录等操作。

main.dart

import 'dart:async';
import 'package:credential_manager/credential_manager.dart';
import 'package:flutter/material.dart';

const String googleClientId = ""; // Your Google Client ID here
const String rpId = "your_rp_id_here"; // Replace with your Relying Party ID
final CredentialManager credentialManager = CredentialManager();

void main() async {
  WidgetsFlutterBinding.ensureInitialized();

  if (credentialManager.isSupportedPlatform) {
    await credentialManager.init(
      preferImmediatelyAvailableCredentials: true,
      googleClientId: googleClientId.isNotEmpty ? googleClientId : null,
    );
  }

  runApp(const MyApp());
}

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

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: "Credential Manager Example",
      debugShowCheckedModeBanner: false,
      theme: ThemeData(useMaterial3: true),
      home: const LoginScreen(),
    );
  }
}

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

  @override
  State<LoginScreen> createState() => _LoginScreenState();
}

class _LoginScreenState extends State<LoginScreen> {
  final GlobalKey<FormState> _formKey = GlobalKey<FormState>();
  bool isLoading = false;
  bool createPassKey = false;
  String? username;
  String? password;
  late CredentialLoginOptions passKeyLoginOption;

  @override
  void initState() {
    super.initState();
    passKeyLoginOption = CredentialLoginOptions(
      challenge: "HjBbH__fbLuzy95AGR31yEARA0EMtKlY0NrV5oy3NQw",
      rpId: rpId,
      userVerification: "required",
      conditionalUI: false,
    );
  }

  Widget _buildAutofillGroup(Widget child) {
    if (enableInlineAutofill) {
      return AutofillGroup(
        child: child,
      );
    }
    return child;
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text("Credentials Manager")),
      body: Stack(
        children: [
          AbsorbPointer(
            absorbing: isLoading,
            child: Opacity(
              opacity: isLoading ? 0.5 : 1,
              child: _buildAutofillGroup(
                Form(
                  key: _formKey,
                  child: Column(
                    mainAxisAlignment: MainAxisAlignment.center,
                    children: [
                      _buildInputField("Username", (value) => username = value),
                      if (createPassKey)
                        _buildInputField(
                          "Password", 
                          (value) => password = value, 
                          isPassword: true
                        ),
                      _buildButton("Register", onRegister),
                      _buildButton("Register with pass key", onRegisterWithPassKey),
                      if (Platform.isAndroid)
                        _buildButton("Register with Google Sign In", onGoogleSignIn),
                      if (Platform.isAndroid)
                        _buildButton("Login (Password, Passkey, Google)", onLogin)
                      else
                        _buildButton("Login Passkey", onLogin)
                    ],
                  ),
                ),
              ),
            ),
          ),
          if (isLoading)
            const Center(child: CircularProgressIndicator.adaptive()),
        ],
      ),
    );
  }

  bool enableInlineAutofill = Platform.isIOS;

  Widget _buildInputField(String hint, Function(String) onChanged, {bool isPassword = false}) {
    return Padding(
      padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 10),
      child: TextFormField(
        onChanged: onChanged,
        obscureText: isPassword,
        autofillHints: enableInlineAutofill
            ? (isPassword
                ? const [AutofillHints.password]
                : const [AutofillHints.username])
            : [],
        keyboardType: isPassword ? TextInputType.visiblePassword : null,
        validator: (value) => value!.isEmpty ? "Please enter a $hint" : null,
        decoration: InputDecoration(
          hintText: hint,
          border: OutlineInputBorder(
            borderRadius: BorderRadius.circular(16),
            borderSide: const BorderSide(color: Colors.blueAccent),
          ),
        ),
      ),
    );
  }

  Widget _buildButton(String label, VoidCallback onPressed) {
    return MaterialButton(
      onPressed: onPressed,
      color: Colors.red,
      minWidth: MediaQuery.of(context).size.width / 2,
      height: 40,
      shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(16)),
      child: Text(label, style: const TextStyle(color: Colors.white, fontSize: 16)),
    );
  }

  Future<void> onRegister() async {
    if (_formKey.currentState!.validate()) {
      if (!createPassKey) {
        setState(() => createPassKey = true);
      } else {
        if (enableInlineAutofill) {
          _navigateToHomeScreen(Credential.password,
              passwordCredential: PasswordCredential(
                username: username,
                password: password,
              ));
          return;
        }

        await _performAction(() async {
          await credentialManager.savePasswordCredentials(
            PasswordCredential(username: username, password: password),
          );

          _showSnackBar("Successfully saved credential");
          _navigateToHomeScreen(Credential.password,
              passwordCredential: PasswordCredential(
                username: username,
                password: password,
              ));
        });
      }
    }
  }

  Future<void> onRegisterWithPassKey() async {
    if (_formKey.currentState!.validate()) {
      await _performAction(() async {
        final credentialCreationOptions = {
          "challenge": "HjBbH__fbLuzy95AGR31yEARA0EMtKlY0NrV5oy3NQw",
          "rp": {"name": "CredMan App Test", "id": rpId},
          "user": {
            "id": EncryptData.getEncodedUserId(),
            "name": username,
            "displayName": username,
          },
          "excludeCredentials": [
            {"id": "ghi789", "type": "public-key"},
            {"id": "jkl012", "type": "public-key"}
          ],
        };

        if (Platform.isAndroid) {
          credentialCreationOptions.addAll({
            "pubKeyCredParams": [
              {"type": "public-key", "alg": -7},
              {"type": "public-key", "alg": -257}
            ],
            "timeout": 1800000,
            "attestation": "none",
            "authenticatorSelection": {
              "authenticatorAttachment": "platform",
              "residentKey": "required",
              "userVerification": "required"
            }
          });
        }

        final res = await credentialManager.savePasskeyCredentials(
          request: CredentialCreationOptions.fromJson(credentialCreationOptions),
        );
        _showSnackBar("Successfully saved credential");
        _navigateToHomeScreen(Credential.passkey, publicKeyCredential: res);
      });
    }
  }

  Future<void> onGoogleSignIn() async {
    await _performAction(() async {
      final credential = await credentialManager.saveGoogleCredential();
      _showSnackBar("Successfully retrieved credential");
      _navigateToHomeScreen(Credential.google, googleIdTokenCredential: credential);
    });
  }

  Future<void> onLogin() async {
    await _performAction(() async {
      Credentials credential = await credentialManager.getCredentials(
        passKeyOption: passKeyLoginOption,
        fetchOptions: FetchOptionsAndroid(
          passKey: true,
          passwordCredential: true,
          googleCredential: true,
        ),
      );
      _showLoginSuccessDialog(credential);
    });
  }

  Future<void> _performAction(Future<void> Function() action) async {
    setState(() => isLoading = true);
    try {
      await action();
    } on CredentialException catch (e) {
      _showSnackBar(e.message.toString());
    } finally {
      if (mounted) setState(() => isLoading = false);
    }
  }

  void _showSnackBar(String message) {
    ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text(message)));
  }

  void _showLoginSuccessDialog(Credentials credential) {
    bool isPasswordBasedCredentials = credential.passwordCredential != null;
    bool isPublicKeyBasedCredentials = credential.publicKeyCredential != null;
    _showSnackBar("Successfully retrieved credential");

    _navigateToHomeScreen(
      isPasswordBasedCredentials
          ? Credential.password
          : isPublicKeyBasedCredentials
              ? Credential.passkey
              : Credential.google,
      googleIdTokenCredential: credential.googleIdTokenCredential,
      passwordCredential: credential.passwordCredential,
      publicKeyCredential: credential.publicKeyCredential,
    );
  }

  void _navigateToHomeScreen(Credential credentialType,
      {GoogleIdTokenCredential? googleIdTokenCredential,
      PasswordCredential? passwordCredential,
      PublicKeyCredential? publicKeyCredential}) {
    Navigator.push(
      context,
      MaterialPageRoute(
        builder: (context) => HomeScreen(
          credentialType: credentialType,
          passwordCredential: passwordCredential,
          publicKeyCredential: publicKeyCredential,
          googleIdTokenCredential: googleIdTokenCredential,
        ),
      ),
    );
  }
}

HomeScreen.dart

为了完整性,这里也提供了一个简单的HomeScreen实现,用于展示登录成功后的界面。

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

class HomeScreen extends StatelessWidget {
  final Credential credentialType;
  final PasswordCredential? passwordCredential;
  final PublicKeyCredential? publicKeyCredential;
  final GoogleIdTokenCredential? googleIdTokenCredential;

  const HomeScreen({
    Key? key,
    required this.credentialType,
    this.passwordCredential,
    this.publicKeyCredential,
    this.googleIdTokenCredential,
  }) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Home Screen'),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            Text(
              'Logged in successfully with ${_getCredentialTypeName()}',
              style: Theme.of(context).textTheme.headlineMedium,
            ),
            const SizedBox(height: 20),
            ElevatedButton(
              onPressed: () {
                Navigator.pop(context);
              },
              child: const Text('Logout'),
            ),
          ],
        ),
      ),
    );
  }

  String _getCredentialTypeName() {
    switch (credentialType) {
      case Credential.password:
        return 'Password';
      case Credential.passkey:
        return 'Passkey';
      case Credential.google:
        return 'Google';
      default:
        return 'Unknown';
    }
  }
}

这个示例展示了如何使用credential_manager插件进行用户凭证的管理和交互,包括注册、登录以及处理不同类型的凭证(如密码、passkey和Google登录)。希望这能帮助您更好地理解和使用该插件。


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

1 回复

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


当然,以下是如何在Flutter项目中使用credential_manager插件进行凭证管理的示例代码。credential_manager插件允许你安全地存储和检索用户凭证,如用户名和密码。

首先,确保你已经在pubspec.yaml文件中添加了credential_manager依赖:

dependencies:
  flutter:
    sdk: flutter
  credential_manager: ^x.y.z  # 请替换为最新版本号

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

接下来,我们编写一些示例代码来展示如何使用这个插件。

1. 导入必要的包

在你的Dart文件中(例如main.dart),导入credential_manager包:

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

2. 初始化CredentialManager

你需要一个CredentialManager实例来存储和检索凭证。

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

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: Text('Credential Manager Example'),
        ),
        body: CredentialManagerExample(),
      ),
    );
  }
}

class CredentialManagerExample extends StatefulWidget {
  @override
  _CredentialManagerExampleState createState() => _CredentialManagerExampleState();
}

class _CredentialManagerExampleState extends State<CredentialManagerExample> {
  late CredentialManager credentialManager;

  @override
  void initState() {
    super.initState();
    credentialManager = CredentialManager.getInstance();
  }

  // 示例函数:存储凭证
  Future<void> saveCredentials() async {
    String service = "example_service";
    String account = "user@example.com";
    String password = "securePassword123";

    try {
      await credentialManager.save(service, account, password);
      print("Credentials saved successfully.");
    } catch (e) {
      print("Failed to save credentials: $e");
    }
  }

  // 示例函数:检索凭证
  Future<void> retrieveCredentials() async {
    String service = "example_service";
    String account = "user@example.com";

    try {
      String? password = await credentialManager.retrieve(service, account);
      if (password != null) {
        print("Retrieved password: $password");
      } else {
        print("No credentials found for the given service and account.");
      }
    } catch (e) {
      print("Failed to retrieve credentials: $e");
    }
  }

  // 示例函数:删除凭证
  Future<void> deleteCredentials() async {
    String service = "example_service";
    String account = "user@example.com";

    try {
      await credentialManager.delete(service, account);
      print("Credentials deleted successfully.");
    } catch (e) {
      print("Failed to delete credentials: $e");
    }
  }

  @override
  Widget build(BuildContext context) {
    return Padding(
      padding: const EdgeInsets.all(16.0),
      child: Column(
        mainAxisAlignment: MainAxisAlignment.center,
        children: <Widget>[
          ElevatedButton(
            onPressed: saveCredentials,
            child: Text('Save Credentials'),
          ),
          ElevatedButton(
            onPressed: retrieveCredentials,
            child: Text('Retrieve Credentials'),
          ),
          ElevatedButton(
            onPressed: deleteCredentials,
            child: Text('Delete Credentials'),
          ),
        ],
      ),
    );
  }
}

3. 运行应用

现在,你可以运行你的Flutter应用,并测试凭证的存储、检索和删除功能。

注意事项

  • CredentialManager插件可能依赖于平台特定的实现(如Android的KeyStore和iOS的Keychain),因此确保在Android和iOS平台上都进行了适当的配置。
  • 在生产环境中,请确保凭证的存储和检索符合你的安全要求。

希望这个示例代码能帮助你理解如何在Flutter项目中使用credential_manager插件进行凭证管理。

回到顶部