Server-Side Blazor应用对接Google Calendar并发布事件问题求助
Google Calendar 一键同步发布事件整合方案
一、核心流程梳理
先明确一键操作的完整链路:
- 点击按钮触发Google OAuth 2.0授权流程
- 授权完成后获取访问令牌与刷新令牌
- 安全存储令牌(避免重复授权)
- 调用Google Calendar API创建目标事件
二、重定向问题修复
- 确保Google Cloud控制台中,OAuth客户端ID的重定向URI与代码中的回调地址完全一致(包括协议、域名、端口)
- 前端授权跳转需使用Google官方授权端点,示例代码:
const CLIENT_ID = '你的客户端ID'; const REDIRECT_URI = '你的回调地址'; const SCOPES = 'https://www.googleapis.com/auth/calendar.events'; function handleAuthClick() { const authUrl = `https://accounts.google.com/o/oauth2/v2/auth?client_id=${CLIENT_ID}&redirect_uri=${REDIRECT_URI}&scope=${encodeURIComponent(SCOPES)}&response_type=code&access_type=offline&prompt=consent`; window.location.href = authUrl; }
access_type=offline用于获取刷新令牌,支持后续静默刷新prompt=consent确保每次授权都能获取刷新令牌(避免缓存导致无法获取)
三、访问令牌存储方案
前端测试场景
用localStorage存储令牌,注意XSS风险:
// 回调页面获取授权码后,交换令牌 async function getTokenFromCode(code) { const tokenUrl = 'https://oauth2.googleapis.com/token'; const params = new URLSearchParams({ code: code, client_id: CLIENT_ID, client_secret: '你的客户端密钥', redirect_uri: REDIRECT_URI, grant_type: 'authorization_code' }); const response = await fetch(tokenUrl, { method: 'POST', body: params }); const tokenData = await response.json(); // 存储令牌 localStorage.setItem('google_access_token', tokenData.access_token); localStorage.setItem('google_refresh_token', tokenData.refresh_token); localStorage.setItem('token_expiry', Date.now() + tokenData.expires_in * 1000); }
生产环境建议
将令牌存储在后端数据库,前端仅持有会话ID,由后端负责令牌的获取、刷新与存储,避免密钥暴露在前端。
四、事件发布逻辑整合
获取有效令牌后,调用Calendar API创建事件:
async function createCalendarEvent() { let accessToken = localStorage.getItem('google_access_token'); const expiry = localStorage.getItem('token_expiry'); // 令牌过期则用刷新令牌更新 if (Date.now() > expiry) { accessToken = await refreshAccessToken(); } const event = { summary: '测试同步事件', location: '线上会议', description: '通过一键同步创建的Google Calendar事件', start: { dateTime: '2024-06-01T10:00:00+08:00', timeZone: 'Asia/Shanghai' }, end: { dateTime: '2024-06-01T11:00:00+08:00', timeZone: 'Asia/Shanghai' } }; const response = await fetch('https://www.googleapis.com/calendar/v3/calendars/primary/events', { method: 'POST', headers: { 'Authorization': `Bearer ${accessToken}`, 'Content-Type': 'application/json' }, body: JSON.stringify(event) }); const result = await response.json(); if (response.ok) { alert('事件发布成功!事件ID:' + result.id); } else { console.error('发布失败:', result); alert('事件发布失败,请检查授权状态'); } } // 刷新令牌函数 async function refreshAccessToken() { const refreshToken = localStorage.getItem('google_refresh_token'); const tokenUrl = 'https://oauth2.googleapis.com/token'; const params = new URLSearchParams({ refresh_token: refreshToken, client_id: CLIENT_ID, client_secret: '你的客户端密钥', grant_type: 'refresh_token' }); const response = await fetch(tokenUrl, { method: 'POST', body: params }); const tokenData = await response.json(); localStorage.setItem('google_access_token', tokenData.access_token); localStorage.setItem('token_expiry', Date.now() + tokenData.expires_in * 1000); return tokenData.access_token; }
五、一键按钮整合
将授权与事件发布逻辑串联,实现点击按钮完成全流程:
<button onclick="handleFullSync()">一键同步发布事件</button> <script> // 页面加载时检查是否已有有效令牌 window.onload = async function() { const accessToken = localStorage.getItem('google_access_token'); const expiry = localStorage.getItem('token_expiry'); if (accessToken && Date.now() < expiry) { // 已有有效令牌,可直接触发事件发布 } }; async function handleFullSync() { const accessToken = localStorage.getItem('google_access_token'); const expiry = localStorage.getItem('token_expiry'); if (accessToken && Date.now() < expiry) { // 已有有效令牌,直接发布事件 await createCalendarEvent(); } else { // 无有效令牌,触发授权流程 handleAuthClick(); } } // 回调页面处理:获取URL中的code参数,交换令牌后发布事件 const urlParams = new URLSearchParams(window.location.search); const code = urlParams.get('code'); if (code) { await getTokenFromCode(code); // 令牌获取成功后自动发布事件 await createCalendarEvent(); // 跳回原页面 window.location.href = '原页面地址'; } </script>
关键注意事项
- 前端不要暴露
client_secret,生产环境必须通过后端完成令牌交换 - 权限范围需确保包含
https://www.googleapis.com/auth/calendar.events(或按需选择更宽的calendar权限) - 测试时需将Google Cloud项目的OAuth同意屏幕状态设置为“外部”,并添加测试用户
内容的提问来源于stack exchange,提问作者alexandros gad
相关产品推荐
相关产品推荐

