65.9K
CodeProject 正在变化。 阅读更多。
Home

ADO.NET 实现 Google API IDataStore

starIconstarIconstarIconstarIconstarIcon

5.00/5 (4投票s)

2016 年 3 月 26 日

CPOL

2分钟阅读

viewsIcon

21637

downloadIcon

332

Google API IDataStore 的简单实现, 使用 MS-SQL 服务器存储您的 token 文件。

引言

在过去的几天里,我一直在做一个关于 Google OAuth Gmail API 的项目,但我需要在数据库中存储我的 Google 令牌。由于 Google 的实现只是将通信令牌存储在文件中,因此我必须实现一个自定义类来完成此操作。

背景

Google Gmail API 提供了许多非常有用的服务,但采用了新的 OAuth 安全技术。 尽管这种协议具有更好的安全性,但它迫使我们升级我们的电子邮件系统。

本文没有提供安装 OAuth G-Mail 服务的教程。 如果您需要,我建议您访问 Google 提供的以下链接:https://developers.Google.com/identity/protocols/OAuth2

该服务的基础知识包括以下操作

  • 您使用在 Google 开发控制台中注册时获得的凭据向 Gmail API 请求服务。
  • 由于此凭据仅用于识别您,因此 Gmail 会返回一个网页登录页面和/或一个网页,该网页允许您在系统中进行身份验证,并授权您程序请求的服务。
  • 如果您授权,则会将令牌信息下载到您那里。 通常,此信息由 Google API 管理,并将其存储在您计算机上的存储文件中。

对我们来说,问题在于我们需要将凭据存储在数据库表中。

为此,我们实现了 Google API 提供的 IDataStore 接口,以自定义我们的令牌存储。

Using the Code

我们使用 C# 为 ASP.NET 经典应用程序进行开发。 然后,我们创建一个实现 IDataStore 的类,以将令牌信息存储在我们的数据库中。

该类的代码如下所示

using System;
using System.Data.SqlClient;
using System.Threading.Tasks;
using Google.Apis.Json;
using Google.Apis.Util.Store;

namespace GMailApiQuickStart
{
    /// <summary>
    /// Store Gmail token in the database
    /// The information is stored in table OauthToken
    /// OAuth token has only three fields 
    /// ID UserKey and Token
    /// </summary>
    public class DbDataStore : IDataStore
    {
        private readonly string conextionDb;

        /// <summary>
        /// Constructor. Get the conexion string
        /// for your database.
        /// </summary>
        /// <param name="conexionString"></param>
        public DbDataStore(string conexionString)
        {
            conextionDb = conexionString;
        }

        #region Implementation of IDataStore

