Flutter凭证管理插件credential_manager的使用
Flutter凭证管理插件credential_manager的使用
Credential Manager
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
更多关于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
插件进行凭证管理。