适用于服务器端应用的 Google 登录功能

<ph type="x-smartling-placeholder">

如要在用户离线时代表用户使用 Google 服务,您必须 使用混合服务器端流程,在客户端上向您的应用授权 并使用 JavaScript API 客户端发送一个特殊的一次性 授权代码发送到您的服务器。您的服务器会更换此一次性使用的存储空间 从 Google 获取自己的访问令牌和刷新令牌,以便服务器 能够自行进行 API 调用,此操作可在用户离线时完成。 这种一次性的代码流程与纯服务器端的 以及向服务器发送访问令牌的流程。

用于为服务器端获取访问令牌的登录流程 如下图所示。

一次性验证码具有若干安全优势。有了验证码,Google 直接向您的服务器提供令牌,而无需任何中介。 虽然我们不建议泄露代码,但代码很难用 不需要客户端密钥请妥善保管您的客户端密钥!

实现一次性代码流程

Google 登录按钮会同时提供访问令牌授权代码。该代码是一次性代码,您的服务器可以交换该代码 获取访问令牌。

以下示例代码演示了如何 一次性代码流程

如需使用一次性代码流程对 Google 登录功能进行身份验证,您需要:

第 1 步:创建客户端 ID 和客户端密钥

要创建客户端 ID 和客户端密钥,请先创建一个 Google API 控制台项目, 设置 OAuth 客户端 ID,并注册 JavaScript 源:

  1. 转到 Google API 控制台

  2. 从项目下拉菜单中,选择一个现有项目,或创建一个新项目 选择创建新项目

  3. 在边栏中的“API 和服务”,选择 Credentials,然后点击 配置同意屏幕

    选择电子邮件地址,指定产品名称,然后按保存

  4. 凭据标签页中,选择创建凭据下拉菜单 列表,然后选择 OAuth 客户端 ID

  5. 应用类型下,选择网页应用

    注册允许您的应用访问 Google API 集成,如下所述。源站是一个独有的协议组合 主机名和端口

    1. 已获授权的 JavaScript 来源字段中,为 。您可以输入多个源,以允许您的应用在其中运行 不同的协议、网域或子网域您不能使用通配符。 在下面的示例中,第二个网址可以是正式版网址。

      http://localhost:8080
      https://myproductionurl.example.com
      
    2. 已获授权的重定向 URI 字段无需填写值。重定向 URI 不与 JavaScript API 一起使用。

    3. 创建按钮。

  6. 在显示的 OAuth 客户端对话框中,复制客户端 ID。通过 客户端 ID 可让您的应用访问已启用的 Google API。

第 2 步:在网页上添加 Google 平台库

包含以下脚本,用于演示匿名函数, 将一个脚本插入到此 index.html 网页的 DOM 中。

<!-- The top of file index.html -->
<html itemscope itemtype="http://schema.org/Article">
<head>
  <!-- BEGIN Pre-requisites -->
  <script src="//ajax.googleapis.com/ajax/libs/jquery/1.8.2/jquery.min.js">
  </script>
  <script src="https://apis.google.com/js/client:platform.js?onload=start" async defer>
  </script>
  <!-- END Pre-requisites -->

第 3 步:初始化 GoogleAuth 对象

加载 auth2 库并调用 gapi.auth2.init() 以初始化 GoogleAuth 对象。指定您的客户端 ID 和您要请求的范围 当您调用 init() 时。

<!-- Continuing the <head> section -->
  <script>
    function start() {
      gapi.load('auth2', function() {
        auth2 = gapi.auth2.init({
          client_id: 'YOUR_CLIENT_ID.apps.googleusercontent.com',
          // Scopes to request in addition to 'profile' and 'email'
          //scope: 'additional_scope'
        });
      });
    }
  </script>
</head>
<body>
  <!-- ... -->
</body>
</html>

第 4 步:向您的页面添加登录按钮

将登录按钮添加到您的网页,并附加一个点击处理程序以调用 grantOfflineAccess() 启动一次性代码流程。

<!-- Add where you want your sign-in button to render -->
<!-- Use an image that follows the branding guidelines in a real app -->
<button id="signinButton">Sign in with Google</button>
<script>
  $('#signinButton').click(function() {
    // signInCallback defined in step 6.
    auth2.grantOfflineAccess().then(signInCallback);
  });
</script>

第 5 步:登录用户

用户点击登录按钮并向您的应用授予相应权限 请求的状态然后,调用您在 系统会向 grantOfflineAccess().then() 方法传递 JSON 对象和 授权代码。例如:

{"code":"4/yU4cQZTMnnMtetyFcIWNItG32eKxxxgXXX-Z4yyJJJo.4qHskT-UtugceFc0ZRONyF4z7U4UmAI"}

第 6 步:将授权代码发送到服务器

