页面刷新后Google登录需重新授权问题求助
问题描述
- 点击Authorize按钮可完成Google登录,但刷新页面时会再次提示登录,希望实现用户主动登出前保持登录状态,当前使用JavaScript版Calendar API插入日历事件。
- 使用Google Calendar API时,页面重新加载无法保持登录状态,未找到相关解决方案。
解决方案:保持Google Calendar API登录状态跨页面刷新
核心思路是持久化存储认证token,并在页面加载时自动恢复登录状态,同时处理token过期的情况。以下是具体实现:
关键修改点
- 登录成功后将token存入
localStorage实现持久化 - 页面初始化API客户端后,读取本地token并验证有效性
- 用户登出时同步清除本地存储的token
修改后的完整代码
<!DOCTYPE html> <html> <head> <title>Google Calendar API Quickstart</title> <meta charset="utf-8" /> </head> <body> <p>Google Calendar API Quickstart</p> <!--Add buttons to initiate auth sequence and sign out--> <button id="authorize_button" onclick="handleAuthClick()">Authorize</button> <button id="signout_button" onclick="handleSignoutClick()">Sign Out</button> <button id="addto_cal" onclick="addto_cal()">Add to calendar</button> <pre id="content" style="white-space: pre-wrap;"></pre> <script type="text/javascript"> /* exported gapiLoaded */ /* exported gisLoaded */ /* exported handleAuthClick */ /* exported handleSignoutClick */ // TODO(developer): Set to client ID and API key from the Developer Console const CLIENT_ID = 'my_client_id'; const API_KEY = 'my_api_key'; // Discovery doc URL for APIs used by the quickstart const DISCOVERY_DOC = 'https://www.googleapis.com/discovery/v1/apis/calendar/v3/rest'; // Authorization scopes required by the API; multiple scopes can be // included, separated by spaces. const SCOPES = 'https://www.googleapis.com/auth/calendar'; let tokenClient; let gapiInited = false; let gisInited = false; document.getElementById('authorize_button').style.visibility = 'hidden'; document.getElementById('signout_button').style.visibility = 'hidden'; document.getElementById('addto_cal').style.visibility = 'hidden'; /** * Callback after api.js is loaded. */ function gapiLoaded() { gapi.load('client', intializeGapiClient); } /** * Callback after the API client is loaded. Loads the * discovery doc to initialize the API. */ async function intializeGapiClient() { await gapi.client.init({ apiKey: API_KEY, discoveryDocs: [DISCOVERY_DOC], }); gapiInited = true; // 页面加载时尝试恢复保存的token await restoreSavedToken(); maybeEnableButtons(); } /** * 恢复本地存储的token并验证有效性 */ async function restoreSavedToken() { const savedToken = localStorage.getItem('googleCalendarToken'); if (!savedToken) return; const token = JSON.parse(savedToken); // 检查token是否过期 if (Date.now() >= token.expires_at) { // token过期,尝试静默刷新 tokenClient.requestAccessToken({prompt: ''}); return; } // 恢复token到gapi客户端 gapi.client.setToken(token); // 更新UI状态 document.getElementById('signout_button').style.visibility = 'visible'; document.getElementById('authorize_button').innerText = 'Refresh'; document.getElementById('addto_cal').style.visibility = 'visible'; await listUpcomingEvents(); } /** * Callback after Google Identity Services are loaded. */ function gisLoaded() { tokenClient = google.accounts.oauth2.initTokenClient({ client_id: CLIENT_ID, scope: SCOPES, callback: '', // defined later }); gisInited = true; maybeEnableButtons(); } /** * Enables user interaction after all libraries are loaded. */ function maybeEnableButtons() { if (gapiInited && gisInited) { document.getElementById('authorize_button').style.visibility = 'visible'; } } /** * Sign in the user upon button click. */ function handleAuthClick() { tokenClient.callback = async (resp) => { if (resp.error !== undefined) { throw (resp); } // 将token存储到localStorage localStorage.setItem('googleCalendarToken', JSON.stringify(gapi.client.getToken())); document.getElementById('signout_button').style.visibility = 'visible'; document.getElementById('authorize_button').innerText = 'Refresh'; document.getElementById('addto_cal').style.visibility = 'visible'; await listUpcomingEvents(); }; if (gapi.client.getToken() === null) { // Prompt the user to select a Google Account and ask for consent to share their data // when establishing a new session. tokenClient.requestAccessToken({prompt: 'consent'}); } else { // Skip display of account chooser and consent dialog for an existing session. tokenClient.requestAccessToken({prompt: ''}); } } /** * Sign out the user upon button click. */ function handleSignoutClick() { const token = gapi.client.getToken(); if (token !== null) { google.accounts.oauth2.revoke(token.access_token); gapi.client.setToken(''); // 清除本地存储的token localStorage.removeItem('googleCalendarToken'); document.getElementById('content').innerText = ''; document.getElementById('authorize_button').innerText = 'Authorize'; document.getElementById('signout_button').style.visibility = 'hidden'; document.getElementById('addto_cal').style.visibility = 'hidden'; } } function addto_cal() { try { var resource = { "summary": "Vimal Bus Booking", "location": "Chennai", 'description': "Trichy to Chennai at 9:00 PM", "start": { "dateTime": "2022-08-30T10:00:00.000-07:00" }, "end": { "dateTime": "2022-09-01T10:25:00.000-07:00" } }; var request = gapi.client.calendar.events.insert({ 'calendarId': 'primary', 'resource': resource }); request.execute(function(resp) { console.log(resp); }); } catch (err) { document.getElementById('content').innerText = err.message; return; } } /** * Print the summary and start datetime/date of the next ten events in * the authorized user's calendar. If no events are found an * appropriate message is printed. */ async function listUpcomingEvents() { let response; try { const request = { 'calendarId': 'primary', 'timeMin': (new Date()).toISOString(), 'showDeleted': false, 'singleEvents': true, 'maxResults': 10, 'orderBy': 'startTime', }; response = await gapi.client.calendar.events.list(request); } catch (err) { document.getElementById('content').innerText = err.message; return; } const events = response.result.items; if (!events || events.length == 0) { document.getElementById('content').innerText = 'No events found.'; return; } // Flatten to string to display const output = events.reduce( (str, event) => `${str}${event.summary} (${event.start.dateTime || event.start.date})\n`, 'Events:\n'); document.getElementById('content').innerText = output; } </script> <script async defer src="https://apis.google.com/js/api.js" onload="gapiLoaded()"></script> <script async defer src="https://accounts.google.com/gsi/client" onload="gisLoaded()"></script> </body> </html>
功能说明
localStorage持久化存储token,避免页面刷新后丢失restoreSavedToken函数自动检查本地token,未过期则直接恢复登录状态,过期则静默刷新token- 登录成功后立即保存token,登出时同步清除本地存储
内容的提问来源于stack exchange,提问作者Vimal M
相关产品推荐
相关产品推荐

