Flutter U2F认证插件u2f的使用
Flutter U2F认证插件u2f的使用
FIDO通用第二因素
此包支持iOS、Android、Windows、Linux、macOS和Web上的NFC、USB和Webauthn Fido2密钥。
开始使用
注册
const u2f = U2fV2();
return await u2f.register(
challenge: '一些随机数据',
appId: 'example.com',
);
验证
const u2f = U2fV2();
return await u2f.authenticate(
challenge: '一些随机数据',
appId: 'example.com',
keyHandles: [
// ... 一个已注册的key handles列表
],
);
设置
请遵循flutter_nfc_kit包的设置部分。
在iOS上,你的新Info.plist
行应该看起来像这样:
<key>NFCReaderUsageDescription</key>
<string>使用NFC与安全设备进行身份验证</string>
<key>com.apple.developer.nfc.readersession.iso7816.select-identifiers</key>
<array>
<string>A000000308</string>
<string>A0000005272101</string>
<string>A000000527471117</string>
<string>A0000006472F0001</string>
</array>
完整示例代码
以下是一个完整的示例代码,展示了如何使用flutter_u2f
插件进行注册和验证。
// 忽略对于文件:避免打印
// 忽略对于文件:实现导入
import 'dart:async';
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:logging/logging.dart';
import 'package:u2f/hid.dart';
import 'package:u2f/u2f.dart';
void main() {
Logger.root.level = Level.ALL;
Logger.root.onRecord.listen((record) {
print('${record.level.name}: ${record.time}: ${record.message}');
});
const u2f = U2fV2();
runApp(const App(u2f: u2f));
}
class App extends StatelessWidget {
const App({super.key, required this.u2f});
final U2fV2 u2f;
[@override](/user/override)
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: const Text('FIDO2插件示例应用'),
),
body: Row(
children: [
Flexible(child: HidDemo(u2f: u2f)),
const VerticalDivider(),
Flexible(
flex: 2,
child: U2fDemo(
u2f: u2f,
challenge: 'F_YaN22CtYQPkmFiEF9a3Q',
appId: 'localhost',
),
),
],
),
),
);
}
}
class U2fDemo extends StatefulWidget {
const U2fDemo({
super.key,
required this.u2f,
required this.challenge,
required this.appId,
});
final U2fV2 u2f;
final String challenge;
final String appId;
[@override](/user/override)
State<U2fDemo> createState() => _U2fDemoState();
}
class _U2fDemoState extends State<U2fDemo> {
U2fRegistration? registration;
int? counter;
String? error;
[@override](/user/override)
Widget build(BuildContext context) {
return Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
Text('注册信息: ${registration?.keyHandle}'),
OutlinedButton(
onPressed: _enroll,
child: const Text('注册'),
),
if (counter != null) Text('计数器: $counter'),
if (error != null)
Text(
'错误: $error',
style: const TextStyle(color: Colors.red),
),
OutlinedButton(
onPressed: registration != null ? _verify : null,
child: const Text('验证'),
),
],
),
);
}
Future<String> _getMessage() async {
final methods = await widget.u2f.checkAvailability();
final text = StringBuffer('请 ');
for (final m in methods) {
switch (m) {
case U2fV2Methods.nfc:
text.writeln('扫描您的安全密钥');
break;
case U2fV2Methods.hid:
text.writeln('按下您的安全密钥上的按钮');
break;
case U2fV2Methods.webauthn:
text.writeln('按下您的安全密钥上的按钮');
break;
}
}
return text.toString();
}
Future<void> _enroll() async {
setState(() {
error = null;
});
try {
final result = await progress<U2fRegistration?>(
text: Text(await _getMessage()),
result: () async {
final registration = await widget.u2f.register(
challenge: widget.challenge,
appId: widget.appId,
);
return registration;
}(),
);
if (result == null) {
print('错误');
return;
}
print('注册数据: ${base64.encode(result.registrationData)}');
print('客户端数据: ${base64.encode(result.clientData)}');
try {
print(
'验证: ${result.verifySignature(result.certificatePublicKey)}',
);
} catch (e) {
print('验证: $e');
}
setState(() {
registration = result;
});
} catch (e) {
setState(() {
error = e.toString();
});
}
}
Future<void> _verify() async {
setState(() {
error = null;
});
try {
final result = await progress<U2fSignature?>(
text: Text(await _getMessage()),
result: () async {
final signature = await widget.u2f.authenticate(
challenge: widget.challenge,
appId: widget.appId,
keyHandles: [registration!.keyHandle],
);
return signature;
}(),
);
if (result == null) {
print('错误');
return;
}
print('客户端数据: ${base64.encode(result.clientData)}');
print('签名数据: ${base64.encode(result.signatureData)}');
print('验证: ${result.verifySignature(registration!.userPublicKey)}');
setState(() {
counter = result.counter;
});
} catch (e) {
setState(() {
error = e.toString();
});
}
}
Future<T?> progress<T>({
required Future<T?> result,
Widget? text,
}) async {
final innerContext = Completer<BuildContext>();
showDialog<void>(
context: context,
barrierDismissible: false,
builder: (BuildContext context) {
innerContext.complete(context);
return AlertDialog(
title: const Text('U2F'),
content: Column(
mainAxisSize: MainAxisSize.min,
children: [
if (text != null) text,
const CircularProgressIndicator(),
],
),
);
},
);
try {
return await result;
} finally {
final dialogContext = await innerContext.future;
if (dialogContext.mounted) {
Navigator.pop(dialogContext);
}
}
}
}
class HidDemo extends StatefulWidget {
const HidDemo({super.key, required this.u2f});
final U2fV2 u2f;
[@override](/user/override)
State<HidDemo> createState() => _HidDemoState();
}
class _HidDemoState extends State<HidDemo> {
List<HidDevice> _hidDevices = const [];
late Timer _timer;
Set<U2fV2Methods> _methods = const {};
[@override](/user/override)
void initState() {
super.initState();
_listDevices();
_timer = Timer.periodic(const Duration(seconds: 1), (timer) {
_listDevices();
});
}
[@override](/user/override)
void dispose() {
_timer.cancel();
super.dispose();
}
Future<void> _listDevices() async {
_methods = await widget.u2f.checkAvailability();
if (_methods.contains(U2fV2Methods.hid)) {
_hidDevices = (await hid.getDeviceList())
.where((e) => e.usagePage == 0xf1d0)
.toList()
..sort((a, b) => a.usage?.compareTo(b.usage ?? 0) ?? 0)
..sort((a, b) => a.usagePage?.compareTo(b.usagePage ?? 0) ?? 0)
..sort((a, b) => a.productId.compareTo(b.productId))
..sort((a, b) => a.vendorId.compareTo(b.vendorId))
..sort((a, b) => a.productName.compareTo(b.productName));
}
if (!mounted) return;
setState(() {});
}
[@override](/user/override)
Widget build(BuildContext context) {
return ListView(
children: [
if (_methods.contains(U2fV2Methods.nfc))
const ListTile(
leading: Icon(Icons.nfc),
title: Text('近场通信'),
),
if (_methods.contains(U2fV2Methods.webauthn))
const ListTile(
leading: Icon(Icons.security),
title: Text('WebAuthn'),
),
for (final device in _hidDevices)
ListTile(
leading: const Icon(Icons.usb),
title: Text(device.productName),
subtitle: Text(
'${device.vendorId.toRadixString(16).padLeft(4, '0')}:${device.productId.toRadixString(16).padLeft(4, '0')} ${device.serialNumber}',
),
),
],
);
}
}
更多关于Flutter U2F认证插件u2f的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html
1 回复