本文档介绍了 OAuth 2.0、何时使用 OAuth、2 .0 如何获取客户端 ID,以及如何将其与 .NET 版 Google API 客户端库配合使用。
OAuth 2.0 协议
OAuth 2.0 是 Google API 使用的授权协议。您应阅读以下链接以熟悉该协议:
获取客户端 ID 和密钥
您可以在 Google API 控制台上获取客户端 ID 和密钥。客户端 ID 有多种不同类型,因此请务必为您的应用获取正确的类型:
在下面的每个代码段(服务帐号除外)中,您必须下载客户端密钥并将其作为 client_secrets.json
存储在您的项目中。
凭据
用户凭据
UserCredential
是一个线程安全的辅助类,用于通过访问令牌访问受保护的资源。访问令牌通常会在 1 小时后过期,不过,如果您尝试使用该令牌,则会收到错误消息。
UserCredential
和 AuthorizationCodeFlow
负责自动“刷新”令牌,即获取新的访问令牌。这是使用长期刷新令牌来完成的;如果您在授权代码流程中使用 access_type=offline
参数,系统会将该令牌与访问令牌一起接收。
对于大多数应用,建议将凭据的访问令牌和刷新令牌存储在永久性存储空间中。否则,您需要每小时向浏览器显示一次授权页面,因为访问令牌在您收到后一小时会失效。
如需确保访问令牌和刷新令牌持续有效,您可以提供自己的 IDataStore
实现,也可以使用库提供的以下实现之一:
-
适用于 .NET 的
FileDataStore
可确保该凭据在文件中保持持久性。
ServiceAccountCredential
ServiceAccountCredential
与 UserCredential
类似,但用途不同。Google OAuth 2.0 支持服务器到服务器的互动,例如 Web 应用和 Google Cloud Storage 之间的服务器到服务器互动。发出请求的应用必须证明自己的身份,才能访问 API,且最终用户无需参与其中。ServiceAccountCredential
存储一个私钥,用于为获取新访问令牌的请求签名。
UserCredential
和 ServiceAccountCredential
均实现了 IConfigurableHttpClientInitializer
,因此您可以将两者都注册为:
- 失败的响应处理程序,因此会在收到 HTTP
401
状态代码时刷新令牌。 - 拦截器,用于拦截每个请求中的
Authorization
标头。
已安装的应用
使用 Books API 的示例代码:
using System; using System.IO; using System.Threading; using System.Threading.Tasks; using Google.Apis.Auth.OAuth2; using Google.Apis.Books.v1; using Google.Apis.Books.v1.Data; using Google.Apis.Services; using Google.Apis.Util.Store; namespace Books.ListMyLibrary { /// <summary> /// Sample which demonstrates how to use the Books API. /// https://developers.google.com/books/docs/v1/getting_started /// <summary> internal class Program { [STAThread] static void Main(string[] args) { Console.WriteLine("Books API Sample: List MyLibrary"); Console.WriteLine("================================"); try { new Program().Run().Wait(); } catch (AggregateException ex) { foreach (var e in ex.InnerExceptions) { Console.WriteLine("ERROR: " + e.Message); } } Console.WriteLine("Press any key to continue..."); Console.ReadKey(); } private async Task Run() { UserCredential credential; using (var stream = new FileStream("client_secrets.json", FileMode.Open, FileAccess.Read)) { credential = await GoogleWebAuthorizationBroker.AuthorizeAsync( GoogleClientSecrets.Load(stream).Secrets, new[] { BooksService.Scope.Books }, "user", CancellationToken.None, new FileDataStore("Books.ListMyLibrary")); } // Create the service. var service = new BooksService(new BaseClientService.Initializer() { HttpClientInitializer = credential, ApplicationName = "Books API Sample", }); var bookshelves = await service.Mylibrary.Bookshelves.List().ExecuteAsync(); ... } } }
-
在此示例代码中,通过调用
GoogleWebAuthorizationBroker.AuthorizeAsync
方法创建新的UserCredential
实例。此静态方法可获取以下内容:- 客户端密钥(或指向客户端密钥的数据流)。
- 必需的范围。
- 用户标识符。
- 用于取消操作的取消令牌。
- 可选的数据存储区。如果未指定数据存储区,则默认为具有默认
Google.Apis.Auth
文件夹的FileDataStore
。该文件夹在Environment.SpecialFolder.ApplicationData
中创建。
-
此方法返回的
UserCredential
会设置为BooksService
(使用初始化程序)上的HttpClientInitializer
。如上所述,UserCredential
会实现 HTTP 客户端初始化程序。 -
请注意,在上面的示例代码中,客户端密钥信息从文件加载,但您还可以执行以下操作:
credential = await GoogleWebAuthorizationBroker.AuthorizeAsync( new ClientSecrets { ClientId = "PUT_CLIENT_ID_HERE", ClientSecret = "PUT_CLIENT_SECRETS_HERE" }, new[] { BooksService.Scope.Books }, "user", CancellationToken.None, new FileDataStore("Books.ListMyLibrary"));
请参阅我们的图书示例。
Web 应用 (ASP.NET Core 3)
Google API 支持适用于 Web 服务器应用的 OAuth 2.0。
Google.Apis.Auth.AspNetCore3 是推荐用于 ASP.NET Core 3 应用中大多数基于 Google 的 OAuth 2.0 场景的库。它实现了 Google 专用的 OpenIdConnect
身份验证处理程序。它支持增量身份验证,并定义了可注入的 IGoogleAuthProvider
,用于提供可与 Google API 搭配使用的 Google 凭据。
本部分介绍如何配置和使用 Google.Apis.Auth.AspNetCore3。此处显示的代码基于 Google.Apis.Auth.AspNetCore3.IntegrationTests,它是一个完全正常运行的标准 ASP.NET Core 3 应用。
如果您想按照本文档中的说明操作,则需要使用自己的 ASP.NET Core 3 应用,并完成这些步骤。
前提条件
- 安装 Google.Apis.Auth.AspNetCore3 软件包。
- 我们正在使用 Google Drive API,因此您还需要安装 Google.Apis.Drive.v3 软件包。
- 如果您还没有 Google Cloud 项目,请创建一个。请按照这些说明执行此操作。这将是系统识别您的应用的项目。
- 请务必启用 Google Drive API。如需启用 API,请按照这些说明操作。
-
创建授权凭据,以便 Google 识别您的应用。请按照这些说明创建授权凭据并下载
client_secrets.json
文件。两大亮点:- 请注意,这些凭据必须是 Web 应用。
- 如需运行此应用,您需要添加的唯一重定向 URI 是
https://localhost:5001/signin-oidc
。
将应用配置为使用 Google.Apis.Auth.AspNetCore3
Google.Apis.Auth.AspNetCore3 是在 Startup
类或您可能正在使用的类似替代方案中配置的。以下代码段是从 Google.Apis.Auth.AspNetCore3.IntegrationTests 项目中的 Startup.cs
中提取的。
-
将以下 using 指令添加到
Startup.cs
文件中。using Google.Apis.Auth.AspNetCore3;
-
在
Startup.ConfigureServices
方法中添加以下代码,将客户端 ID 和客户端密钥占位符更改为client_secrets.json
文件中包含的值。您可以直接从 JSON 文件加载这些值,也可以以任何其他安全方式存储。请参阅 Google.Apis.Auth.AspNetCore3.IntegrationTests 项目中的ClientInfo.Load
方法,获取有关如何直接从 JSON 文件加载这些值的示例。public void ConfigureServices(IServiceCollection services) { ... // This configures Google.Apis.Auth.AspNetCore3 for use in this app. services .AddAuthentication(o => { // This forces challenge results to be handled by Google OpenID Handler, so there's no // need to add an AccountController that emits challenges for Login. o.DefaultChallengeScheme = GoogleOpenIdConnectDefaults.AuthenticationScheme; // This forces forbid results to be handled by Google OpenID Handler, which checks if // extra scopes are required and does automatic incremental auth. o.DefaultForbidScheme = GoogleOpenIdConnectDefaults.AuthenticationScheme; // Default scheme that will handle everything else. // Once a user is authenticated, the OAuth2 token info is stored in cookies. o.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme; }) .AddCookie() .AddGoogleOpenIdConnect(options => { options.ClientId = {YOUR_CLIENT_ID}; options.ClientSecret = {YOUR_CLIENT_SECRET}; }); }
-
在
Startup.Configure
方法中,确保向流水线添加 ASP.NET Core 3 身份验证和授权中间件以及 HTTPS 重定向:public void Configure(IApplicationBuilder app, IWebHostEnvironment env) { ... app.UseHttpsRedirection(); ... app.UseAuthentication(); app.UseAuthorization(); ... }
使用用户凭据代表用户访问 Google API
您现在可以向您的控制器添加操作方法,这些方法要求用户凭据才能代表其访问 Google API。以下代码段展示了如何列出经过身份验证的用户的 Google 云端硬盘帐号上的文件。请注意以下两点:
-
用户不仅需要通过身份验证,而且还需要向您的应用授予
https://www.googleapis.com/auth/drive.readonly
范围(通过GoogleScopedAuthorize
属性指定)。 -
我们使用 ASP.NET Core 3 的标准依赖项注入机制来接收
IGoogleAuthProvider
,用于获取用户的凭据。
代码:
-
首先,将以下使用指令添加到您的控制器。
using Google.Apis.Auth.AspNetCore3; using Google.Apis.Auth.OAuth2; using Google.Apis.Drive.v3; using Google.Apis.Services;
-
添加控制器操作,如下所示(并附有一个用于接收
IList<string>
模型的简单视图):/// <summary> /// Lists the authenticated user's Google Drive files. /// Specifying the <see cref="GoogleScopedAuthorizeAttribute"> will guarantee that the code /// executes only if the user is authenticated and has granted the scope specified in the attribute /// to this application. /// </summary> /// <param name="auth">The Google authorization provider. /// This can also be injected on the controller constructor.</param> [GoogleScopedAuthorize(DriveService.ScopeConstants.DriveReadonly)] public async Task<IActionResult> DriveFileList([FromServices] IGoogleAuthProvider auth) { GoogleCredential cred = await auth.GetCredentialAsync(); var service = new DriveService(new BaseClientService.Initializer { HttpClientInitializer = cred }); var files = await service.Files.List().ExecuteAsync(); var fileNames = files.Files.Select(x => x.Name).ToList(); return View(fileNames); }
以上就是一些基本操作。您可以查看 Google.Apis.Auth.AspNetCore3.IntegrationTests 项目中的 HomeController.cs
,了解如何实现以下目标:
- 仅限用户身份验证,无特定范围
- 退出登录功能
- 通过代码进行增量授权。请注意,上面的代码段通过属性显示了增量授权。
- 检查当前授予的范围
- 检查访问令牌和刷新令牌
- 强制刷新访问令牌。请注意,您无需自行执行此操作,因为 Google.Apis.Auth.AspNetCore3 会检测访问令牌是否已过期或即将过期,并会自动刷新。
Web 应用 (ASP.NET MVC)
Google API 支持适用于 Web 服务器应用的 OAuth 2.0。为了成功运行以下代码,您必须先在 Google API 控制台中为您的项目添加重定向 URI。由于您将使用 FlowMetadata
及其默认设置,因此请将重定向 URI 设置为 your_site/AuthCallback/IndexAsync
。
如需查找 OAuth 2.0 凭据的重定向 URI,请执行以下操作:
- 打开 API 控制台中的“凭据”页面。
- 如果您尚未创建 OAuth 2.0 凭据,请点击创建凭据 > OAuth 客户端 ID(如果尚未创建)。
- 创建凭据后,点击 OAuth 2.0 客户端 ID 部分中的客户端 ID(适用于 Web 应用),以查看或修改重定向网址。
在 IDE 中创建新的 Web 应用项目后,请为 Drive、YouTube 或您要使用的其他服务添加正确的 Google.Apis
NuGet 软件包。然后,添加 Google.Apis.Auth.MVC 软件包。
以下代码演示了用于查询 Google API 服务的 ASP.NET MVC 应用。
-
添加您自己的
FlowMetadata
实现。using System; using System.Web.Mvc; using Google.Apis.Auth.OAuth2; using Google.Apis.Auth.OAuth2.Flows; using Google.Apis.Auth.OAuth2.Mvc; using Google.Apis.Drive.v2; using Google.Apis.Util.Store; namespace Google.Apis.Sample.MVC4 { public class AppFlowMetadata : FlowMetadata { private static readonly IAuthorizationCodeFlow flow = new GoogleAuthorizationCodeFlow(new GoogleAuthorizationCodeFlow.Initializer { ClientSecrets = new ClientSecrets { ClientId = "PUT_CLIENT_ID_HERE", ClientSecret = "PUT_CLIENT_SECRET_HERE" }, Scopes = new[] { DriveService.Scope.Drive }, DataStore = new FileDataStore("Drive.Api.Auth.Store") }); public override string GetUserId(Controller controller) { // In this sample we use the session to store the user identifiers. // That's not the best practice, because you should have a logic to identify // a user. You might want to use "OpenID Connect". // You can read more about the protocol in the following link: // https://developers.google.com/accounts/docs/OAuth2Login. var user = controller.Session["user"]; if (user == null) { user = Guid.NewGuid(); controller.Session["user"] = user; } return user.ToString(); } public override IAuthorizationCodeFlow Flow { get { return flow; } } } }
FlowMetadata
是一个抽象类,其中包含您自己的用于检索用户标识符的逻辑以及您使用的IAuthorizationCodeFlow
。在上面的示例代码中,系统会使用适当的范围、客户端密钥和数据存储区创建新的
GoogleAuthorizationCodeFlow
。考虑添加您自己的IDataStore
实现,例如,您可以编写一个使用EntityFramework
的实现。 -
实现您自己的使用 Google API 服务的控制器。以下示例使用的是
DriveService
:using System.Linq; using System.Threading; using System.Threading.Tasks; using System.Web.Mvc; using Google.Apis.Auth.OAuth2.Mvc; using Google.Apis.Drive.v2; using Google.Apis.Services; using Google.Apis.Sample.MVC4; namespace Google.Apis.Sample.MVC4.Controllers { public class HomeController : Controller { public async Task<ActionResult> IndexAsync(CancellationToken cancellationToken) { var result = await new AuthorizationCodeMvcApp(this, new AppFlowMetadata()). AuthorizeAsync(cancellationToken); if (result.Credential != null) { var service = new DriveService(new BaseClientService.Initializer { HttpClientInitializer = result.Credential, ApplicationName = "ASP.NET MVC Sample" }); // YOUR CODE SHOULD BE HERE.. // SAMPLE CODE: var list = await service.Files.List().ExecuteAsync(); ViewBag.Message = "FILE COUNT IS: " + list.Items.Count(); return View(); } else { return new RedirectResult(result.RedirectUri); } } } }
-
实现您自己的回调控制器。实现应如下所示:
using Google.Apis.Sample.MVC4; namespace Google.Apis.Sample.MVC4.Controllers { public class AuthCallbackController : Google.Apis.Auth.OAuth2.Mvc.Controllers.AuthCallbackController { protected override Google.Apis.Auth.OAuth2.Mvc.FlowMetadata FlowData { get { return new AppFlowMetadata(); } } } }
服务帐号
Google API 还支持服务帐号。与客户端应用请求访问最终用户数据的场景不同,服务帐号提供对客户端应用自身数据的访问权限。
您的客户端应用使用从 Google API 控制台下载的私钥对访问令牌请求进行签名。创建新的客户端 ID 后,您应该选择“服务帐号”应用类型,然后下载私钥。查看我们的使用 Google Plus API 的服务帐号示例。
using System; using System.Security.Cryptography.X509Certificates; using Google.Apis.Auth.OAuth2; using Google.Apis.Plus.v1; using Google.Apis.Plus.v1.Data; using Google.Apis.Services; namespace Google.Apis.Samples.PlusServiceAccount { /// <summary> /// This sample demonstrates the simplest use case for a Service Account service. /// The certificate needs to be downloaded from the Google API Console /// <see cref="https://console.cloud.google.com/"> /// "Create another client ID..." -> "Service Account" -> Download the certificate, /// rename it as "key.p12" and add it to the project. Don't forget to change the Build action /// to "Content" and the Copy to Output Directory to "Copy if newer". /// </summary> public class Program { // A known public activity. private static String ACTIVITY_ID = "z12gtjhq3qn2xxl2o224exwiqruvtda0i"; public static void Main(string[] args) { Console.WriteLine("Plus API - Service Account"); Console.WriteLine("=========================="); String serviceAccountEmail = "SERVICE_ACCOUNT_EMAIL_HERE"; var certificate = new X509Certificate2(@"key.p12", "notasecret", X509KeyStorageFlags.Exportable); ServiceAccountCredential credential = new ServiceAccountCredential( new ServiceAccountCredential.Initializer(serviceAccountEmail) { Scopes = new[] { PlusService.Scope.PlusMe } }.FromCertificate(certificate)); // Create the service. var service = new PlusService(new BaseClientService.Initializer() { HttpClientInitializer = credential, ApplicationName = "Plus API Sample", }); Activity activity = service.Activities.Get(ACTIVITY_ID).Execute(); Console.WriteLine(" Activity: " + activity.Object.Content); Console.WriteLine(" Video: " + activity.Object.Attachments[0].Url); Console.WriteLine("Press any key to continue..."); Console.ReadKey(); } } }
上面的示例代码会创建 ServiceAccountCredential
。设置了所需的范围,并调用了 FromCertificate
,该方法会从给定的 X509Certificate2
加载私钥。与所有其他示例代码一样,凭据设置为 HttpClientInitializer
。