You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

.NET 5 MVC应用集成Google Calendar,如何跳过控制台获取用户凭证?

.NET 5 MVC Google Calendar OAuth流程优化方案

核心优化思路

将本地credentials.json的配置迁移至应用配置中心,使用Google官方OAuth客户端库替代手动拼接授权URL,同时通过应用内登录校验确保用户无需接触Google控制台,仅需完成应用登录即可触发授权流程,并将凭证自动存入自有数据库。

步骤1:迁移凭证配置到appsettings.json

将Google控制台获取的客户端信息移至应用配置文件,避免依赖本地文件:

"GoogleCalendarSettings": {
  "ClientId": "你的Google客户端ID",
  "ClientSecret": "你的Google客户端密钥",
  "RedirectUri": "https://localhost:44311/Reservation/CalendarCallback",
  "Scopes": [
    "https://www.googleapis.com/auth/calendar",
    "https://www.googleapis.com/auth/calendar.events"
  ]
}

步骤2:安装Google官方授权库

通过NuGet安装依赖包:

Install-Package Google.Apis.Auth.Mvc
Install-Package Google.Apis.Calendar.v3
Install-Package Newtonsoft.Json

步骤3:改造授权跳转与回调逻辑

替换手动拼接URL的方式,使用官方库生成授权请求,并处理回调获取凭证:

using Google.Apis.Auth.OAuth2;
using Google.Apis.Auth.Mvc;
using Google.Apis.Calendar.v3;
using Microsoft.AspNetCore.Authorization;
using Microsoft.Extensions.Configuration;
using Newtonsoft.Json;
using System.Threading;
using System.Threading.Tasks;

public class ReservationController : Controller
{
    private readonly IConfiguration _configuration;
    private readonly YourDbContext _dbContext;

    public ReservationController(IConfiguration configuration, YourDbContext dbContext)
    {
        _configuration = configuration;
        _dbContext = dbContext;
    }

    // 要求用户先完成应用登录
    [Authorize]
    public ActionResult InitiateCalendarAuth()
    {
        var settings = _configuration.GetSection("GoogleCalendarSettings");
        var clientSecrets = new ClientSecrets
        {
            ClientId = settings["ClientId"],
            ClientSecret = settings["ClientSecret"]
        };

        var flow = new GoogleAuthorizationCodeFlow(new GoogleAuthorizationCodeFlow.Initializer
        {
            ClientSecrets = clientSecrets,
            Scopes = settings.GetSection("Scopes").Get<string[]>(),
            DataStore = new DatabaseDataStore(_dbContext)
        });

        var authorizationUrl = flow.CreateAuthorizationCodeRequest(settings["RedirectUri"])
            .SetAccessType("offline")
            .SetIncludeGrantedScopes(true)
            .SetState(User.Identity.Name) // 用登录用户标识关联凭证
            .Build();

        return Redirect(authorizationUrl.ToString());
    }

    // Google授权回调处理
    public async Task<ActionResult> CalendarCallback(string code, string state)
    {
        var settings = _configuration.GetSection("GoogleCalendarSettings");
        var clientSecrets = new ClientSecrets
        {
            ClientId = settings["ClientId"],
            ClientSecret = settings["ClientSecret"]
        };

        var flow = new GoogleAuthorizationCodeFlow(new GoogleAuthorizationCodeFlow.Initializer
        {
            ClientSecrets = clientSecrets,
            Scopes = settings.GetSection("Scopes").Get<string[]>(),
            DataStore = new DatabaseDataStore(_dbContext)
        });

        // 交换授权码获取凭证,自动存入数据库
        await flow.ExchangeCodeForTokenAsync(
            userId: state,
            code: code,
            redirectUri: settings["RedirectUri"],
            cancellationToken: CancellationToken.None);

        return RedirectToAction("Calendar");
    }
}

步骤4:实现数据库凭证存储

自定义IDataStore实现,将凭证直接存入自有数据库:

using Google.Apis.Util.Store;
using Microsoft.EntityFrameworkCore;
using Newtonsoft.Json;
using System.Threading;
using System.Threading.Tasks;

public class DatabaseDataStore : IDataStore
{
    private readonly YourDbContext _dbContext;

    public DatabaseDataStore(YourDbContext dbContext)
    {
        _dbContext = dbContext;
    }

    public async Task StoreAsync<T>(string key, T value)
    {
        var json = JsonConvert.SerializeObject(value);
        var credential = await _dbContext.UserCalendarCredentials
            .FirstOrDefaultAsync(c => c.UserId == key);

        if (credential != null)
        {
            credential.CredentialJson = json;
        }
        else
        {
            _dbContext.UserCalendarCredentials.Add(new UserCalendarCredential
            {
                UserId = key,
                CredentialJson = json
            });
        }

        await _dbContext.SaveChangesAsync();
    }

    public async Task<T> GetAsync<T>(string key)
    {
        var credential = await _dbContext.UserCalendarCredentials
            .FirstOrDefaultAsync(c => c.UserId == key);

        return credential == null ? default : JsonConvert.DeserializeObject<T>(credential.CredentialJson);
    }

    public async Task DeleteAsync<T>(string key)
    {
        var credential = await _dbContext.UserCalendarCredentials
            .FirstOrDefaultAsync(c => c.UserId == key);

        if (credential != null)
        {
            _dbContext.UserCalendarCredentials.Remove(credential);
            await _dbContext.SaveChangesAsync();
        }
    }

    public async Task ClearAsync()
    {
        _dbContext.UserCalendarCredentials.RemoveRange(_dbContext.UserCalendarCredentials);
        await _dbContext.SaveChangesAsync();
    }
}

// 数据库实体类示例
public class UserCalendarCredential
{
    public int Id { get; set; }
    public string UserId { get; set; }
    public string CredentialJson { get; set; }
}

优化效果

  • 移除本地文件依赖,配置集中管理,适配部署场景
  • 官方库处理授权流程,避免手动拼接URL的潜在错误
  • 应用登录校验前置,用户无需接触Google控制台
  • 凭证自动关联登录用户并存入数据库,无需手动操作

内容的提问来源于stack exchange,提问作者Wannes Limbourg

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.11 04:10:13