5

我正在尝试设置“使用 Google 登录”按钮,允许人们在我的网站上购买商品。

客户端身份验证看起来很简单,但我很难理解服务器端身份验证的工作原理。在示例代码中,他们将客户端“code”参数传递给服务器,在服务器上可以用它换取访问令牌,然后可以使用该令牌查看用户的朋友列表。

但我不想看到用户的朋友列表。我只是想确定客户实际上是他们声称的那个人。

检索令牌后,示例代码将令牌放入会话中,并且似乎使用令牌的存在来验证用户是否已通过身份验证。那是正确的/安全的吗?当需要购买时,我的服务器是否应该(不)以某种方式(如何?)重新验证令牌?我是否应该在每次请求时不断向 Google 重新验证令牌?(希望不是?)

4

1 回答 1

2

在执行购买之前,您可能想要做的是通过将用户 ID 从客户端安全地传递到您的服务器并针对存储的凭据的用户 ID 进行验证,来验证用户是您期望的用户。这提供了针对重放攻击的额外保护,其中攻击者通过劫持他们的会话来假装是您网站的用户,并且是在接受用户付款之前进行的最相关检查。

我不会仅仅依靠用户验证作为防止欺诈的机制。您应该使用安全的支付系统,例如Google Commerce 平台,并遵循商业的最佳做法

提醒一下,每次初始化缓存的凭据时,都应使用 OAuth2 v2 端点检查您的令牌。检查每个请求似乎有点过分,因为您应该使用已经过验证并存储在服务器端的缓存凭据。最多,您可以在更新访问令牌时执行检查,但如果您信任您的刷新令牌,那么如果您在创建帐户并设置刷新令牌时执行检查,您应该足够安全。

除了在创建帐户时验证用户 ID 之外,还执行以下步骤:

  • 验证客户是您期望的人。这可以保护您免受伪造的访问令牌被传递到您的应用程序,以便使用您的配额代表攻击者有效地发出请求。
  • 验证该帐户是否由您的应用程序创建,这可以保护您和您的用户在代表用户创建其他帐户的情况下免受跨站点请求伪造。

正如您在链接的帖子中提到的,Google+ 快速入门中的示例代码应充分展示如何以各种编程语言执行这些检查以进行帐户授权。

在 HTML/JS 客户端中,以下代码显示了检索 userId(值,而不是特殊字符串“me”)以传递给 connect 方法以验证 Google+ userId 的位置:

  var request = gapi.client.plus.people.get( {'userId' : 'me'} );
  request.execute( function(profile) {
      $('#profile').empty();
      if (profile.error) {
        $('#profile').append(profile.error);
        return;
      }
      helper.connectServer(profile.id);
      $('#profile').append(
          $('<p><img src=\"' + profile.image.url + '\"></p>'));
      $('#profile').append(
          $('<p>Hello ' + profile.displayName + '!<br />Tagline: ' +
          profile.tagline + '<br />About: ' + profile.aboutMe + '</p>'));
      if (profile.cover && profile.coverPhoto) {
        $('#profile').append(
            $('<p><img src=\"' + profile.cover.coverPhoto.url + '\"></p>'));
      }
    });

...并且以下代码显示了正在传递的 Google+ id。

connectServer: function(gplusId) {
  console.log(this.authResult.code);
  $.ajax({
    type: 'POST',
    url: window.location.href + '/connect?state={{ STATE }}&gplus_id=' +
        gplusId,
    contentType: 'application/octet-stream; charset=utf-8',
    success: function(result) {
      console.log(result);
      helper.people();
    },
    processData: false,
    data: this.authResult.code
  });
}

Java 示例中执行这些检查的相关代码如下:

      // Check that the token is valid.
      Oauth2 oauth2 = new Oauth2.Builder(
          TRANSPORT, JSON_FACTORY, credential).build();
      Tokeninfo tokenInfo = oauth2.tokeninfo()
          .setAccessToken(credential.getAccessToken()).execute();
      // If there was an error in the token info, abort.
      if (tokenInfo.containsKey("error")) {
        response.status(401);
        return GSON.toJson(tokenInfo.get("error").toString());
      }
      // Make sure the token we got is for the intended user.
      if (!tokenInfo.getUserId().equals(gPlusId)) {
        response.status(401);
        return GSON.toJson("Token's user ID doesn't match given user ID.");
      }
      // Make sure the token we got is for our app.
      if (!tokenInfo.getIssuedTo().equals(CLIENT_ID)) {
        response.status(401);
        return GSON.toJson("Token's client ID does not match app's.");
      }
      // Store the token in the session for later use.
      request.session().attribute("token", tokenResponse.toString());
      return GSON.toJson("Successfully connected user.");
    } catch (TokenResponseException e) {
      response.status(500);
      return GSON.toJson("Failed to upgrade the authorization code.");
    } catch (IOException e) {
      response.status(500);
      return GSON.toJson("Failed to read token data from Google. " +
          e.getMessage());
    }

在示例中,ClientID 来自 Google API 控制台,并且对于您的应用程序来说是不同的。

于 2013-03-15T12:05:08.660 回答