code 是您的一次性代码,您的服务器可以使用该代码交换自己的 访问令牌和刷新令牌刷新令牌的剩余时间过后 向用户显示了一个请求离线访问的授权对话框。 如果您已在select-account prompt OfflineAccessOptions 在第 4 步中,您必须存储检索到的刷新令牌以供日后使用 因为后续交换会针对刷新令牌返回 null。此流程 提供比标准 OAuth 2.0 流程更高的安全性。

系统始终会在交换有效授权时返回访问令牌 代码。

以下脚本定义了登录按钮的回调函数。时间 登录成功后,该函数会存储客户端的访问令牌 使用一次性代码并将其发送到位于相同网域的服务器。

<!-- Last part of BODY element in file index.html -->
<script>
function signInCallback(authResult) {
  if (authResult['code']) {

    // Hide the sign-in button now that the user is authorized, for example:
    $('#signinButton').attr('style', 'display: none');

    // Send the code to the server
    $.ajax({
      type: 'POST',
      url: 'http://example.com/storeauthcode',
      // Always include an `X-Requested-With` header in every AJAX request,
      // to protect against CSRF attacks.
      headers: {
        'X-Requested-With': 'XMLHttpRequest'
      },
      contentType: 'application/octet-stream; charset=utf-8',
      success: function(result) {
        // Handle or verify the server response.
      },
      processData: false,
      data: authResult['code']
    });
  } else {
    // There was an error.
  }
}
</script>

第 7 步:用授权代码换取访问令牌

在服务器上,用授权代码换取访问和刷新令牌。使用 代表用户调用 Google API 并(可选)存储 刷新令牌,以便在访问令牌到期时获取新的访问令牌。

如果您请求了个人资料访问权限,则还会获得 ID 令牌,其中包含基本 用户的个人资料信息。

例如:

Java
// (Receive authCode via HTTPS POST)


if (request.getHeader("X-Requested-With") == null) {
  // Without the `X-Requested-With` header, this request could be forged. Aborts.
}

// Set path to the Web application client_secret_*.json file you downloaded from the
// Google API Console: https://console.cloud.google.com/apis/credentials
// You can also find your Web application client ID and client secret from the
// console and specify them directly when you create the GoogleAuthorizationCodeTokenRequest
// object.
String CLIENT_SECRET_FILE = "/path/to/client_secret.json";

// Exchange auth code for access token
GoogleClientSecrets clientSecrets =
    GoogleClientSecrets.load(
        JacksonFactory.getDefaultInstance(), new FileReader(CLIENT_SECRET_FILE));
GoogleTokenResponse tokenResponse =
          new GoogleAuthorizationCodeTokenRequest(
              new NetHttpTransport(),
              JacksonFactory.getDefaultInstance(),
              "https://oauth2.googleapis.com/token",
              clientSecrets.getDetails().getClientId(),
              clientSecrets.getDetails().getClientSecret(),
              authCode,
              REDIRECT_URI)  // Specify the same redirect URI that you use with your web
                             // app. If you don't have a web version of your app, you can
                             // specify an empty string.
              .execute();

String accessToken = tokenResponse.getAccessToken();

// Use access token to call API
GoogleCredential credential = new GoogleCredential().setAccessToken(accessToken);
Drive drive =
    new Drive.Builder(new NetHttpTransport(), JacksonFactory.getDefaultInstance(), credential)
        .setApplicationName("Auth Code Exchange Demo")
        .build();
File file = drive.files().get("appfolder").execute();

// Get profile info from ID token
GoogleIdToken idToken = tokenResponse.parseIdToken();
GoogleIdToken.Payload payload = idToken.getPayload();
String userId = payload.getSubject();  // Use this value as a key to identify a user.
String email = payload.getEmail();
boolean emailVerified = Boolean.valueOf(payload.getEmailVerified());
String name = (String) payload.get("name");
String pictureUrl = (String) payload.get("picture");
String locale = (String) payload.get("locale");
String familyName = (String) payload.get("family_name");
String givenName = (String) payload.get("given_name");
Python
from apiclient import discovery
import httplib2
from oauth2client import client

# (Receive auth_code by HTTPS POST)


# If this request does not have `X-Requested-With` header, this could be a CSRF
if not request.headers.get('X-Requested-With'):
    abort(403)

# Set path to the Web application client_secret_*.json file you downloaded from the
# Google API Console: https://console.cloud.google.com/apis/credentials
CLIENT_SECRET_FILE = '/path/to/client_secret.json'

# Exchange auth code for access token, refresh token, and ID token
credentials = client.credentials_from_clientsecrets_and_code(
    CLIENT_SECRET_FILE,
    ['https://www.googleapis.com/auth/drive.appdata', 'profile', 'email'],
    auth_code)

# Call Google API
http_auth = credentials.authorize(httplib2.Http())
drive_service = discovery.build('drive', 'v3', http=http_auth)
appfolder = drive_service.files().get(fileId='appfolder').execute()

# Get profile info from ID token
userid = credentials.id_token['sub']
email = credentials.id_token['email']