南锋

南奔万里空,脱死锋镝余

在android实现Google的web登录

前言

由于业务上的需要,想要在android端实现Google登录。但是android的原生方法受到限制,实现起来比较麻烦。于是想到了一个曲线救国的方法,在android端使用Google的web登录。

实现逻辑

在andorid端拉起外部浏览器,用户登录Google账号,登录成功后,再将登录信息返回到app中。
这里需要注意,必须要跳转到外部浏览器,不能使用android自带的webview。因为google官方已经禁止了这种行为。如下图:
Google官方说明

实现步骤

  1. 创建一个google-login.html文件,使用 Google OAuth 登录,代码如下

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    <!DOCTYPE html>
    <html lang="zh-CN">
    <head>
    <meta charset="UTF-8">
    <title>Google 登录</title>
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <style>
    body {
    background-color: #000;
    color: #fff;
    font-family: Arial, sans-serif;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    height: 100vh;
    margin: 0;
    }
    button {
    padding: 12px 24px;
    font-size: 16px;
    border-radius: 8px;
    border: none;
    background-color: #4285f4;
    color: white;
    cursor: pointer;
    margin-top: 16px;
    }
    #status {
    margin-top: 20px;
    font-size: 14px;
    color: #0f0;
    }
    </style>
    </head>
    <body>

    <h2>使用 Google 登录</h2>
    <button onclick="loginWithGoogle()">登录</button>
    <div id="status"></div>

    <script>
    const clientId = '你谷歌登录的客户端id';

    function loginWithGoogle() {
    const redirectUri = encodeURIComponent('https://www.projectdtest.com/oauth2callback.html');
    const scope = encodeURIComponent('profile email openid');
    const authUrl = `https://accounts.google.com/o/oauth2/v2/auth?` +
    `client_id=${clientId}&` +
    `redirect_uri=${redirectUri}&` +
    `response_type=token&` +
    `scope=${scope}`;
    window.location.href = authUrl;
    }

    const urlParams = new URLSearchParams(window.location.search);
    const token = urlParams.get("google_token");

    if (token) {
    document.getElementById("status").innerText = "登录成功,正在拉取用户信息...";

    fetchUserInfo(token).then(userInfo => {
    document.getElementById("status").innerText = `欢迎你,${userInfo.name || userInfo.email}`;

    const message = {
    type: 'google_login',
    token: token,
    user: userInfo
    };

    if (window.ReactNativeWebView && window.ReactNativeWebView.postMessage) {
    window.ReactNativeWebView.postMessage(JSON.stringify(message));
    } else if (window.AndroidInterface && window.AndroidInterface.onReceiveToken) {
    window.AndroidInterface.onReceiveToken(JSON.stringify(message));
    } else if (window.parent !== window) {
    window.parent.postMessage(JSON.stringify(message), '*');
    } else {
    console.log("Token & 用户信息:", message);
    }
    }).catch(err => {
    document.getElementById("status").innerText = "用户信息拉取失败";
    console.error("用户信息错误", err);
    });
    }

    async function fetchUserInfo(token) {
    const res = await fetch("https://www.googleapis.com/oauth2/v2/userinfo", {
    headers: {
    Authorization: `Bearer ${token}`
    }
    });
    if (!res.ok) throw new Error("Google 用户信息获取失败");
    return await res.json();
    }
    </script>

    </body>
    </html>

    客户端id需要去https://console.cloud.google.com/获取,如下图:
    获取客户端id

  2. 创建一个oauth2callback.html,用来将google登录成功后返回的token和用户信息返回到app中。代码如下:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    <!DOCTYPE html>
    <html lang="zh-CN">
    <head>
    <meta charset="UTF-8">
    <title>Google 登录回调</title>
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    </head>
    <body>
    <script>
    async function fetchUserInfo(token) {
    const res = await fetch("https://www.googleapis.com/oauth2/v2/userinfo", {
    headers: {
    Authorization: `Bearer ${token}`
    }
    });
    if (!res.ok) throw new Error("获取用户信息失败");
    return await res.json();
    }

    (async () => {
    const hash = window.location.hash.substring(1);
    const params = new URLSearchParams(hash);
    const token = params.get("access_token");

    console.log("Access Token:", token);

    if (token) {
    try {
    const user = await fetchUserInfo(token);
    console.log("用户信息:", user);

    const scheme = `yourApp://google-login` +
    `?google_token=${encodeURIComponent(token)}` +
    `&id=${encodeURIComponent(user.id || '')}` +
    `&email=${encodeURIComponent(user.email || '')}` +
    `&name=${encodeURIComponent(user.name || '')}` +
    `&picture=${encodeURIComponent(user.picture || '')}`;

    window.location.href = scheme;

    } catch (err) {
    document.body.innerText = '用户信息获取失败,请重试';
    console.error(err);
    }
    } else {
    document.body.innerText = 'Google 登录失败,请重试';
    }
    })();
    </script>
    </body>
    </html>
  3. 将上面2个文件放在服务器上,在后台相应位置配置好即可。配置如下图:
    配置

  4. 修改 AppAndroidManifest.xml
    注册 URL scheme:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    <activity android:name=".AppActivity"
    android:exported="true"
    android:launchMode="singleTop">
    <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="yourApp" android:host="google-login" />
    </intent-filter>
    </activity>

    注意:这里的相关信息要改成你自己的。特别是android:schemeandroid:host要和oauth2callback.html中的schemehost保持一致。不然在web登录成功后无法唤起App。

  5. App中接收回调数据。
    5.1 在工程中新增一个BrowserCallback类,代码如下:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    package com.a.b ; // 注意这里要改成你自己的包名

    import android.content.Intent;
    import android.net.Uri;
    import android.util.Log;

    import com.cocos.lib.CocosHelper;
    import com.cocos.lib.CocosJavascriptJavaBridge;

    public class BrowserCallback {
    private static final String TAG = "BrowserCallback";
    private static BrowserCallback instance = null;

    public static BrowserCallback getInstance() {
    if (instance == null) {
    instance = new BrowserCallback();
    }
    return instance;
    }

    public void handleIntent(Intent intent) {
    Uri uri = intent.getData();
    if (uri != null && "ronaldoapp".equals(uri.getScheme()) && "google-login".equals(uri.getHost())) {
    String token = uri.getQueryParameter("google_token");
    String email = uri.getQueryParameter("email");
    String id = uri.getQueryParameter("id");
    String name = uri.getQueryParameter("name");
    String picture = uri.getQueryParameter("picture");

    String json = String.format("{\"token\":\"%s\", \"email\":\"%s\", \"id\":\"%s\", \"name\":\"%s\", \"picture\":\"%s\"}",
    token, email, id, name, picture);

    Log.d(TAG, "handleIntent: " + json);

    CocosHelper.runOnGameThread(() -> {
    String js = String.format("window.onGoogleLogin && window.onGoogleLogin(%s)", json);
    CocosJavascriptJavaBridge.evalString(js);
    });
    }
    }
    }

5.2 在AppAppActivity中注册BrowserCallback
onCreate方法中添加下面代码:

1
BrowserCallback.getInstance().handleIntent(getIntent()); // 处理首次唤起

再在AppActivity中添加下面代码:

1
2
3
4
5
6
7
@Override
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
SDKWrapper.shared().onNewIntent(intent);
// 添加这一行调用 BrowserCallback
com.cocos.browser.BrowserCallback.getInstance().handleIntent(intent);
}

总结

到这里整个登录流程就完成了。
如果你相关参数没有配置错的话,使用我的这套代码是可以实现Google登录的。

+