实现服务器端授权

必须使用 OAuth 2.0 授权对 Gmail API 的请求 凭据。在开发应用时,您应该使用服务器端流程 需要代表用户访问 Google API,例如,当用户 离线使用。此方法要求将一次性授权代码从 从客户端复制到服务器此代码用于获取访问令牌 刷新服务器令牌

如需详细了解服务器端 Google OAuth 2.0 实现,请参阅 针对网络服务器应用使用 OAuth 2.0

目录

创建客户端 ID 和客户端密钥

要开始使用 Gmail API,您首先需要 设置工具,它会引导您在 Google API 控制台、启用 API 并创建凭据。

  1. 在“凭据”页面上,请依次点击创建凭据 > OAuth 客户端 ID 创建 OAuth 2.0 凭据,或依次点击创建凭据 > 服务账号密钥创建服务账号。
  2. 如果创建的是 OAuth 客户端 ID,请选择应用类型。
  3. 填写表单并点击创建

“凭据”页面现在会列出应用的客户端 ID 和服务账号密钥。您可以点击客户端 ID 查看详情,其中的具体参数会因 ID 类型而异,但可能包含电子邮件地址、客户端密钥、JavaScript 来源和重定向 URI。

记下客户端 ID,稍后您需要将其添加到代码中。

处理授权请求

用户首次加载您的应用时,会看到 一个对话框,授予应用程序访问其 Gmail 的权限 具有所请求权限范围的账号。在此初始配置之后, 授权,则只有在您的 应用的客户端 ID 更改或请求的范围已更改。

对用户进行身份验证

此初始登录返回一个授权结果对象,该对象包含 授权代码(如果成功)。

用授权代码换取访问令牌

授权代码是一次性代码,您的服务器可以使用该代码交换 访问令牌。系统会将此访问令牌传递给 Gmail API, 您的应用对用户数据的访问权限。

如果您的应用需要 offline 访问权限,那么在您的应用首次交换时 授权代码,还会收到一个刷新令牌,用于 在先前的令牌过期后收到新的访问令牌。您的应用 存储此刷新令牌(通常存储在您服务器上的数据库中) 供稍后使用。

以下代码示例演示了如何将授权代码交换为 一个具有 offline 访问权限并存储刷新令牌的访问令牌。

Python

CLIENTSECRETS_LOCATION 值替换为您所在位置 client_secrets.json 文件。

import logging
from oauth2client.client import flow_from_clientsecrets
from oauth2client.client import FlowExchangeError
from apiclient.discovery import build
# ...


# Path to client_secrets.json which should contain a JSON document such as:
#   {
#     "web": {
#       "client_id": "[[YOUR_CLIENT_ID]]",
#       "client_secret": "[[YOUR_CLIENT_SECRET]]",
#       "redirect_uris": [],
#       "auth_uri": "https://accounts.google.com/o/oauth2/auth",
#       "token_uri": "https://accounts.google.com/o/oauth2/token"
#     }
#   }
CLIENTSECRETS_LOCATION = '<PATH/TO/CLIENT_SECRETS.JSON>'
REDIRECT_URI = '<YOUR_REGISTERED_REDIRECT_URI>'
SCOPES = [
    'https://www.googleapis.com/auth/gmail.readonly',
    'https://www.googleapis.com/auth/userinfo.email',
    'https://www.googleapis.com/auth/userinfo.profile',
    # Add other requested scopes.
]

class GetCredentialsException(Exception):
  """Error raised when an error occurred while retrieving credentials.

  Attributes:
    authorization_url: Authorization URL to redirect the user to in order to
                       request offline access.
  """

  def __init__(self, authorization_url):
    """Construct a GetCredentialsException."""
    self.authorization_url = authorization_url


class CodeExchangeException(GetCredentialsException):
  """Error raised when a code exchange has failed."""


class NoRefreshTokenException(GetCredentialsException):
  """Error raised when no refresh token has been found."""


class NoUserIdException(Exception):
  """Error raised when no user ID could be retrieved."""


def get_stored_credentials(user_id):
  """Retrieved stored credentials for the provided user ID.

  Args:
    user_id: User's ID.
  Returns:
    Stored oauth2client.client.OAuth2Credentials if found, None otherwise.
  Raises:
    NotImplemented: This function has not been implemented.
  """
  # TODO: Implement this function to work with your database.
  #       To instantiate an OAuth2Credentials instance from a Json
  #       representation, use the oauth2client.client.Credentials.new_from_json
  #       class method.
  raise NotImplementedError()


def store_credentials(user_id, credentials):
  """Store OAuth 2.0 credentials in the application's database.

  This function stores the provided OAuth 2.0 credentials using the user ID as
  key.

  Args:
    user_id: User's ID.
    credentials: OAuth 2.0 credentials to store.
  Raises:
    NotImplemented: This function has not been implemented.
  """
  # TODO: Implement this function to work with your database.
  #       To retrieve a Json representation of the credentials instance, call the
  #       credentials.to_json() method.
  raise NotImplementedError()


def exchange_code(authorization_code):
  """Exchange an authorization code for OAuth 2.0 credentials.

  Args:
    authorization_code: Authorization code to exchange for OAuth 2.0
                        credentials.
  Returns:
    oauth2client.client.OAuth2Credentials instance.
  Raises:
    CodeExchangeException: an error occurred.
  """
  flow = flow_from_clientsecrets(CLIENTSECRETS_LOCATION, ' '.join(SCOPES))
  flow.redirect_uri = REDIRECT_URI
  try:
    credentials = flow.step2_exchange(authorization_code)
    return credentials
  except FlowExchangeError, error:
    logging.error('An error occurred: %s', error)
    raise CodeExchangeException(None)


