如何使用Django allauth刷新Google Access Token无需用户重登录
如何在Django allauth中自动刷新Google Access Token(无需用户重新登录)
背景与现有配置
Google Access Token默认有效期1小时,我已经通过Django allauth配置了谷歌登录,并添加了日历只读权限,配置如下:
THIRD_PARTY_APPS = [ # ... "allauth.socialaccount", "allauth.socialaccount.providers.google", ] SITE_ID = 1 SOCIALACCOUNT_PROVIDERS = { "google": { "SCOPE": [ "profile", "email", "https://www.googleapis.com/auth/calendar.readonly", ], "AUTH_PARAMS": { "access_type": "offline", }, } } SOCIALACCOUNT_STORE_TOKENS = True
从数据库读取令牌并创建Google Credentials对象的代码:
# 获取用户的谷歌令牌 token = SocialToken.objects.get( account__user=request.user, account__provider="google" ) client_id = env("GOOGLE_CLIENT_ID", default="") client_secret = env("GOOGLE_CLIENT_SECRET", default="") from google.oauth2.credentials import Credentials creds = Credentials( token=token.token, refresh_token=token.token_secret, token_uri="https://oauth2.googleapis.com/token", client_id=client_id, client_secret=client_secret, )
令牌未过期时,能正常调用谷歌日历API获取事件,但令牌过期后调用creds.refresh(...)无法成功刷新,需要解决这个问题。
解决方案
1. 确认refresh_token已正确获取并存储
先检查数据库中SocialToken表的token_secret字段是否有值:
- 只有用户第一次授权时(且配置了
access_type=offline),谷歌才会返回refresh_token。如果之前未配置offline,需要让用户重新授权一次(可通过allauth的重新连接功能触发)。 - 确保
SOCIALACCOUNT_STORE_TOKENS=True,这是allauth将令牌存储到数据库的必要配置。
2. 正确调用刷新方法并更新数据库令牌
creds.refresh()需要传入请求对象,使用google.auth.transport.requests.Request()即可。刷新成功后,必须将新的access token(以及可能更新的refresh_token)保存回数据库,否则下次请求仍会使用旧令牌。
修改异常处理逻辑,加入刷新流程:
from googleapiclient.discovery import build from datetime import datetime from google.auth.transport.requests import Request service = build("calendar", "v3", credentials=creds) try: events_result = ( service.events() .list( calendarId="primary", timeMin=datetime.now().date().isoformat() + "Z", maxResults=30, singleEvents=True, orderBy="startTime", ) .execute() ) except Exception as e: print("Google API Error") if e.status_code == 403: if e.reason == "Request had insufficient authentication scopes.": print("用户需要授予日历权限") # 尝试刷新令牌 if creds.expired and creds.refresh_token: try: # 执行刷新 creds.refresh(Request()) # 更新数据库中的令牌 token.token = creds.token # 部分场景下refresh_token可能更新,保险起见同步 if creds.refresh_token: token.token_secret = creds.refresh_token token.save() # 刷新成功后重新请求 service = build("calendar", "v3", credentials=creds) events_result = service.events().list( calendarId="primary", timeMin=datetime.now().date().isoformat() + "Z", maxResults=30, singleEvents=True, orderBy="startTime", ).execute() # 继续处理events_result except Exception as refresh_e: print(f"令牌刷新失败: {refresh_e}") # 刷新失败时,可引导用户重新授权 else: print("无有效refresh_token,无法自动刷新")
3. 主动检查令牌状态,提前刷新
不要等API报错再处理,在创建Credentials对象后主动检查是否过期,提前刷新:
# 创建creds后立即检查 from google.auth.transport.requests import Request if creds.expired and creds.refresh_token: creds.refresh(Request()) # 更新数据库 token.token = creds.token if creds.refresh_token: token.token_secret = creds.refresh_token token.save() # 之后再构建服务调用API service = build("calendar", "v3", credentials=creds)
额外注意事项
SocialToken的expires_at字段由allauth自动维护,Credentials对象会根据这个字段判断令牌是否过期,确保该字段值正确。- 如果refresh_token失效(比如用户撤销了应用权限),刷新会失败,此时需要引导用户重新登录授权。
内容的提问来源于stack exchange,提问作者theptrk
相关产品推荐
相关产品推荐

