FastAPI部署在VPS的Docker容器中响应无法写入Cookie导致登录失败如何修复?
无法登录问题排查与解决
问题描述
本地Docker运行容器时可以正常登录,Docker无任何报错,但部署到远程服务器后就无法登录,响应中无法写入Cookie,无错误提示,只会重定向到预设的页面,之后触发报错,原因是Cookie中缺少对应的授权密钥。
相关代码
SignIn接口实现代码
@auth_router.post('/signin') async def sign_in(response: Response, username: str = Form(...), password: str = Form(...), recaptchav3: str = Form(...)) -> dict: is_human = await verify_recaptcha(recaptchav3) if is_human['success']: user = await authenticate_user(username, password) if not user: raise HTTPException( status_code=status.HTTP_401_UNAUTHORIZED, detail='Invalid username or password', ) user_obj = await User_Pydantic.from_tortoise_orm(user) user_token = await generate_token(user_obj) response.set_cookie(key="Authorization", value=user_token, httponly=True, secure=True, expires=(8*60*60)) response.headers["Authorization"] = user_token user.jwt_token = user_token await user.save() return { 'access_token': user_token, 'token_type': 'bearer' } else: raise HTTPException( status_code=status.HTTP_400_BAD_REQUEST, detail='Invalid captcha', )
表单提交JS代码
const request = (method, url, data = null, redirectPage) => { return new Promise((resolve, reject) => { let xhr = new XMLHttpRequest() xhr.open(method, url, true) // xhr.setRequestHeader('Content-Type', 'multipart/form-data') xhr.onerror = function (event) { alert(event) console.log(event); }; xhr.onload = () => { if (xhr.status === 200) { return window.location.href = redirectPage; return resolve(JSON.parse(xhr.responseText || '{}')) } else { alert(`Request failed with status ${xhr.status}`) reject(new Error(`Request failed with status ${xhr.status}`)) return window.location.reload(); } } if (data) { if (typeof data === 'string' || data instanceof String || typeof data.constructor == Object){ xhr.send(JSON.stringify(data)) } else { xhr.send(data) } } else { xhr.send() } }) } signInForm = getElementById('signinform'); handleEvent(signInForm, 'submit', e => { e.preventDefault(); if(!isEmpty(signInForm)){ signInUsername = getElement('input[name="username"]', signInForm).value; signInPassword = getElement('input[name="password"]', signInForm).value; recaptchaV3 = getElement('[name="g-recaptcha-response"]').value; if(recaptchaV3){ signInData = new FormData(); signInData.append('username', signInUsername); signInData.append('password', signInPassword); signInData.append('recaptchav3', recaptchaV3); isLogened = request('POST', '/signin', signInData, 'dashboard'); } else{ alert('Перезагрузите страницу'); } } })
根因与修复方案
核心问题原因
- 后端设置Cookie时开启了
secure=True属性,该属性要求Cookie只能在HTTPS协议下被浏览器存储,localhost属于浏览器默认的安全上下文,所以本地HTTP访问也能正常写入,但远程服务器如果用HTTP访问,浏览器会直接忽略该Cookie。 - XMLHttpRequest默认不带凭据,若前后端跨域部署,就算后端返回Set-Cookie头,浏览器也不会执行写入操作。
- 若后端配置了CORS规则,使用
*作为允许的源时,带凭据的请求会被浏览器直接拦截,无法正常处理Cookie。
修复步骤
- 检查远程服务器访问协议:如果使用HTTP访问,将后端
set_cookie中的secure=True改为secure=False,或者给远程服务器配置HTTPS证书,用HTTPS协议访问。 - 在JS的XHR初始化逻辑中,
xhr.open之后添加xhr.withCredentials = true,允许请求携带凭据,同时接收响应的Cookie写入。 - 若前后端跨域部署,后端CORS配置需要设置
allow_credentials=True,并且指定具体的允许访问源,不能使用*通配符。 - 可选优化:调整XHR的onload逻辑顺序,先处理响应再执行跳转,避免后续逻辑无法执行:
xhr.onload = () => { if (xhr.status === 200) { resolve(JSON.parse(xhr.responseText || '{}')) window.location.href = redirectPage; } else { alert(`Request failed with status ${xhr.status}`) reject(new Error(`Request failed with status ${xhr.status}`)) window.location.reload(); } }
内容的提问来源于stack exchange,提问作者WhyIsThatHappening
相关产品推荐
相关产品推荐

