Flutter网页认证插件authwebview的使用
Flutter网页认证插件authwebview的使用
AuthWebView
是一个用于在 Flutter 应用中处理 OAuth 认证流程的插件。该插件最初由 Rapider.ai 开发,并已作为开源解决方案提供给 Flutter 社区。
特性
- 支持 OAuth 2.0 认证流程并包含 PKCE(Proof Key for Code Exchange)
- 可定制的 OAuth 提供商
- 遵循内置的安全最佳实践
- 安全的令牌存储
- 处理授权码交换为访问令牌
- 提供简单直观的 API
- 自定义认证过程中的加载小部件
- 错误处理和认证错误回调
- 在 Rapider.ai 中经过生产测试
入门指南
前提条件
- 已安装 Flutter SDK
- 已设置 Flutter 项目
安装
在 pubspec.yaml
文件中添加以下依赖:
dependencies:
authwebview: ^1.0.4
然后运行 flutter pub get
来安装包。
使用
在 Dart 代码中导入 authwebview
包:
import 'package:authwebview/authwebview.dart';
定义一个 OAuth 提供商:
final provider = OAuthProvider(
name: 'Google',
discoveryUrl: 'https://accounts.google.com/.well-known/openid-configuration',
clientId: 'your-client-id',
redirectUrl: 'your-redirect-url',
scopes: ['openid', 'profile', 'email'],
);
执行 OAuth 流程:
final result = await OAuthService.performOAuthFlow(
context,
provider,
loadingWidget: CircularProgressIndicator(),
);
if (result != null) {
// 认证成功,访问令牌在结果中可用
print(result.accessToken);
} else {
// 认证失败或用户取消了操作
}
处理认证错误:
final result = await OAuthService.performOAuthFlow(
context,
provider,
loadingWidget: CircularProgressIndicator(),
onError: (error) {
// 处理认证错误
print('Authentication error: $error');
},
);
安全最佳实践
PKCE 实现
该插件默认实现 PKCE,以增强安全性。PKCE 可防止授权码拦截攻击。
令牌存储
- 不要将令牌存储在
SharedPreferences
或本地存储中,除非加密 - 使用
Flutter Secure Storage
或平台特定的安全存储解决方案 - 正确实现令牌刷新机制
WebView 安全
- 总是验证重定向 URL
- 实施适当的
state
验证 - 登出后清除 WebView 缓存和 cookie
其他建议
- 实施适当的 SSL/TLS 证书验证
- 使用合适的超时值
- 对令牌刷新实施速率限制
- 定期进行安全审计
API 参考
OAuthProvider
表示 OAuth 提供商配置。
属性 | 类型 | 描述 |
---|---|---|
name | String | OAuth 提供商的名称 |
discoveryUrl | String | 提供商的 OpenID Connect 发现文档的 URL |
clientId | String | OAuth 应用程序的客户端 ID |
redirectUrl | String | OAuth 应用程序的重定向 URL |
scopes | List | 认证过程中请求的范围列表 |
AuthService
提供执行 OAuth 认证流程的方法。
方法 | 描述 |
---|---|
performOAuthFlow | 在 webview 中启动 OAuth 认证流程 |
logout | 执行已认证用户的登出过程 |
getAuthorizationUrl | 获取 OAuth 提供商的授权 URL |
handleRedirect | 在成功认证后的重定向 URL 处理 |
AuthorizationTokenResponse
表示包含授权令牌的响应。
属性 | 类型 | 描述 |
---|---|---|
accessToken | String? | 用于进行身份验证请求的访问令牌 |
refreshToken | String? | 用于获取新访问令牌的刷新令牌 |
accessTokenExpirationDateTime | DateTime? | 访问令牌的过期日期和时间 |
idToken | String? | 包含用户信息的 ID 令牌 |
tokenType | String? | 访问令牌的类型(例如,Bearer) |
scopes | List | 授权令牌授予的范围列表 |
authorizationAdditionalParameters | Map<String, dynamic>? | 与令牌一起返回的其他参数 |
错误处理
该插件通过自定义异常和 performOAuthFlow
方法中的 onError
回调提供错误处理:
try {
final result = await OAuthService.performOAuthFlow(
context,
provider,
onError: (error) {
print('Authentication error: $error');
},
);
} catch (e) {
if (e is OAuthException) {
print('OAuth Error: ${e.message}');
print('Error Code: ${e.code}');
}
}
示例
一个完整的示例应用程序展示了如何使用此插件,可以在 这里 查看。
贡献
欢迎贡献!如果你发现了一个错误或想要添加一个功能,请创建一个问题或提交一个拉取请求。
许可证
该项目根据 MIT 许可证授权,详情请参阅 许可证文件。
下面是完整的示例代码:
import 'package:authwebview/authwebview.dart';
import 'package:flutter/material.dart';
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
import 'dart:convert';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
[@override](/user/override)
Widget build(BuildContext context) {
return MaterialApp(
title: 'AuthWebView Example',
theme: ThemeData(
colorScheme: ColorScheme.fromSeed(seedColor: Colors.blue),
useMaterial3: true,
),
home: const HomePage(),
);
}
}
class HomePage extends StatefulWidget {
const HomePage({super.key});
[@override](/user/override)
State<HomePage> createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
AuthorizationTokenResponse? _tokenResponse;
bool _isLoading = true;
final storage = const FlutterSecureStorage();
final List<OAuthProvider> _providers = [
// Example Google provider
const OAuthProvider(
name: 'Google',
discoveryUrl:
'https://accounts.google.com/.well-known/openid-configuration',
clientId: 'your-client-id',
redirectUrl: 'com.example.app:/oauth2callback',
scopes: ['openid', 'profile', 'email'],
),
// Example Microsoft provider
const OAuthProvider(
name: 'Microsoft',
discoveryUrl:
'https://login.microsoftonline.com/common/v2.0/.well-known/openid-configuration',
clientId: 'your-client-id',
redirectUrl: 'com.example.app:/oauth2callback',
scopes: ['openid', 'profile', 'email', 'offline_access'],
),
];
[@override](/user/override)
void initState() {
super.initState();
_loadSavedTokens();
}
Future<void> _loadSavedTokens() async {
try {
final tokenJson = await storage.read(key: 'oauth_tokens');
if (tokenJson != null) {
setState(() {
_tokenResponse = AuthorizationTokenResponse.fromJson(
json.decode(tokenJson),
);
});
}
} catch (e) {
debugPrint('Error loading saved tokens: $e');
} finally {
setState(() {
_isLoading = false;
});
}
}
Future<void> _saveTokens(AuthorizationTokenResponse tokens) async {
try {
await storage.write(
key: 'oauth_tokens',
value: json.encode(tokens.toJson()),
);
} catch (e) {
debugPrint('Error saving tokens: $e');
}
}
Future<void> _clearTokens() async {
try {
await storage.delete(key: 'oauth_tokens');
} catch (e) {
debugPrint('Error clearing tokens: $e');
}
}
Future<void> _login(OAuthProvider provider) async {
try {
final result = await OAuthService.performOAuthFlow(
context,
provider,
loadingWidget: const Center(
child: CircularProgressIndicator(),
),
backgroundColor:
Theme.of(context).colorScheme.surface, // background -> surface
);
if (result != null) {
setState(() {
_tokenResponse = result;
});
await _saveTokens(result);
}
} catch (e) {
if (mounted) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text('Login failed: ${e.toString()}'),
backgroundColor: Theme.of(context).colorScheme.error,
),
);
}
}
}
Future<void> _logout(OAuthProvider provider) async {
if (_tokenResponse?.idToken == null) {
setState(() {
_tokenResponse = null;
});
await _clearTokens();
return;
}
try {
final success = await OAuthService.logout(
provider,
_tokenResponse!.idToken!,
);
if (success) {
setState(() {
_tokenResponse = null;
});
await _clearTokens();
} else {
throw Exception('Logout failed');
}
} catch (e) {
if (mounted) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text('Logout failed: ${e.toString()}'),
backgroundColor: Theme.of(context).colorScheme.error,
),
);
}
}
}
[@override](/user/override)
Widget build(BuildContext context) {
if (_isLoading) {
return const Scaffold(
body: Center(
child: CircularProgressIndicator(),
),
);
}
return Scaffold(
appBar: AppBar(
title: const Text('AuthWebView Example'),
backgroundColor: Theme.of(context).colorScheme.primaryContainer,
),
body: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
if (_tokenResponse == null) ...[
const Text(
'Choose a provider to login:',
style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
),
const SizedBox(height: 20),
..._providers.map(
(provider) => Padding(
padding: const EdgeInsets.only(bottom: 10),
child: ElevatedButton(
onPressed: () => _login(provider),
child: Text('Login with ${provider.name}'),
),
),
),
] else ...[
Card(
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Text(
'Authentication Successful!',
style: TextStyle(
fontSize: 20,
fontWeight: FontWeight.bold,
color: Colors.green,
),
),
const SizedBox(height: 20),
Text('Access Token: ${_tokenResponse!.accessToken}'),
if (_tokenResponse!.refreshToken != null) ...[
const SizedBox(height: 10),
Text('Refresh Token: ${_tokenResponse!.refreshToken}'),
],
if (_tokenResponse!.accessTokenExpirationDateTime !=
null) ...[
const SizedBox(height: 10),
Text(
'Expires: ${_tokenResponse!.accessTokenExpirationDateTime}',
),
],
const SizedBox(height: 20),
ElevatedButton(
onPressed: () => _logout(_providers.first),
style: ElevatedButton.styleFrom(
backgroundColor: Theme.of(context).colorScheme.error,
foregroundColor: Theme.of(context).colorScheme.onError,
),
child: const Text('Logout'),
),
],
),
),
),
],
],
),
),
);
}
}
更多关于Flutter网页认证插件authwebview的使用的实战教程也可以访问 https://www.itying.com/category-92-b0.html
更多关于Flutter网页认证插件authwebview的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html
authwebview
是 Flutter 中一个用于处理网页认证的插件,通常用于 OAuth、OpenID Connect 或其他基于 Web 的认证流程。它允许你在 Flutter 应用中嵌入一个 WebView,用户可以在其中进行登录或授权操作,然后将认证结果返回给 Flutter 应用。
以下是如何在 Flutter 项目中使用 authwebview
插件的基本步骤:
1. 添加依赖
首先,你需要在 pubspec.yaml
文件中添加 authwebview
插件的依赖:
dependencies:
flutter:
sdk: flutter
authwebview: ^1.0.0 # 请使用最新版本
然后运行 flutter pub get
来获取依赖。
2. 导入插件
在你的 Dart 文件中导入 authwebview
插件:
import 'package:authwebview/authwebview.dart';
3. 使用 AuthWebView
你可以使用 AuthWebView
类来创建一个 WebView,并处理认证流程。以下是一个简单的示例:
import 'package:flutter/material.dart';
import 'package:authwebview/authwebview.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
[@override](/user/override)
Widget build(BuildContext context) {
return MaterialApp(
home: AuthWebViewExample(),
);
}
}
class AuthWebViewExample extends StatelessWidget {
[@override](/user/override)
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('AuthWebView Example'),
),
body: AuthWebView(
initialUrl: 'https://your-authentication-url.com',
redirectUri: 'https://your-redirect-uri.com',
onCallback: (Uri uri) {
// 处理认证回调
print('Callback Uri: $uri');
// 你可以在这里解析 Uri 并提取 token 或其他信息
},
onError: (String error) {
// 处理错误
print('Error: $error');
},
),
);
}
}