Triển khai ủy quyền phía máy chủ

Bạn phải uỷ quyền các yêu cầu gửi tới API Gmail qua OAuth 2.0 thông tin xác thực. Bạn nên sử dụng luồng phía máy chủ khi ứng dụng của bạn cần truy cập API của Google thay mặt cho người dùng, chẳng hạn như khi người dùng ngoại tuyến. Phương pháp này yêu cầu chuyển mã uỷ quyền một lần từ máy khách của bạn đến máy chủ của bạn; mã này sẽ được dùng để lấy mã truy cập và làm mới mã cho máy chủ của bạn.

Để tìm hiểu thêm về cách triển khai Google OAuth 2.0 phía máy chủ, hãy xem Sử dụng OAuth 2.0 cho ứng dụng máy chủ web.

Nội dung

Tạo mã ứng dụng khách và mật khẩu ứng dụng khách

Để bắt đầu sử dụng API Gmail, trước tiên, bạn cần sử dụng công cụ thiết lập. Công cụ này sẽ hướng dẫn bạn tạo dự án trong Google API Console, bật API và tạo thông tin đăng nhập.

  1. Trên trang Thông tin đăng nhập, hãy nhấp vào Tạo thông tin xác thực > Ứng dụng OAuth Mã nhận dạng để tạo thông tin xác thực OAuth 2.0 hoặc Tạo thông tin xác thực > Dịch vụ khoá tài khoản để tạo một tài khoản dịch vụ.
  2. Nếu bạn đã tạo mã ứng dụng OAuth, hãy chọn loại ứng dụng bạn dùng.
  3. Điền vào biểu mẫu rồi nhấp vào Tạo.

ID ứng dụng khách và khoá tài khoản dịch vụ của ứng dụng hiện được liệt kê trên trang Thông tin xác thực. Để biết thông tin chi tiết, hãy nhấp vào một mã ứng dụng khách; các thông số khác nhau tuỳ thuộc vào loại mã nhận dạng, nhưng có thể bao gồm địa chỉ email, mật khẩu ứng dụng khách, Nguồn gốc JavaScript hoặc URI chuyển hướng.

Ghi lại Mã ứng dụng khách vì bạn sẽ cần thêm mã vào mã sau này.

Xử lý yêu cầu uỷ quyền

Khi người dùng tải ứng dụng của bạn lần đầu tiên, họ sẽ thấy hộp thoại cấp quyền cho ứng dụng của bạn truy cập vào Gmail của họ tài khoản của bạn với phạm vi cấp quyền được yêu cầu. Sau tên viết tắt này cấp quyền, người dùng sẽ chỉ nhìn thấy hộp thoại cấp quyền nếu các thay đổi về mã ứng dụng khách của ứng dụng hoặc phạm vi được yêu cầu đã thay đổi.

Xác thực người dùng

Hoạt động đăng nhập ban đầu này trả về đối tượng kết quả ủy quyền chứa mã uỷ quyền nếu thành công.

Trao đổi mã uỷ quyền để lấy mã truy cập

Mã uỷ quyền là mã dùng một lần mà máy chủ của bạn có thể đổi lấy mã truy cập. Mã truy cập này được chuyển đến API của Gmail để cấp quyền truy cập của ứng dụng vào dữ liệu người dùng trong một khoảng thời gian giới hạn.

Nếu ứng dụng của bạn yêu cầu quyền truy cập vào offline, thì lần đầu tiên ứng dụng của bạn trao đổi mã uỷ quyền thì mã này cũng nhận được mã làm mới mà mã này dùng để nhận được mã truy cập mới sau khi mã thông báo trước đó đã hết hạn. Đơn đăng ký của bạn lưu trữ mã làm mới này (thường trong cơ sở dữ liệu trên máy chủ của bạn) để để sử dụng sau này.

Các mã mẫu sau minh hoạ việc trao đổi mã uỷ quyền cho mã truy cập có quyền truy cập offline và lưu trữ mã làm mới.

Python

Thay thế giá trị CLIENTSECRETS_LOCATION bằng vị trí của 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)

Đang uỷ quyền bằng thông tin đăng nhập đã lưu trữ

Khi người dùng truy cập vào ứng dụng của bạn sau khi uỷ quyền thành công lần đầu thì ứng dụng của bạn có thể sử dụng mã làm mới đã lưu trữ để cho phép yêu cầu mà không cần nhắc lại người dùng.

Nếu bạn đã xác thực người dùng, ứng dụng của bạn có thể truy xuất mã làm mới từ cơ sở dữ liệu và lưu trữ mã này ở phía máy chủ phiên hoạt động. Nếu mã làm mới bị thu hồi hoặc không hợp lệ, bạn sẽ cần phát hiện điều này và có biện pháp xử lý thích hợp.

Sử dụng thông tin đăng nhập OAuth 2.0

Sau khi thông tin đăng nhập OAuth 2.0 được truy xuất như hiển thị trong phần trước, chúng có thể được dùng để cấp quyền cho một đối tượng dịch vụ Gmail và gửi yêu cầu đến API.

Tạo thực thể cho một đối tượng dịch vụ

Mã mẫu này cho biết cách tạo thực thể cho một đối tượng dịch vụ rồi uỷ quyền để tạo yêu cầu 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)

Gửi yêu cầu được uỷ quyền và kiểm tra thông tin xác thực bị thu hồi

Đoạn mã sau đây sử dụng một phiên bản dịch vụ Gmail được uỷ quyền để truy xuất danh sách thư.

Nếu xảy ra lỗi, mã này sẽ kiểm tra mã trạng thái HTTP 401, cần được xử lý bằng cách chuyển hướng người dùng đến lệnh uỷ quyền URL.

Các thao tác khác của API Gmail được ghi lại trong Tài liệu tham khảo 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()

Các bước tiếp theo

Khi đã cảm thấy thoải mái khi cho phép các yêu cầu API của Gmail, bạn đã sẵn sàng bắt đầu xử lý thư, chuỗi và nhãn, như được mô tả trong Phần Hướng dẫn dành cho nhà phát triển.

Bạn có thể tìm hiểu thêm về các phương thức API có sẵn trong Tài liệu tham khảo API.