谷歌+;跨客户端(android/web)身份验证登录

谷歌+;跨客户端(android/web)身份验证登录,android,google-api-php-client,google-api-client,Android,Google Api Php Client,Google Api Client,我正在尝试在应用程序中集成“登录谷歌”,该应用程序具有android和web组件。通过以下步骤,web组件中的所有内容都可以正常工作: 1.使用防伪令牌、客户端id和应用程序名称呈现视图 $state = md5(rand()); Session::set('state', $state); $this->view->render('login', array( 'CLIENT_ID' => 'my_web_client_id', 'STATE' => $

我正在尝试在应用程序中集成“登录谷歌”,该应用程序具有
android
和web组件。通过以下步骤,web组件中的所有内容都可以正常工作: 1.使用防伪令牌、客户端id和应用程序名称呈现视图

$state = md5(rand());
Session::set('state', $state);
$this->view->render('login', array(
    'CLIENT_ID' => 'my_web_client_id',
    'STATE' => $state,
    'APPLICATION_NAME' => 'my_app_name'));
二,。当用户单击Google的登录按钮时,从Google的服务器获取一次性代码并将其发送到我的服务器。 3.在我的服务器收到一次性代码后,使用该代码对用户进行身份验证

if ($_SESSION['state'] != $_POST['state']) { // Where state is the anti-forgery token
  return 'some error';
}

$code = $_POST['code'];
$client = new Google_Client();
$client->setApplicationName("my_app_name");
$client->setClientId('my_web_client_id');
$client->setClientSecret('client_secret');
$client->setRedirectUri('postmessage');
$client->addScope("https://www.googleapis.com/auth/urlshortener");
$client->authenticate($code);

$token = json_decode($client->getAccessToken());
// Verify the token
$reqUrl = 'https://www.googleapis.com/oauth2/v1/tokeninfo?access_token=' . $token->access_token;
$req = new Google_Http_Request($reqUrl);          
$tokenInfo = json_decode($client->getAuth()->authenticatedRequest($req)->getResponseBody());

// If there was an error in the token info, abort.
if ($tokenInfo->error) {
  return 'some error';
}

// Make sure the token we got is for our app.
if ($tokenInfo->audience != "my_web_client_id") {
  return 'some error';
}

// Saving user in db
...
// Load the app view
现在,android客户端应该是类似的,对吗?以下教程:和

onConnected
方法中执行异步任务

class CreateToken extends AsyncTask<Void, Void, String> {

    @Override
    protected String doInBackground(Void... voids) {
        oneTimeCode = getOneTimeCode();
        String email = getUserGPlusEmail();
        try {
            // Opens connection and sends the one-time code and email to the server with 'POST' request
            googleLogin(oneTimeCode, email);
        } catch (IOException e) {
            e.printStackTrace();
        }
        return oneTimeCode;
    }
}

private String getOneTimeCode() {

    String scopes = "oauth2:server:client_id:" + SERVER_CLIENT_ID + ":api_scope:" + SCOPE_EMAIL;
    String code = null;
    try {
        code = GoogleAuthUtil.getToken(
                LoginActivity.this,                                // Context context
                Plus.AccountApi.getAccountName(mGoogleApiClient),  // String accountName
                scopes                                             // String scope
        );

    } catch (IOException transientEx) {
        Log.e(Constants.TAG, "IOException");
        transientEx.printStackTrace();
        // network or server error, the call is expected to succeed if you try again later.
        // Don't attempt to call again immediately - the request is likely to
        // fail, you'll hit quotas or back-off.
    } catch (UserRecoverableAuthException e) {
        Log.e(Constants.TAG, "UserRecoverableAuthException");
        e.printStackTrace();
        // Requesting an authorization code will always throw
        // UserRecoverableAuthException on the first call to GoogleAuthUtil.getToken
        // because the user must consent to offline access to their data.  After
        // consent is granted control is returned to your activity in onActivityResult
        // and the second call to GoogleAuthUtil.getToken will succeed.
        startActivityForResult(e.getIntent(), AUTH_CODE_REQUEST_CODE);
    } catch (GoogleAuthException authEx) {
        // Failure. The call is not expected to ever succeed so it should not be
        // retried.
        Log.e(Constants.TAG, "GoogleAuthException");
        authEx.printStackTrace();
    } catch (Exception e) {
        throw new RuntimeException(e);
    }

    Log.e(Constants.TAG, "ONE TIME CODE: " + code);
    return code;
}

问题是认证每10-15分钟只工作一次。当在10-15分钟内多次尝试获取一次性代码时,我得到的代码与上一个代码相同(显然有问题。这只发生在android客户端,我得到了以下错误:
获取OAuth2访问令牌时出错,消息:“无效的授权:i”
)。在这里找不到有同样问题的人。可能是我做错了什么,但我想不出是什么原因……如果有任何帮助,我们将不胜感激。

您不应该每次都发送代码。在网络上,这是一种可以接受的方式,因为当你第一次同意时,你会得到一个允许你离线访问的代码(当你交换它时,你会在响应中看到一个刷新令牌),但在将来的情况下,你不会。在Android上,每次都会有一个代码给你一个刷新令牌,这意味着你每次都需要表示同意,你很可能会遇到每个用户的限制或缓存问题(就像你看起来那样)

您需要的神奇的额外组件是一个称为ID令牌的东西。你可以在两个平台上轻松找到这个人,并告诉你这个人是谁。有关更多信息,请查看此博客帖子:

ID标记的限制是,您不能使用它来调用Google API。它所做的只是给你谷歌用户ID,正在使用的应用程序的客户端ID和(如果使用了电子邮件范围)电子邮件地址。很好的一点是,在所有平台上都可以很容易地获得一个,用户交互更少,而且它们是经过加密签名的,因此大多数时候您可以使用它们,而无需在服务器上进行任何进一步的网络调用。如果您不需要进行Google API调用(因为您只是使用它进行身份验证),这是迄今为止最好使用的方法-考虑到您刚刚收到电子邮件,我倾向于到此为止

但是,如果您需要从服务器调用GoogleAPI,那么您应该使用该代码,但只需使用一次。交换时,将刷新令牌存储在根据用户ID设置密钥的数据库中。然后,当用户返回时,查找刷新令牌并使用它生成新的访问令牌。因此,流程将是:

第一次:

  • Android->Server:id令牌
  • 服务器->我没有刷新令牌
  • 安卓->服务器:代码
  • 其他时间:

  • Android->Server:id令牌
  • 服务器-我有一个代码,可以打电话 对于web,您可以使用相同的流或每次继续发送代码,但如果响应包含刷新令牌,则仍应在数据库中保留刷新令牌

    非常详细的回答:)谢谢你,伊恩!正是我需要的。
    $code = $_POST['code'];
    $client = new Google_Client();
    $client->setApplicationName("my_app_name");
    $client->setClientId('my_web_client_id');  // Web component's client id
    $client->setClientSecret('client_secret'); // Web component's secret
    $client->addScope("email");
    $client->setAccessType("offline");
    $client->authenticate($code);
    ...