def get_user_info(credentials):
  """Send a request to the UserInfo API to retrieve the user's information.

  Args:
    credentials: oauth2client.client.OAuth2Credentials instance to authorize the
                 request.
  Returns:
    User information as a dict.
  """
  user_info_service = build(
      serviceName='oauth2', version='v2',
      http=credentials.authorize(httplib2.Http()))
  user_info = None
  try:
    user_info = user_info_service.userinfo().get().execute()
  except errors.HttpError, e:
    logging.error('An error occurred: %s', e)
  if user_info and user_info.get('id'):
    return user_info
  else:
    raise NoUserIdException()


def get_authorization_url(email_address, state):
  """Retrieve the authorization URL.

  Args:
    email_address: User's e-mail address.
    state: State for the authorization URL.
  Returns:
    Authorization URL to redirect the user to.
  """
  flow = flow_from_clientsecrets(CLIENTSECRETS_LOCATION, ' '.join(SCOPES))
  flow.params['access_type'] = 'offline'
  flow.params['approval_prompt'] = 'force'
  flow.params['user_id'] = email_address
  flow.params['state'] = state
  return flow.step1_get_authorize_url(REDIRECT_URI)


def get_credentials(authorization_code, state):
  """Retrieve credentials using the provided authorization code.

  This function exchanges the authorization code for an access token and queries
  the UserInfo API to retrieve the user's e-mail address.
  If a refresh token has been retrieved along with an access token, it is stored
  in the application database using the user's e-mail address as key.
  If no refresh token has been retrieved, the function checks in the application
  database for one and returns it if found or raises a NoRefreshTokenException
  with the authorization URL to redirect the user to.

  Args:
    authorization_code: Authorization code to use to retrieve an access token.
    state: State to set to the authorization URL in case of error.
  Returns:
    oauth2client.client.OAuth2Credentials instance containing an access and
    refresh token.
  Raises:
    CodeExchangeError: Could not exchange the authorization code.
    NoRefreshTokenException: No refresh token could be retrieved from the
                             available sources.
  """
  email_address = ''
  try:
    credentials = exchange_code(authorization_code)
    user_info = get_user_info(credentials)
    email_address = user_info.get('email')
    user_id = user_info.get('id')
    if credentials.refresh_token is not None:
      store_credentials(user_id, credentials)
      return credentials
    else:
      credentials = get_stored_credentials(user_id)
      if credentials and credentials.refresh_token is not None:
        return credentials
  except CodeExchangeException, error:
    logging.error('An error occurred during code exchange.')
    # Drive apps should try to retrieve the user and credentials for the current
    # session.
    # If none is available, redirect the user to the authorization URL.
    error.authorization_url = get_authorization_url(email_address, state)
    raise error
  except NoUserIdException:
    logging.error('No user ID could be retrieved.')
  # No refresh token has been retrieved.
  authorization_url = get_authorization_url(email_address, state)
  raise NoRefreshTokenException(authorization_url)

使用存储的凭据进行授权

用户在首次授权成功后访问您的应用 则您的应用可以使用存储的刷新令牌为请求授权, 而无需再次提示用户

如果您已对用户进行身份验证,则您的应用程序可以检索 从数据库检索刷新令牌,并将该令牌存储在服务器端 会话。如果刷新令牌被撤消或因其他原因而无效, 您需要捕获这一情况并采取适当措施

使用 OAuth 2.0 凭据

检索 OAuth 2.0 凭据后,如 它们可用于授权 Gmail 服务对象 并向 API 发送请求

实例化服务对象

此代码示例展示了如何实例化服务对象,然后为其授权 发出 API 请求

Python

from apiclient.discovery import build
# ...

def build_service(credentials):
  """Build a Gmail service object.

  Args:
    credentials: OAuth 2.0 credentials.

  Returns:
    Gmail service object.
  """
  http = httplib2.Http()
  http = credentials.authorize(http)
  return build('gmail', 'v1', http=http)

发送已授权的请求并检查是否存在已吊销的凭据

以下代码段使用已获授权的 Gmail 服务实例 检索消息列表。

如果发生错误,该代码会检查 HTTP 401 状态代码, 应通过将用户重定向到授权页面来处理 网址。

更多 Gmail API 操作记录在 API 参考中。

Python

from apiclient import errors
# ...

def ListMessages(service, user, query=''):
  """Gets a list of messages.

  Args:
    service: Authorized Gmail API service instance.
    user: The email address of the account.
    query: String used to filter messages returned.
           Eg.- 'label:UNREAD' for unread Messages only.

  Returns:
    List of messages that match the criteria of the query. Note that the
    returned list contains Message IDs, you must use get with the
    appropriate id to get the details of a Message.
  """
  try:
    response = service.users().messages().list(userId=user, q=query).execute()
    messages = response['messages']

    while 'nextPageToken' in response:
      page_token = response['nextPageToken']
      response = service.users().messages().list(userId=user, q=query,
                                         pageToken=page_token).execute()
      messages.extend(response['messages'])

    return messages
  except errors.HttpError, error:
    print 'An error occurred: %s' % error
    if error.resp.status == 401:
      # Credentials have been revoked.
      # TODO: Redirect the user to the authorization URL.
      raise NotImplementedError()

后续步骤

熟悉了对 Gmail API 请求的授权后,您就可以 开始处理邮件、会话和标签,如 开发者指南部分。

如需详细了解可用的 API 方法,请参阅 API 参考文档