必须使用 OAuth 2.0 凭据对 Gmail API 的请求进行授权。当您的应用需要代表用户访问 Google API 时(例如,当用户处于离线状态时),您应使用服务器端流程。此方法需要将一次性授权代码从客户端传递到服务器;此代码用于为服务器获取访问令牌和刷新令牌。
如需详细了解服务器端 Google OAuth 2.0 实现,请参阅针对网络服务器应用使用 OAuth 2.0。
目录
创建客户端 ID 和客户端密钥
要开始使用 Gmail API,您需要先使用设置工具,该工具会引导您在 Google API 控制台中创建项目、启用 API 以及创建凭据。
- 在“凭据”页面上,请依次点击创建凭据 > OAuth 客户端 ID 创建 OAuth 2.0 凭据,或依次点击创建凭据 > 服务账号密钥创建服务账号。
- 如果创建的是 OAuth 客户端 ID,请选择应用类型。
- 填写表单并点击创建。
“凭据”页面现在会列出应用的客户端 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 方法。