        /// <summary>
        /// Asynchronously stores the given value for the given key (replacing any existing value).
        /// </summary>
        /// <typeparam name="T">The type to store in the data store.</typeparam>
        /// <param name="key">The key.</param>
        /// <param name="value">The value to store.</param>
        public Task StoreAsync<T>(string key, T value)
        {
            if (string.IsNullOrEmpty(key))
            {
                throw new ArgumentException("Key MUST have a value");
            }

            string contents = NewtonsoftJsonSerializer.Instance.Serialize((object)value);
            var conn = new SqlConnection(conextionDb);
            var comm = new SqlCommand("SELECT COUNT(*) FROM OAuthToken WHERE UserKey = @Param1", conn);
            comm.Parameters.AddWithValue("@Param1", key);
            conn.Open();
            try
            {
                var res = comm.ExecuteScalar();
                if ((int)res == 0)
                {
                    // Insert token
                    comm = new SqlCommand("INSERT INTO OAuthToken (UserKey, Token) 
                                           VALUES (@Param1, @Param2)", conn);
                    comm.Parameters.AddWithValue("@Param1", key);
                    comm.Parameters.AddWithValue("@Param2", contents);
                }
                else
                {
                    //Update token
                    comm = new SqlCommand("UPDATE OAuthToken SET Token = @Param2 
                                           WHERE UserKey = @Param1", conn);
                    comm.Parameters.AddWithValue("@Param1", key);
                    comm.Parameters.AddWithValue("@Param2", contents);
                }

                var exec = comm.ExecuteNonQuery();
            }
            finally
            {
                conn.Close();
            }

            return TaskEx.Delay(0);
        }

        /// <summary>
        /// Asynchronously deletes the given key. The type is provided here as well 
        /// because the "real" saved key should
        /// contain type information as well, so the data store will be able to store 
        /// the same key for different types.
        /// </summary>
        /// <typeparam name="T">
        /// The type to delete from the data store.
        /// </typeparam>
        /// <param name="key">The key to delete.</param>
        public Task DeleteAsync<T>(string key)
        {
            var conn = new SqlConnection(conextionDb);
            var comm = new SqlCommand("DELETE OAuthToken WHERE UserKey = @Param1", conn);
            comm.Parameters.AddWithValue("@Param1", key);
            conn.Open();
            try
            {
                var res = comm.ExecuteScalar();
            }
            finally
            {
                conn.Close();
            }

            return TaskEx.Delay(0);
        }

        /// <summary>
        /// Asynchronously returns the stored value for the given key or <c>null</c> if not found.
        /// </summary>
        /// <typeparam name="T">The type to retrieve from the data store.</typeparam>
        /// <param name="key">The key to retrieve its value.</param>
        /// <returns>
        /// The stored object.
        /// </returns>
        public Task<T> GetAsync<T>(string key)
        {
            if (string.IsNullOrEmpty(key))
            {
                throw new ArgumentException("Key MUST have a value");
            }

            TaskCompletionSource<T> completionSource = new TaskCompletionSource<T>();
            var conn = new SqlConnection(conextionDb);
            var comm = new SqlCommand("SELECT Token FROM OAuthToken WHERE UserKey = @Param1", conn);
            comm.Parameters.AddWithValue("@Param1", key);
            conn.Open();
            try
            {
                var res = comm.ExecuteScalar();
                if (res == null || string.IsNullOrWhiteSpace(res.ToString()))
                {
                    completionSource.SetResult(default(T));
                }
                else
                {
                     completionSource.SetResult(NewtonsoftJsonSerializer
                                                .Instance.Deserialize<T>(res.ToString()));
                }
            }
            catch (Exception ex)
            {
                completionSource.SetException(ex);
            }
            finally
            {
                conn.Close();
            }
            return completionSource.Task;
        }

        /// <summary>
        /// Asynchronously clears all values in the data store.
        /// </summary>
        public Task ClearAsync()
        {
            var conn = new SqlConnection(conextionDb);
            var comm = new SqlCommand("TRUNCATE TABLE OAuthToken", conn);
            conn.Open();
            try
            {
                var res = comm.ExecuteNonQuery();
            }

            finally
            {
                conn.Close();
            }

            return TaskEx.Delay(0);
        }

        #endregion
    }
}

要使用此类,您需要为其提供您的连接字符串。 我使用 Google API Gmail 快速入门的修改版本进行了测试。 您可以在此处找到原始代码和安装程序。

我在代码中包含了一个脚本,用于在测试环境中进行测试。 当然,您可以更改类中的查询以使其适应您的系统。

要进行测试,我们建议您下载快速入门并安装在您的计算机上,使用本文的附带脚本创建数据库,并对其进行修改。 DbDataStore 类的代码也在 zip 文件中。

using System;
using System.Collections.Generic;
using System.Configuration;
using System.IO;
using System.Threading;
using Google.Apis.Auth.OAuth2;
using Google.Apis.Gmail.v1;
using Google.Apis.Gmail.v1.Data;
using Google.Apis.Services;

namespace GMailApiQuickStart
{
    // This is a modified version of Gmail API Net Quickstart from Google
    class Program
    {
        // If modifying these scopes, delete your previously saved credentials
        // at ~/.credentials/gmail-dotnet-quickstart.json
        static string[] Scopes = { GmailService.Scope.GmailReadonly };
        static string ApplicationName = "Gmail API .NET Quickstart";

        static void Main(string[] args)
        {
            UserCredential credential;

            using (var stream =
                new FileStream("client_secret.json", FileMode.Open, FileAccess.Read))
            {
                var conexion = ConfigurationManager.ConnectionStrings["Google"].ConnectionString;

                credential = GoogleWebAuthorizationBroker.AuthorizeAsync(
                    GoogleClientSecrets.Load(stream).Secrets,
                    Scopes,
                    "user",
                    CancellationToken.None,
                    new DbDataStore(conexion)).Result;
            }

            // Create Gmail API service.
            var service = new GmailService(new BaseClientService.Initializer()
            {
                HttpClientInitializer = credential,
                ApplicationName = ApplicationName,
            });

            // Define parameters of request.
            UsersResource.LabelsResource.ListRequest request = service.Users.Labels.List("me");

            // List labels.
            IList<Label> labels = request.Execute().Labels;
            Console.WriteLine("Labels:");
            if (labels != null && labels.Count > 0)
            {
                foreach (var labelItem in labels)
                {
                    Console.WriteLine("{0}", labelItem.Name);
                }
            }
            else
            {
                Console.WriteLine("No labels found.");
            }
            Console.Read();
        }
    }
} 

如果您在调用 GoogleWebAuthorizationBroker 时看到,您将 FileStore 参数更改为本文中描述的 DbDataStore 类。 就这些了!

关注点

您可以扩展这个想法,并将密钥文件也放入您的数据库中。 然后,所有 Google OAuth 的管理都可以转换为数据库,而不是使用系统中的文件。

历史

  • 第一版

注意:Google 和 G-Mail 是 Google 的注册商标。

© . All rights reserved.