You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Flutter集成Keycloak(OpenID)内部WebView重定向问题求助

解决Flutter内部WebView对接Keycloak OpenID认证的重定向异常问题

问题根源

你遇到的问题核心是url_launcher的inAppWebView模式无法拦截自定义Scheme的重定向请求。当Keycloak登录完成后跳转到你配置的自定义redirectUri(比如myapp://auth/callback)时,WebView无法识别并处理这个Scheme,导致跳转到异常页面。

解决方案:用webview_flutter替代url_launcher

webview_flutter支持拦截所有URL跳转,能捕获Keycloak的重定向请求,提取授权码后完成Token交换。

1. 更新依赖

修改pubspec.yaml,替换原有依赖并添加webview_flutter:

dependencies:
  flutter:
    sdk: flutter
  openid_client: ^0.4.6
  webview_flutter: ^4.4.0

2. 配置自定义Scheme的跳转权限

Android端(android/app/src/main/AndroidManifest.xml)

在主<activity>标签内添加intent-filter,允许应用处理自定义Scheme:

<intent-filter>
    <action android:name="android.intent.action.VIEW" />
    <category android:name="android.intent.category.DEFAULT" />
    <category android:name="android.intent.category.BROWSABLE" />
    <data android:scheme="myapp" android:host="auth" android:path="/callback" />
</intent-filter>

iOS端(ios/Runner/Info.plist)

添加以下配置,注册自定义Scheme:

<key>CFBundleURLTypes</key>
<array>
    <dict>
        <key>CFBundleURLSchemes</key>
        <array>
            <string>myapp</string>
        </array>
        <key>CFBundleURLName</key>
        <string>AuthCallback</string>
    </dict>
</array>

3. 实现带拦截逻辑的WebView认证页面

创建一个专门的认证WebView页面,拦截重定向请求并提取授权码:

import 'package:flutter/material.dart';
import 'package:webview_flutter/webview_flutter.dart';
import 'package:openid_client/openid_client.dart';

class AuthWebViewPage extends StatefulWidget {
  final AuthorizationRequest authRequest;
  final Uri redirectUri;

  const AuthWebViewPage({
    super.key,
    required this.authRequest,
    required this.redirectUri,
  });

  @override
  State<AuthWebViewPage> createState() => _AuthWebViewPageState();
}

class _AuthWebViewPageState extends State<AuthWebViewPage> {
  late WebViewController _webViewController;

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

  void _initWebViewController() {
    _webViewController = WebViewController()
      ..setJavaScriptMode(JavaScriptMode.unrestricted)
      ..setNavigationDelegate(
        NavigationDelegate(
          onNavigationRequest: (NavigationRequest request) async {
            final uri = Uri.parse(request.url);
            // 拦截目标重定向地址
            if (uri.scheme == widget.redirectUri.scheme &&
                uri.host == widget.redirectUri.host &&
                uri.path == widget.redirectUri.path) {
              final code = uri.queryParameters['code'];
              if (code != null) {
                // 关闭WebView,返回授权码
                Navigator.pop(context, code);
              }
              return NavigationDecision.prevent;
            }
            return NavigationDecision.navigate;
          },
        ),
      )
      ..loadRequest(widget.authRequest.requestUri);
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('用户登录')),
      body: WebViewWidget(controller: _webViewController),
    );
  }
}

4. 发起认证流程并交换Token

替换原有的认证逻辑,通过WebView获取授权码后,用openid_client交换Token:

// 初始化Keycloak OpenID客户端
final issuer = await Issuer.discover(Uri.parse('https://你的Keycloak域名/auth/realms/你的Realm'));
final client = Client(issuer, '你的客户端ID');

// 配置重定向URI(需和Keycloak后台保持一致)
const redirectUrl = 'myapp://auth/callback';
final redirectUri = Uri.parse(redirectUrl);

// 创建授权请求
final authRequest = client.createAuthorizationRequest(
  scopes: ['openid', 'profile', 'email'],
  redirectUri: redirectUri,
);

// 打开WebView页面,等待返回授权码
final code = await Navigator.push<String>(
  context,
  MaterialPageRoute(
    builder: (context) => AuthWebViewPage(
      authRequest: authRequest,
      redirectUri: redirectUri,
    ),
  ),
);

if (code != null) {
  // 用授权码交换Token
  final tokenResponse = await authRequest.exchange(code);
  print('TOKEN = ${tokenResponse.accessToken}');
} else {
  print('认证流程被取消或失败');
}

5. Keycloak后台配置

确保Keycloak客户端的Valid Redirect URIs列表中包含你配置的自定义Scheme地址,比如myapp://auth/callback。

为什么之前的方案不行?

url_launcher的inAppWebView模式仅封装了基础WebView功能,没有提供拦截自定义Scheme跳转的能力。当Keycloak跳转到自定义Scheme时,WebView无法处理,只能尝试调用外部应用打开,最终导致异常页面。而webview_flutter通过NavigationDelegate可以完全控制URL跳转逻辑,完美解决这个问题。

内容的提问来源于stack exchange,提问作者Nipun

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.12 17:30:13