Flutter实现不依赖Firebase的多平台的Google登录
GoogleConsole配置
官网:
点击创建OAuth2.0客户端,选择对应的应用类型。
推荐的应用类型如下,多个应用类型可以共用一个client_id,也可以考虑创建多个client_id。
平台 | 应用类型 |
---|---|
Android | Web |
iOS | iOS |
MacOS | iOS |
Web | Web |
Windows | Web |
创建OAuth客户端ID的示例如下:
Web应用需要填写回调地址,地址就是Web的访问地址,其中调试的时候可以在命令行或IDE启动配置中添加配置--web-port 3000
来指定端口,重定向的URI只支持http或https的schema。
其中Android端需要根据命令提示生成本地证书的SHA-1指纹,其中需要替换命令行的keystore地址,调试的keystore的默认密码是android。
iOS的只需要填写软件包的ID,即类com.example.xxx。
Flutter项目配置
Web
除了获取client_id之外不需要额外配置。
如果要获取token,则使用如下语句获取,提前需要打开Google平台上的PeopleAPI。
代码语言:javascript代码运行次数:0运行复制final GoogleSignInAuthentication googleSignInAuthentication = await account!.authentication;
没有开启PeopleAPI就会报错如下:
代码语言:javascript代码运行次数:0运行复制[17:41:01.638][unknown:?][INFO] [AuthService]: ClientException: {
"error": {
"code": 403,
"message": "People API has not been used in project 488325119723 before or it is disabled. Enable it by visiting .googleapis/overview?project=488325119723 then retry. If you enabled this API recently, wait a few minutes for the action to propagate to our systems and retry.",
"status": "PERMISSION_DENIED",
"details": [
{
"@type": "type.googleapis/google.rpc.ErrorInfo",
"reason": "SERVICE_DISABLED",
"domain": "googleapis",
"metadata": {
"consumer": "projects/488325119723",
"service": "people.googleapis",
"containerInfo": "488325119723",
"activationUrl": ".googleapis/overview?project=488325119723",
"serviceTitle": "People API"
}
},
{
"@type": "type.googleapis/google.rpc.LocalizedMessage",
"locale": "en-US",
"message": "People API has not been used in project 488325119723 before or it is disabled. Enable it by visiting .googleapis/overview?project=488325119723 then retry. If you enabled this API recently, wait a few minutes for the action to propagate to our systems and retry."
},
{
"@type": "type.googleapis/google.rpc.Help",
"links": [
{
"description": "Google developers console API activation",
"url": ".googleapis/overview?project=488325119723"
}
]
}
]
}
}
, uri=;personFields=photos%2Cnames%2CemailAddresses
===============
在Google Console中找到People API,地址:
启动People API如下:
Android
除了获取client_id之外不需要额外配置。
如果使用应用类型为Android的client_id,则会报错如下:
代码语言:javascript代码运行次数:0运行复制clientId is not supported on Android and is interpreted as serverClientId. Use serverClientId instead to suppress this warning.
serverClientId就是Web应用类型的client_id,可以参考如下,clientId可以去掉,也可以只设置clientId的值为Web应用类型的client_id。
代码语言:javascript代码运行次数:0运行复制final GoogleSignIn _googleSignIn = GoogleSignIn(
clientId: '<ANDROID_CLIENT_ID>', // 可选,仅Android需要
serverClientId: '<WEB_CLIENT_ID>', // 必须,Web应用ID
);
iOS
参考配置如下:
/packages/google_sign_in_ios
在Info.plist文件中添加如下配置,其中需要配置client_id,以及域名反转过来的client_id。
代码语言:javascript代码运行次数:0运行复制<key>CFBundleURLTypes</key>
<array>
<dict>
<key>CFBundleTypeRole</key>
<string>Editor</string>
<key>CFBundleURLSchemes</key>
<array>
<string>com.googleusercontent.apps.488325119723-xxx</string>
</array>
</dict>
</array>
<key>GIDClientID</key>
<string>488325119723-xxx.apps.googleusercontent</string>
MacOS
在Info.plist文件中仍然添加iOS中上述配置。
如果缺少Podfile文件,则可以从其它项目中拷贝过来,也可以尝试build一下。
如果报错最低支持版本,则可以在Podfile修改最低支持版本。
代码语言:javascript代码运行次数:0运行复制platform :osx, '11.5'
安装依赖
代码语言:javascript代码运行次数:0运行复制cd macos
pod install
如果报错如下
代码语言:javascript代码运行次数:0运行复制[!] CocoaPods did not set the base configuration of your project because your project already has a custom config set. In order for CocoaPods integration to work at all, please either set the base configurations of the target `Runner` to `Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig` or include the `Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig` in your build configuration (`Runner/Configs/AppInfo.xcconfig`).
[!] CocoaPods did not set the base configuration of your project because your project already has a custom config set. In order for CocoaPods integration to work at all, please either set the base configurations of the target `Runner` to `Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig` or include the `Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig` in your build configuration (`Runner/Configs/AppInfo.xcconfig`).
[!] CocoaPods did not set the base configuration of your project because your project already has a custom config set. In order for CocoaPods integration to work at all, please either set the base configurations of the target `Runner` to `Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig` or include the `Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig` in your build configuration (`Runner/Configs/AppInfo.xcconfig`).
则在macos/Runner/Configs/AppInfo.xcconfig
文件中添加如下内容。
#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"
#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"
#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"
如果Xcode报错如下找不到FlutterInputs.xcfilelist
文件,则在命令行中先执行flutter build macos
可以自动生成文件。
报错:Unable to load contents of file list: '/Users/ryonluo/Code/flutter_game_mvp/macos/Flutter/ephemeral/FlutterInputs.xcfilelist'
如果没有报错,还是获取不到用户信息,但在Xcode有如下日志。
代码语言:javascript代码运行次数:0运行复制w_socket_connect [C1.1.1.1:3] connectx(25 (guarded), [srcif=0, srcaddr=<NULL>, dstaddr=142.250.217.74:443], SAE_ASSOCID_ANY, 0, NULL, 0, NULL, SAE_CONNID_ANY) failed: [1: Operation not permitted]
nw_socket_connect [C1.1.1.1:3] connectx failed (fd 25) [1: Operation not permitted]
nw_socket_connect connectx failed [1: Operation not permitted]
nw_endpoint_flow_failed_with_error [C1.1.1.1 142.250.217.74:443 in_progress socket-flow (satisfied (Path is satisfied), interface: utun4, dns)] already failing, returning
nw_socket_connect [C1.1.1.2:3] connectx(25 (guarded), [srcif=0, srcaddr=<NULL>, dstaddr=142.250.217.106:443], SAE_ASSOCID_ANY, 0, NULL, 0, NULL, SAE_CONNID_ANY) failed: [1: Operation not permitted]
nw_socket_connect [C1.1.1.2:3] connectx failed (fd 25) [1: Operation not permitted]
nw_socket_connect connectx failed [1: Operation not permitted]
nw_endpoint_flow_failed_with_error [C1.1.1.2 142.250.217.106:443 in_progress socket-flow (satisfied (Path is satisfied), interface: utun4, dns)] already failing, returning
nw_socket_connect [C1.1.1.3:3] connectx(25 (guarded), [srcif=0, srcaddr=<NULL>, dstaddr=142.251.215.234:443], SAE_ASSOCID_ANY, 0, NULL, 0, NULL, SAE_CONNID_ANY) failed: [1: Operation not permitted]
nw_socket_connect [C1.1.1.3:3] connectx failed (fd 25) [1: Operation not permitted]
nw_socket_connect connectx failed [1: Operation not permitted]
nw_endpoint_flow_failed_with_error [C1.1.1.3 142.251.215.234:443 in_progress socket-flow (satisfied (Path is satisfied), interface: utun4, dns)] already failing, returning
nw_socket_connect [C1.1.1.4:3] connectx(25 (guarded), [srcif=0, srcaddr=<NULL>, dstaddr=142.250.69.202:443], SAE_ASSOCID_ANY, 0, NULL, 0, NULL, SAE_CONNID_ANY) failed: [1: Operation not permitted]
nw_socket_connect [C1.1.1.4:3] connectx failed (fd 25) [1: Operation not permitted]
nw_socket_connect connectx failed [1: Operation not permitted]
nw_endpoint_flow_failed_with_error [C1.1.1.4 142.250.69.202:443 in_progress socket-flow (satisfied (Path is satisfied), interface: utun4, dns)] already failing, returning
nw_socket_connect [C1.1.1.5:3] connectx(25 (guarded), [srcif=0, srcaddr=<NULL>, dstaddr=172.217.14.234:443], SAE_ASSOCID_ANY, 0, NULL, 0, NULL, SAE_CONNID_ANY) failed: [1: Operation not permitted]
nw_socket_connect [C1.1.1.5:3] connectx failed (fd 25) [1: Operation not permitted]
nw_socket_connect connectx failed [1: Operation not permitted]
nw_endpoint_flow_failed_with_error [C1.1.1.5 172.217.14.234:443 in_progress socket-flow (satisfied (Path is satisfied), interface: utun4, dns)] already failing, returning
nw_socket_connect [C1.1.1.6:3] connectx(25 (guarded), [srcif=0, srcaddr=<NULL>, dstaddr=142.251.33.106:443], SAE_ASSOCID_ANY, 0, NULL, 0, NULL, SAE_CONNID_ANY) failed: [1: Operation not permitted]
nw_socket_connect [C1.1.1.6:3] connectx failed (fd 25) [1: Operation not permitted]
nw_socket_connect connectx failed [1: Operation not permitted]
nw_endpoint_flow_failed_with_error [C1.1.1.6 142.251.33.106:443 in_progress socket-flow (satisfied (Path is satisfied), interface: utun4, dns)] already failing, returning
nw_socket_connect [C1.1.1.7:3] connectx(25 (guarded), [srcif=0, srcaddr=<NULL>, dstaddr=142.251.211.234:443], SAE_ASSOCID_ANY, 0, NULL, 0, NULL, SAE_CONNID_ANY) failed: [1: Operation not permitted]
nw_socket_connect [C1.1.1.7:3] connectx failed (fd 25) [1: Operation not permitted]
nw_socket_connect connectx failed [1: Operation not permitted]
nw_endpoint_flow_failed_with_error [C1.1.1.7 142.251.211.234:443 in_progress socket-flow (satisfied (Path is satisfied), interface: utun4, dns)] already failing, returning
nw_socket_connect [C1.1.1.8:3] connectx(25 (guarded), [srcif=0, srcaddr=<NULL>, dstaddr=142.251.33.74:443], SAE_ASSOCID_ANY, 0, NULL, 0, NULL, SAE_CONNID_ANY) failed: [1: Operation not permitted]
nw_socket_connect [C1.1.1.8:3] connectx failed (fd 25) [1: Operation not permitted]
nw_socket_connect connectx failed [1: Operation not permitted]
nw_endpoint_flow_failed_with_error [C1.1.1.8 142.251.33.74:443 in_progress socket-flow (satisfied (Path is satisfied), interface: utun4, dns)] already failing, returning
nw_endpoint_flow_failed_with_error [C1.1.1.8 142.251.33.74:443 cancelled socket-flow ((null))] already failing, returning
Connection 1: received failure notification
Connection 1: failed to connect 1:1, reason -1
Connection 1: encountered error(1:1)
Task <DEFF15B9-BAC1-4282-96C3-EE94DB1D4DBF>.<1> HTTP load failed, 0/0 bytes (error code: 1 [1:1])
Task <DEFF15B9-BAC1-4282-96C3-EE94DB1D4DBF>.<1> finished with error [1] Error Domain=NSPOSIXErrorDomain Code=1 "Operation not permitted" UserInfo={_NSURLErrorFailingURLSessionTaskErrorKey=LocalDataTask <DEFF15B9-BAC1-4282-96C3-EE94DB1D4DBF>.<1>, _kCFStreamErrorDomainKey=1, _kCFStreamErrorCodeKey=1, _NSURLErrorRelatedURLSessionTaskErrorKey=(
"LocalDataTask <DEFF15B9-BAC1-4282-96C3-EE94DB1D4DBF>.<1>"
), _NSURLErrorNWPathKey=satisfied (Path is satisfied), interface: en0, ipv4, dns}
修改 macos/Runner/DebugProfile.entitlements
和 Release.entitlements
<key>com.apple.securitywork.client</key>
<true/>
<key>com.apple.securitywork.server</key>
<true/>
登陆实现
基于google_sign_in实现的多平台Google等登陆如下,支持Android, iOS, MacOS, Web等平台,其中android可以使用web端端client_id,macos可以使用ios端client_id。
如果要获取accessToken,可以打开代码中的注释代码。
代码语言:javascript代码运行次数:0运行复制import 'package:google_sign_in/google_sign_in.dart';
static const Map<String, String> googleClientId = {
"web": "488325119723-xxx1.apps.googleusercontent", // web, android
"windows": "488325119723-xxx2.apps.googleusercontent", //windows
"ios": "488325119723-xxx3.apps.googleusercontent" // ios. macos
};
// 支持Android, iOS, MacOS, Web等平台
Future<bool> signInWithGoogle() async {
try {
logger.i('start google login');
String platform = getPlatform();
if(platform == '') {
logger.e('Unknown platform!');
return false;
}
final GoogleSignIn googleSignIn = GoogleSignIn(
clientId: OAuthConfig.googleClientId[platform],
scopes: ['email', 'profile'],
);
GoogleSignInAccount? account;
if(kIsWeb){
account = await googleSignIn.signIn();
// account = await _googleSignIn.signInSilently();
}else {
account = await googleSignIn.signIn();
}
// 获取accessToken
// final GoogleSignInAuthentication googleSignInAuthentication = await account!.authentication;
// logger.i('accessToken :${googleSignInAuthentication.accessToken}');
_userId = account?.id;
_userName = account?.displayName;
_userEmail = account?.email;
_userPicture = account?.photoUrl;
_isAuthenticated = true;
logger.i(account);
logger.i('start google login success');
return true;
} catch (error) {
logger.i('start google login failed');
logger.i(error.toString());
}
return false;
}
String getPlatform() {
if (kIsWeb || Platform.isAndroid) {
return "web";
} else if (Platform.isWindows) {
return "windows";
} else if (Platform.isIOS || Platform.isMacOS) {
return "ios";
}
return '';
}
延伸
Windows的Google登陆探索
Windows的应用登陆可以参考Web的登陆,GoogleConsole中的回调地址可以通过nginx重定向为自定义的schema,比如notion://oauth2callback,或者直接中浏览器中输入自定义的schema地址即可拉起应用。
拉起应用需要提前中Windows修改注册表,参考如下:
如果要实现给应用传递参数,在可以参考如下方式传参。
在注册表的 command
键中,%1
会被替换为实际传递给应用程序的参数。当用户点击一个自定义 URL 协议(如 notion://
)时,完整的 URL(如 notion://open?page=123
)会作为参数传递给应用程序。
notion:/oauth2callback?state=eyJjYWxsYmFja1R5cGUiOiJuYXRpdmVyZWRpcmVjdCIsImVuY3J5cHRlZFRva2VuIjoidjAyOmxvZ2luX3dpdGhfZ29vZ2xlOkhNc3BWMV9kTXZqM0xEX2h2anhQRUN5QkIyVDNXenNPM1BnXzF0SFpVeFJiTUNHZ0ZnQ1dxOGhwQUNhdlN3RnNNWHdrN1MtaEU0LUFtUElpR0ZVeFAxLWk3VkxzeEZiYzNPNk0waEh6ZjdidTVvdXkxQm5FZXdkaDlrSVBHZjVRVWZkOCJ9&code=4%2F0AQSTgQEH6RpPdpU7DuXDGyQrt1JbmERJ0SPZQV0pRuskgqzF13aeIJIbX0v5_bdYHv4V3g&scope=email%20profile%20https%3A%2F%2Fwww.googleapis%2Fauth%2Fuserinfo.email%20https%3A%2F%2Fwww.googleapis%2Fauth%2Fuserinfo.profile%20openid&authuser=0&prompt=none&di=99c7d862c0ae4050b14bdf98ebdb6ad1
发布评论