Flutter Windows端无需google_sign_in包实现Google身份验证方案咨询
Flutter Windows端无google_sign_in包的Google登录实现方案
你的实现思路完全可行,本质是走Google标准OAuth 2.0授权码流程,无需依赖google_sign_in包,Windows、macOS、Linux桌面端均可通用。
前置准备
先到Google Cloud控制台创建OAuth 2.0客户端ID,平台选择桌面应用,重定向URI设置为http://localhost:自定义闲置端口,比如http://localhost:7890,端口需后续和代码里的监听端口保持一致。
核心实现逻辑
- 拼接Google授权URL,携带客户端ID、重定向URI、权限scope等参数
- 调用系统默认浏览器跳转授权页,用户完成登录授权
- 本地启动临时HTTP服务,监听预先设置的端口,捕获Google回调的授权码
- 用授权码向Google令牌接口发起请求,兑换access_token、id_token等凭证
- 关闭本地临时服务,可选使用access_token请求用户信息接口获取账号资料
实现代码示例
依赖引入
pubspec.yaml中添加如下依赖:
dependencies: flutter: sdk: flutter http: ^1.1.0 url_launcher: ^6.2.2
完整登录逻辑代码
import 'dart:convert'; import 'dart:io'; import 'package:flutter/material.dart'; import 'package:http/http.dart' as http; import 'package:url_launcher/url_launcher.dart'; class GoogleLoginService { // 替换为自己申请的桌面应用客户端ID static const String clientId = "你的客户端ID.apps.googleusercontent.com"; // 替换为自己设置的重定向URI,端口要和监听端口一致 static const String redirectUri = "http://localhost:7890"; static const int listenPort = 7890; // 根据需求调整请求的权限范围 static const List<String> scopes = [ 'openid', 'email', 'profile', ]; Future<Map<String, dynamic>?> signInWithGoogle() async { // 1. 拼接授权页URL final authUrl = Uri.https('accounts.google.com', '/o/oauth2/v2/auth', { 'client_id': clientId, 'redirect_uri': redirectUri, 'response_type': 'code', 'scope': scopes.join(' '), 'access_type': 'offline', // 需要refresh_token时保留该参数 }); // 2. 打开系统浏览器跳转授权页 if (await canLaunchUrl(authUrl)) { await launchUrl(authUrl); } else { debugPrint('无法打开系统浏览器'); return null; } // 3. 启动本地服务监听授权回调 final server = await HttpServer.bind(InternetAddress.loopbackIPv4, listenPort); try { final request = await server.first; final code = request.uri.queryParameters['code']; if (code == null) { request.response.statusCode = HttpStatus.badRequest; request.response.write('授权失败,未获取到授权码'); await request.response.close(); return null; } // 给浏览器返回提示,用户可手动关闭页面 request.response.statusCode = HttpStatus.ok; request.response.write('授权成功,你可以关闭当前页面返回应用'); await request.response.close(); // 4. 用授权码兑换访问令牌 final tokenResponse = await http.post( Uri.parse('https://oauth2.googleapis.com/token'), headers: {'Content-Type': 'application/x-www-form-urlencoded'}, body: { 'client_id': clientId, 'redirect_uri': redirectUri, 'grant_type': 'authorization_code', 'code': code, }, ); if (tokenResponse.statusCode != 200) { debugPrint('获取令牌失败: ${tokenResponse.body}'); return null; } final tokenData = jsonDecode(tokenResponse.body); final accessToken = tokenData['access_token']; // 5. 可选:获取用户基础信息 final userInfoResponse = await http.get( Uri.parse('https://www.googleapis.com/oauth2/v3/userinfo'), headers: {'Authorization': 'Bearer $accessToken'}, ); if (userInfoResponse.statusCode == 200) { final userInfo = jsonDecode(userInfoResponse.body); return { 'token_info': tokenData, 'user_info': userInfo, }; } return null; } catch (e) { debugPrint('登录过程出错: $e'); return null; } finally { // 登录完成后必须关闭本地服务,避免端口占用 await server.close(); } } }
注意事项
- 客户端ID必须选择桌面应用类型,不能选移动端、Web端类型,否则会出现重定向URI不匹配错误
- 本地监听的端口必须和Google Cloud后台设置的重定向URI端口完全一致,不要使用被系统或其他软件占用的端口
- 若需要实现免重复登录,可以将返回的refresh_token持久化存储,令牌过期后直接用refresh_token兑换新的access_token即可,无需用户重复授权
内容的提问来源于stack exchange,提问作者Christian Findlay
相关产品推荐
相关产品推荐

