不使用Node.js时如何实现Gmail API持久保持登录状态?
问题根因
每次刷新页面需要重复授权的核心原因是:
- 原有代码没有持久化存储授权得到的token,页面刷新后内存中的授权数据会被直接清空
- gapi客户端初始化流程中没有恢复已存储有效token的逻辑,无法识别用户之前的授权状态
- 缺少明确的登出逻辑来主动清除授权状态
实现方案
补充三个核心逻辑即可实现登录态持久化,用户主动登出前无需重复授权:
- 授权成功后将token存入
localStorage做持久化保存 - gapi客户端初始化完成后,检查本地存储的有效token,主动注入gapi客户端恢复登录态
- 新增登出方法,登出时清空内存、本地存储的token,同时撤销Google侧的授权凭证
修改后完整代码
let authBtn = document.getElementById('authorize_button'); let signoutBtn = document.getElementById('signout_button'); // 新增登出按钮元素 const CLIENT_ID = 'XXXXXX-XXXXXXXXXXX.apps.googleusercontent.com'; const API_KEY = 'XXXXXX-XXXXXXXXXXXXXXX'; const DISCOVERY_DOC = 'https://www.googleapis.com/discovery/v1/apis/gmail/v1/rest'; const SCOPES = 'https://www.googleapis.com/auth/gmail.readonly'; const TOKEN_STORAGE_KEY = 'gmail_api_auth_token'; // 本地存储token的自定义key let tokenClient; let gapiInited = false; let gisInited = false; authBtn.style.visibility = 'hidden'; signoutBtn.style.visibility = 'hidden'; function gapiLoaded() { gapi.load('client', intializeGapiClient); } async function intializeGapiClient() { await gapi.client.init({ apiKey: API_KEY, discoveryDocs: [DISCOVERY_DOC], }); // 初始化完成后尝试恢复本地存储的登录态 const savedToken = localStorage.getItem(TOKEN_STORAGE_KEY); if (savedToken) { const tokenObj = JSON.parse(savedToken); gapi.client.setToken(tokenObj); authBtn.innerText = 'Refresh'; signoutBtn.style.visibility = 'visible'; // 恢复登录态后直接拉取邮件,不需要重复触发授权流程 await getMessages(); } gapiInited = true; maybeEnableButtons(); } function gisLoaded() { tokenClient = google.accounts.oauth2.initTokenClient({ client_id: CLIENT_ID, scope: SCOPES, callback: '', }); gisInited = true; maybeEnableButtons(); } function maybeEnableButtons() { if (gapiInited && gisInited) { authBtn.style.visibility = 'visible'; } } function handleAuthClick() { tokenClient.callback = async (resp) => { if (resp.error !== undefined) throw (resp); // 授权成功后持久化存储token const currentToken = gapi.client.getToken(); localStorage.setItem(TOKEN_STORAGE_KEY, JSON.stringify(currentToken)); authBtn.innerText = 'Refresh'; signoutBtn.style.visibility = 'visible'; await getMessages(); }; if (gapi.client.getToken() === null) { // 首次无有效token时才弹出授权同意窗 tokenClient.requestAccessToken({prompt: 'consent'}); } else { // 已有有效token时静默续期,不会弹出任何用户交互窗口 tokenClient.requestAccessToken({prompt: ''}); } } // 新增登出逻辑 function handleSignoutClick() { const token = gapi.client.getToken(); if (token) { // 撤销Google侧记录的授权凭证 google.accounts.oauth2.revoke(token.access_token); // 清空gapi内存中存储的token gapi.client.setToken(null); // 清空本地持久化存储的token localStorage.removeItem(TOKEN_STORAGE_KEY); // 重置按钮状态 authBtn.innerText = 'Authorize'; signoutBtn.style.visibility = 'hidden'; } } // 给按钮绑定对应事件 authBtn.addEventListener('click', handleAuthClick); signoutBtn.addEventListener('click', handleSignoutClick);
注意事项
- 需要在页面上新增一个id为
signout_button的按钮元素作为登出入口 - Gmail API的access token默认有效期为1小时左右,只要用户没有主动登出、没有在Google账号侧撤销应用权限,调用
requestAccessToken({prompt: ''})会自动静默刷新token,不会弹出授权窗口打断操作 - 如果请求接口时返回401未授权错误,只需要在错误回调里重新触发一次静默续期逻辑即可
内容的提问来源于stack exchange,提问作者Extraterrestrial
相关产品推荐
相关产品推荐

