Flutter OAuth2认证插件custom_oauth2的使用

Flutter OAuth2认证插件custom_oauth2的使用

简介

custom_oauth2 是一个用于代表用户与远程服务进行身份验证,并使用用户的 OAuth2 凭证进行授权的 HTTP 请求的客户端库。

OAuth2 允许客户端(使用此库的程序)访问并操作由资源拥有者(最终用户)拥有的、位于远程服务器上的资源。客户端会将资源拥有者重定向到授权服务器(通常但不总是与资源所在的服务器相同),资源拥有者会在授权服务器上告知授权服务器将访问令牌授予给客户端。此令牌作为证明,表明客户端有权代表资源拥有者访问资源。

OAuth2 提供了几种不同的方法来获取客户端的授权。目前,该库仅支持以下几种授权方式:

  • 授权码授权
  • 客户端凭据授权
  • 资源拥有者密码授权

授权码授权

资源

import 'dart:io';

import 'package:oauth2/oauth2.dart' as oauth2;

// 这些 URL 是授权服务器提供的端点。它们通常包含在服务器的 OAuth2 API 文档中。
final authorizationEndpoint =
    Uri.parse('http://example.com/oauth2/authorization');
final tokenEndpoint = Uri.parse('http://example.com/oauth2/token');

// 授权服务器会为每个客户端分配单独的客户端标识符和密钥,以允许服务器识别哪个客户端正在访问它。某些服务器可能还有匿名的标识符/密钥对,任何客户端都可以使用。
//
// 注意,源代码或可执行文件易于获得的客户端可能无法确保客户端密钥保密。这没关系;OAuth2 服务器通常不会依赖于确定客户端就是其所声称的身份。
final identifier = 'my client identifier';
final secret = 'my client secret';

// 这是在您的应用程序服务器上的一个 URL。一旦资源拥有者授权了客户端,授权服务器将重定向资源拥有者到此 URL。重定向时,查询参数中会包含授权码。
final redirectUrl = Uri.parse('http://my-site.com/oauth2-redirect');

/// 用户的凭证存储在一个持久性文件中。如果服务器发出了刷新令牌,允许客户端刷新过期的凭证,则这些凭证可能是有效的,这意味着用户无需重新认证。
final credentialsFile = File('~/.myapp/credentials.json');

/// 从已保存的凭证中加载 OAuth2 客户端,或者认证一个新的客户端。
Future<oauth2.Client> createClient() async {
  var exists = await credentialsFile.exists();

  // 如果 OAuth2 凭证已经从之前的运行中保存下来,我们只需重新加载它们。
  if (exists) {
    var credentials =
        oauth2.Credentials.fromJson(await credentialsFile.readAsString());
    return oauth2.Client(credentials, identifier: identifier, secret: secret);
  }

  // 如果我们还没有 OAuth2 凭证,我们需要获取资源拥有者的授权。这里假设我们是一个命令行应用程序。
  var grant = oauth2.AuthorizationCodeGrant(
      identifier, authorizationEndpoint, tokenEndpoint,
      secret: secret);

  // 在授权服务器上的一个 URL(带有额外的查询参数)。可以选用地传递范围和状态。
  var authorizationUrl = grant.getAuthorizationUrl(redirectUrl);

  // 将资源拥有者重定向到授权 URL。一旦资源拥有者授权,他们会被重定向到 `redirectUrl` 并附带授权码。`redirect` 应该导致浏览器重定向到另一个 URL,该 URL 应该也有一个监听器。
  //
  // `redirect` 和 `listen` 的实现细节未在此处展示。
  await redirect(authorizationUrl);
  var responseUrl = await listen(redirectUrl);

  // 一旦用户被重定向到 `redirectUrl`,将查询参数传递给 AuthorizationCodeGrant。它会验证这些参数并提取授权码以创建新的客户端。
  return await grant.handleAuthorizationResponse(responseUrl.queryParameters);
}

void main() async {
  var client = await createClient();

  // 一旦你有一个客户端,你可以像使用其他任何 HTTP 客户端一样使用它。
  print(await client.read(Uri.http('example.com', 'protected-resources.txt')));

  // 一旦我们完成了客户端的使用,保存凭证文件。这样可以确保如果在使用客户端期间自动刷新了凭证,则新的凭证可以在下一次程序运行时可用。
  await credentialsFile.writeAsString(client.credentials.toJson());
}

Future<void> redirect(Uri url) async {
  // 客户端实现细节
}

Future<Uri> listen(Uri url) async {
  // 客户端实现细节
  return Uri();
}

客户端凭据授权

资源

// 这个 URL 是授权服务器提供的一个端点。它通常包含在服务器的 OAuth2 API 文档中。
final authorizationEndpoint =
    Uri.parse('http://example.com/oauth2/authorization');

// OAuth2 规范期望在使用客户端凭据授权时发送客户端的标识符和密钥。
//
// 因为客户凭据授权本质上不与用户关联,所以是否返回允许有限 API 访问的令牌取决于具体服务器。
//
// 不管怎样,您必须提供一个客户端标识符和一个客户端密钥:
final identifier = 'my client identifier';
final secret = 'my client secret';

// 调用顶级函数 `clientCredentialsGrant` 将返回一个 [Client]。
var client = await oauth2.clientCredentialsGrant(
    authorizationEndpoint, identifier, secret);

// 使用已认证的客户端,您可以进行请求,客户端凭据授权期间服务器返回的 Bearer 令牌将附加到您发出的任何请求中。
var response =
    await client.read('https://example.com/api/some_resource.json');

