Django+React对接TrueLayer API时,HttpOnly Cookie存储的refresh_token无法获取导致无法刷新access_token的问题求助
我目前用React写前端、Django做后端,对接TrueLayer的开放银行API,已经搞定了第一步的认证流程,但在实现用refresh_token刷新access_token的时候遇到了麻烦。
之前查资料说refresh_token要存在HttpOnly Cookie里才安全,所以我在TrueLayer回调接口里这么写的:
def truelayer_callback(request): code = request.GET.get('code') token_data = { "grant_type": "authorization_code", "client_id": settings.TRUELAYER_CLIENT_ID, "client_secret": settings.TRUELAYER_CLIENT_SECRET, "redirect_uri": REDIRECT_URI, "code": code, } try: data = requests.post(TOKEN_URL, data=token_data).json() access_token = data.get('access_token') refresh_token = data.get('refresh_token') if access_token: query_params = urlencode({'token' : access_token}) response = HttpResponseRedirect(f'{FRONTEND_URL}?{query_params}') response.set_cookie(key='refresh_token', value=refresh_token, httponly=True, secure=False, samesite='Lax') return response return HttpResponseRedirect(f'{FRONTEND_URL}?error=No access token received') except Exception as e: return HttpResponseRedirect(f'{FRONTEND_URL}?error={str(e)}')
这里我把refresh_token设置到了响应的Cookie里,但之后调用刷新接口的时候,死活拿不到这个Cookie:
def check_refresh_cookie(request): refresh_token = request.COOKIES.get("refresh_token") # 这里拿到的是None! token_data = { "grant_type": "refresh_token", "client_id": settings.TRUELAYER_CLIENT_ID, "client_secret": settings.TRUELAYER_CLIENT_SECRET, "refresh_token": refresh_token, } try: response = requests.post(TOKEN_URL, data=token_data) return JsonResponse({'access_token' : response.json()}) except Exception as e: return HttpResponseRedirect(f'{FRONTEND_URL}?error={str(e)}')
现在我有点懵,不知道怎么才能既安全存储refresh_token,又能正常拿到它来刷新access_token。前端那边就是个按钮绑定了点击事件,暂时没什么复杂逻辑。
补充说明:核心问题就是我在回调接口里设置了Cookie,但调用check_refresh_cookie时refresh_token是None,完全不符合预期。
嘿,我来帮你捋捋这个问题!HttpOnly Cookie拿不到的情况,大概率是跨域配置或者Cookie属性的问题,咱们一步步排查:
1. 先搞定跨域请求的Cookie携带问题
如果你的React前端和Django后端是跨域的,那前端发起请求时必须明确告诉浏览器要携带Cookie,不然浏览器会自动忽略。举个例子:
- 用
fetch的话:
fetch('http://你的Django后端地址/check_refresh_cookie', { method: 'POST', credentials: 'include' })
- 用
axios的话:
axios.post('http://你的Django后端地址/check_refresh_cookie', {}, { withCredentials: true })
同时Django这边要允许跨域携带Cookie,如果你用的是django-cors-headers,得在settings.py里加这些配置:
CORS_ALLOW_CREDENTIALS = True CORS_ORIGIN_WHITELIST = [ 'http://你的React前端地址' # 必须写具体地址,不能用* ]
2. 检查Cookie的属性配置
你设置Cookie时用了secure=False,如果生产环境是HTTPS的话,这个属性会导致Cookie不被存储;反过来如果后端是HTTPS、前端是HTTP,也会有问题。另外SameSite=Lax在某些跨域POST请求场景下,浏览器可能不会发送Cookie,你可以根据环境调整:
response.set_cookie( key='refresh_token', value=refresh_token, httponly=True, secure=not settings.DEBUG, # 本地调试关secure,生产环境开 samesite='None' if not settings.DEBUG else 'Lax', # 生产环境要SameSite=None配合secure=True domain='你的后端域名', # 如果前后端域名不同,必须显式设置这个值 max_age=3600*24*7 # 给Cookie设个过期时间,别永久有效 )
3. 先确认Cookie有没有被存下来
打开浏览器开发者工具的Application标签(Chrome),看看Cookies里有没有refresh_token这个键:
- 如果没有,说明回调接口的Cookie设置有问题,比如域名不匹配、secure属性冲突;
- 如果有,但调用刷新接口时没传,那就是跨域请求的
credentials配置没做好。
4. 排除CSRF的干扰
如果你的刷新接口是POST请求,Django默认会检查CSRF Token,没带的话请求会被拒绝,也可能间接导致Cookie读取异常。可以先给接口加@csrf_exempt装饰器临时关闭验证,确认是不是这个原因:
from django.views.decorators.csrf import csrf_exempt @csrf_exempt def check_refresh_cookie(request): # 原代码不变
按这几个步骤排查,应该能找到问题所在!
备注:内容来源于stack exchange,提问作者Leo El

