Django集成Google OAuth 存储凭证刷新后身份认证失败
问题根因
你的代码存在3个核心错误,直接导致凭证无法持久化生效:
- 凭证更新语法错误:在
oauth_redir视图更新已存在的用户凭证时,每个字段赋值末尾多余的逗号会让Python将值包装为单元素元组,最终存入数据库的client_id、client_secret、token等字段全是元组类型而非字符串,加载凭证时SDK读到的参数完全非法,直接触发invalid_client: The OAuth client was not found报错。 - 凭证字段存储缺失:你的
Emp_google_creds模型没有存储凭证的expiry(过期时间)字段,从数据库加载凭证时,Google SDK无法判断token有效期,会直接将凭证标记为无效。 - 授权配置与刷新逻辑缺失:初始化OAuth授权流程时没有配置
access_type='offline',Google不会返回可长期使用的refresh_token,短期access_token(有效期1小时)过期后没有自动刷新的依据;同时你将凭证刷新的代码全部注释,即使存在合法refresh_token也不会触发刷新动作。
修复步骤
- 修正凭证更新的语法错误,删除
oauth_redir中更新凭证块所有字段赋值末尾的逗号,同时补充过期时间的存储逻辑:
# 修正后的更新逻辑 else: creds=Emp_google_creds.objects.get(employee=u) creds.token = credentials.token creds.refresh_token = credentials.refresh_token creds.token_uri = credentials.token_uri creds.client_id = credentials.client_id creds.client_secret = credentials.client_secret creds.scopes = " ".join(credentials.scopes) creds.expiry = credentials.expiry creds.save()
- 给
Emp_google_creds模型添加过期时间字段,执行数据库迁移生效:
from django.db import models class Emp_google_creds(models.Model): # 保留原有所有字段,新增下面这行 expiry = models.DateTimeField(null=True, blank=True)
- 修改引导用户跳转Google授权页的逻辑,添加离线访问参数,确保能拿到refresh_token:
# 生成授权跳转URL时必须添加以下两个参数 authorization_url, state = flow.authorization_url( access_type='offline', # 要求Google返回可用于长期刷新的refresh_token prompt='consent', # 强制弹出授权确认,避免已授权用户拿不到refresh_token include_granted_scopes='true' ) # 再执行redirect(authorization_url)引导用户跳转授权
- 修复
calendar_home视图的凭证加载与刷新逻辑,不要凭证一失效就跳授权,优先尝试用refresh_token刷新:
# 先导入刷新需要的依赖 from google.auth.transport.requests import Request def calendar_home(request): u = Employee.objects.filter(dashboard_user=request.user) if not u: u = Employee.objects.create(dashboard_user=request.user) else: u = u[0] if not Emp_google_creds.objects.filter(employee=u): return redirect("/calendar/cal_auth/") else: creds=Emp_google_creds.objects.filter(employee=u)[0] gcreds=google.oauth2.credentials.Credentials.from_authorized_user_info(creds.to_dict()) # 凭证无效时优先尝试刷新 if not gcreds.valid: if gcreds.expired and gcreds.refresh_token: try: gcreds.refresh(Request()) # 刷新成功后把新的凭证同步回数据库 creds.token = gcreds.token creds.expiry = gcreds.expiry if gcreds.refresh_token: creds.refresh_token = gcreds.refresh_token creds.save() except Exception as e: # 刷新失败再跳授权 return redirect("/calendar/cal_auth/") else: # 没有有效refresh_token,跳转重新授权 return redirect("/calendar/cal_auth/") # 后续调用Calendar API的逻辑保持不变即可 try: service = googleapiclient.discovery.build('calendar', 'v3', credentials=gcreds) now = datetime.datetime.utcnow().isoformat() + 'Z' print('Getting the upcoming 10 events') events_result = service.events().list(calendarId='primary', timeMin=now, maxResults=10, singleEvents=True, orderBy='startTime').execute() events = events_result.get('items', []) if not events: print('No upcoming events found.') return render(request,"schedule.html") for event in events: start = event['start'].get('dateTime', event['start'].get('date')) print(start, event['summary']) except HttpError as error: print('An error occurred: %s' % error) return render(request,"schedule.html")
- 修复完成后,需要让所有已授权过的用户重新走一次授权流程,才能拿到有效的refresh_token存入数据库,后续页面刷新、token过期时都会自动刷新,不需要重复授权。
内容的提问来源于stack exchange,提问作者wrong1man
相关产品推荐
相关产品推荐