// 您可以将客户端的凭证(包括访问令牌,以及可能的刷新令牌和过期日期)保存到一个文件中。这样,后续运行就不需要重新认证,并且您可以避免直接存储客户端标识符和密钥。
await credentialsFile.writeAsString(client.credentials.toJson());

资源拥有者密码授权

资源

// 这个 URL 是授权服务器提供的一个端点。它通常包含在服务器的 OAuth2 API 文档中。
final authorizationEndpoint =
    Uri.parse('http://example.com/oauth2/authorization');

// 用户应提供自己的用户名和密码。
final username = 'example user';
final password = 'example password';

// 授权服务器可能会为每个客户端分配单独的客户端标识符和密钥,以允许服务器识别哪个客户端正在访问它。某些服务器可能还有匿名的标识符/密钥对,任何客户端都可以使用。
//
// 某些服务器不要求客户端进行自我认证,在这种情况下,这些标识符和密钥应该被省略。
final identifier = 'my client identifier';
final secret = 'my client secret';

// 向授权端点发起请求,生成完全认证的客户端。
var client = await oauth2.resourceOwnerPasswordGrant(
    authorizationEndpoint, username, password,
    identifier: identifier, secret: secret);

// 一旦你有了客户端,你可以像使用其他任何 HTTP 客户端一样使用它。
var result = await client.read('http://example.com/protected-resources.txt');

// 一旦我们完成了客户端的使用,保存凭证文件。这样可以让我们重复使用凭证,并避免直接存储用户名和密码。
File('~/.myapp/credentials.json').writeAsString(client.credentials.toJson());

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

1 回复

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


在Flutter中实现OAuth2认证可以使用custom_oauth2插件。custom_oauth2是一个灵活的插件,允许你自定义OAuth2认证流程。以下是使用custom_oauth2插件的基本步骤:

1. 添加依赖

首先,在pubspec.yaml文件中添加custom_oauth2插件的依赖:

dependencies:
  flutter:
    sdk: flutter
  custom_oauth2: ^1.0.0  # 请使用最新版本

然后运行flutter pub get来获取依赖。

2. 创建OAuth2客户端

接下来,你需要创建一个OAuth2客户端。custom_oauth2提供了一个CustomOAuth2Client类,你可以继承它并实现必要的方法。

import 'package:custom_oauth2/custom_oauth2.dart';

class MyOAuth2Client extends CustomOAuth2Client {
  MyOAuth2Client({
    required String authorizationEndpoint,
    required String tokenEndpoint,
    required String redirectUri,
    required String clientId,
    String? clientSecret,
  }) : super(
          authorizationEndpoint: authorizationEndpoint,
          tokenEndpoint: tokenEndpoint,
          redirectUri: redirectUri,
          clientId: clientId,
          clientSecret: clientSecret,
        );

  @override
  Future<Map<String, String>> createAuthorizationHeader(String accessToken) async {
    return {'Authorization': 'Bearer $accessToken'};
  }
}

3. 初始化OAuth2客户端

在你的应用中初始化OAuth2客户端:

final oauth2Client = MyOAuth2Client(
  authorizationEndpoint: 'https://your.auth.server/authorize',
  tokenEndpoint: 'https://your.auth.server/token',
  redirectUri: 'your.app://oauth2redirect',
  clientId: 'your-client-id',
  clientSecret: 'your-client-secret', // 如果不需要可以省略
);

4. 进行OAuth2认证

使用OAuth2客户端进行认证:

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

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

class MyApp extends StatelessWidget {
  final oauth2Client = MyOAuth2Client(
    authorizationEndpoint: 'https://your.auth.server/authorize',
    tokenEndpoint: 'https://your.auth.server/token',
    redirectUri: 'your.app://oauth2redirect',
    clientId: 'your-client-id',
    clientSecret: 'your-client-secret', // 如果不需要可以省略
  );

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: Text('OAuth2 Example'),
        ),
        body: Center(
          child: ElevatedButton(
            onPressed: () async {
              try {
                final tokenResponse = await oauth2Client.requestAccessToken();
                print('Access Token: ${tokenResponse.accessToken}');
              } catch (e) {
                print('Error: $e');
              }
            },
            child: Text('Login with OAuth2'),
          ),
        ),
      ),
    );
  }
}

5. 处理回调URL

android/app/src/main/AndroidManifest.xml中配置回调URL:

<activity android:name="com.linusu.flutter_web_auth.CallbackActivity">
    <intent-filter android:label="flutter_web_auth">
        <action android:name="android.intent.action.VIEW" />
        <category android:name="android.intent.category.DEFAULT" />
        <category android:name="android.intent.category.BROWSABLE" />
        <data android:scheme="your.app" />
    </intent-filter>
</activity>

在iOS中,在ios/Runner/Info.plist中配置回调URL:

<key>CFBundleURLTypes</key>
<array>
    <dict>
        <key>CFBundleURLSchemes</key>
        <array>
            <string>your.app</string>
        </array>
    </dict>
</array>

6. 获取和刷新Token

在获取到access_token后,你可以使用它来进行API调用。如果需要刷新access_token,可以调用refreshAccessToken方法:

final newTokenResponse = await oauth2Client.refreshAccessToken(refreshToken);
print('New Access Token: ${newTokenResponse.accessToken}');

7. 处理异常

在OAuth2流程中可能会遇到各种异常,建议在处理时捕获并妥善处理这些异常。

try {
  final tokenResponse = await oauth2Client.requestAccessToken();
  print('Access Token: ${tokenResponse.accessToken}');
} catch (e) {
  print('Error: $e');
}
回到顶部