Flutter OAuth2认证插件oauth2_alevato的使用

Flutter OAuth2认证插件oauth2_alevato的使用

关于OAuth2

OAuth2允许客户端(使用此库的程序)访问并操作由资源拥有者(最终用户)拥有的远程服务器上的资源。客户端会将资源拥有者导向授权服务器(通常但不总是与资源所在的服务器相同),在那里资源拥有者告知授权服务器向客户端发放访问令牌。该令牌作为证据证明客户端有权限代表资源拥有者访问资源。

OAuth2提供了几种不同的方法让客户端获取授权。在撰写本文时,此库仅支持以下三种流:

  • 授权码授权(Authorization Code Grant)
  • 客户端凭证授权(Client Credentials Grant)
  • 资源所有者密码授权(Resource Owner Password Grant)

授权码授权

资源:

import 'dart:io';

import 'package:oauth2_alevato/oauth2_alevato.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服务器通常不会依赖于确切知道客户端是谁。
const identifier = 'my client identifier';
const secret = 'my client secret';

// 这是在您的应用程序服务器上的一个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(带有附加查询参数的authorizationEndpoint)。范围和状态可以可选地传递给此方法。
  var authorizationUrl = grant.getAuthorizationUrl(redirectUrl);

  // 将资源拥有者重定向到授权URL。一旦资源拥有者授权,他们将被重定向到`redirectUrl`并带有授权码。`redirect`应导致浏览器重定向到另一个URL,该URL也应具有侦听器。
  //
  // `redirect`和`listen`未在此处实现。请参阅以下详细信息。
  await redirect(authorizationUrl);
  var responseUrl = await listen(redirectUrl);

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

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

  // 一旦您有了Client,您可以像任何其他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();
}

如何实现 redirectlisten

没有统一的例子来实现 redirectlisten,因为每个平台都有不同的选项。

对于Flutter应用,有两种流行的方法:

  1. 使用 url_launcher 启动浏览器并使用 uni_links 监听重定向。

    if (await canLaunch(authorizationUrl.toString())) {
      await launch(authorizationUrl.toString()); 
    }
    
    // -------
    final linksStream = getLinksStream().listen((Uri uri) async {
     if (uri.toString().startsWith(redirectUrl)) {
       responseUrl = uri;
     }
    });
    
  2. 在应用内启动WebView并使用 webview_flutter 监听重定向。

    WebView(
      javascriptMode: JavascriptMode.unrestricted,
      initialUrl: authorizationUrl.toString(),
      navigationDelegate: (navReq) {
        if (navReq.url.startsWith(redirectUrl)) {
          responseUrl = Uri.parse(navReq.url);
          return NavigationDecision.prevent;
        }
        return NavigationDecision.navigate;
      },
      // -------
    );
    

对于Dart应用,最佳方法取决于访问浏览器的可用选项。一般来说,你需要通过客户端的浏览器启动授权URL并监听重定向URL。

客户端凭证授权

资源:

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

// OAuth2规范期望在使用客户端凭证授权时发送客户端的标识符和密钥。
//
// 因为客户端凭证授权不固有地关联于用户,所以返回的令牌是否允许有限API访问取决于特定服务器。
//
// 无论如何,你必须提供一个客户端标识符和一个客户端密钥:
const identifier = 'my client identifier';
const 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');

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

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

// 向授权端点发起请求,这将产生一个完全经过身份验证的Client。
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认证插件oauth2_alevato的使用的实战教程也可以访问 https://www.itying.com/category-92-b0.html

1 回复

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


当然,下面是一个关于如何在Flutter应用中使用oauth2_alevato插件进行OAuth2认证的代码示例。这个插件允许你通过OAuth2协议进行用户认证,并获取访问令牌。

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

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

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

接下来,下面是一个完整的示例,展示如何使用oauth2_alevato插件进行OAuth2认证:

import 'package:flutter/material.dart';
import 'package:oauth2_alevato/oauth2_alevato.dart';
import 'dart:convert';
import 'package:http/http.dart' as http;

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

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'OAuth2 Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: OAuth2Demo(),
    );
  }
}

class OAuth2Demo extends StatefulWidget {
  @override
  _OAuth2DemoState createState() => _OAuth2DemoState();
}

class _OAuth2DemoState extends State<OAuth2Demo> {
  OAuth2Client? _oauth2Client;
  String? _accessToken;

  @override
  void initState() {
    super.initState();
    _setupOAuth2();
  }

  void _setupOAuth2() {
    final clientId = 'your-client-id';
    final clientSecret = 'your-client-secret'; // 如果需要的话
    final redirectUri = 'your-redirect-uri';
    final authorizationEndpoint = Uri.parse('https://your-auth-server.com/oauth/authorize');
    final tokenEndpoint = Uri.parse('https://your-auth-server.com/oauth/token');

    _oauth2Client = OAuth2Client(
      clientId: clientId,
      clientSecret: clientSecret,
      redirectUri: redirectUri,
      authorizationEndpoint: authorizationEndpoint,
      tokenEndpoint: tokenEndpoint,
      scopes: ['scope1', 'scope2'], // 根据需要设置
    );
  }

  Future<void> _authorize() async {
    try {
      final Uri authorizationUri = _oauth2Client!.createAuthorizationUri();
      // 打开授权URI,通常这里会使用一个WebView或系统浏览器
      // 这里为了演示,直接打印URI
      print('Open this URI in your browser: $authorizationUri');

      // 假设用户已经完成了授权并返回了redirectUri,解析code参数
      // 在实际应用中,你会从WebView或系统浏览器的回调中获取这个code
      final String? code = 'user-authorized-code'; // 替换为实际的code

      if (code != null) {
        final Credentials credentials = await _oauth2Client!.getCredentials(code: code);
        setState(() {
          _accessToken = credentials.accessToken;
        });

        // 使用访问令牌进行API调用示例
        _makeApiCall();
      }
    } catch (e) {
      print('Error during OAuth2 authorization: $e');
    }
  }

  Future<void> _makeApiCall() async {
    if (_accessToken != null) {
      final Uri apiUri = Uri.parse('https://api.example.com/protected-resource');
      final headers = {
        'Authorization': 'Bearer $_accessToken',
      };

      final response = await http.get(apiUri, headers: headers);
      if (response.statusCode == 200) {
        final data = jsonDecode(response.body);
        print('API response: $data');
      } else {
        print('Failed to fetch API data: ${response.statusCode}');
      }
    }
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('OAuth2 Demo'),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            Text(
              'Access Token: $_accessToken',
              style: TextStyle(fontSize: 18),
            ),
            SizedBox(height: 20),
            ElevatedButton(
              onPressed: _authorize,
              child: Text('Authorize'),
            ),
          ],
        ),
      ),
    );
  }
}

注意事项

  1. 替换占位符:你需要将your-client-idyour-client-secretyour-redirect-uri以及授权和令牌端点URL替换为实际的OAuth2提供者信息。
  2. 处理授权URI:在实际应用中,你会使用flutter_webview_pluginurl_launcher等插件来打开并处理授权URI的回调。
  3. 错误处理:在实际应用中,添加更多的错误处理逻辑以确保应用的健壮性。

这个示例演示了如何设置OAuth2客户端、获取授权URI、解析授权码以及使用访问令牌进行API调用。希望这对你有所帮助!

回到顶部