FastAPI не возвращает файлы cookie во внешний интерфейс React
Почему FastAPI не возвращает файлы cookie в мой внешний интерфейс, который является приложением React?
Вот мой код:
@router.post("/login")
def user_login(response: Response,username :str = Form(),password :str = Form(),db: Session = Depends(get_db)):
user = db.query(models.User).filter(models.User.mobile_number==username).first()
if not user:
raise HTTPException(400, detail='wrong phone number or password')
if not verify_password(password, user.password):
raise HTTPException(400, detail='wrong phone number or password')
access_token = create_access_token(data={"sub": user.mobile_number})
response.set_cookie(key="fakesession", value="fake-cookie-session-value") #here I am set cookie
return {"status":"success"}
Когда я захожу в систему из автодокументов Swagger UI, я вижу файл cookie в заголовках ответов с помощью DevTools в браузере Chrome. Однако, когда я захожу в систему из своего приложения React, файл cookie не возвращается. Я использую axios для отправки запроса следующим образом:
await axios.post(login_url, formdata)
Переведено автоматически
Ответ 1
Сначала создайте файл cookie, как показано в примере ниже, и убедитесь, что при выполнении запроса Axios POST не возвращается ошибка, и что вы получаете 'status': 'success'
ответ с 200
кодом состояния. Возможно, вы также захотите ознакомиться с этим ответом, в котором также объясняется, как использовать флаги max_age
и expires
.
from fastapi import FastAPI, Response
app = FastAPI()
@app.get('/')
def main(response: Response):
response.set_cookie(key='token', value='some-token-value', httponly=True)
return {'status': 'success'}
Во—вторых, как вы упомянули, вы используете React во внешнем интерфейсе — который должен прослушивать порт, отличный от того, который используется для серверной части FastAPI, что означает, что вы выполняете запросы CORS - вам нужно установить для withCredentials
свойства значение true
(по умолчанию оно установлено в false
), чтобы разрешить получение / отправку учетных данных, таких как файлы cookie и заголовки HTTP-аутентификации, от / к другим источникам. Два сервера с одинаковым доменом и протоколом, но с разными портами, например, http://localhost:8000
и http://localhost:3000
считаются разными источниками (см. Документацию FastAPI по CORS и этот ответ, в котором подробно описаны файлы cookie в целом, а также решения для настройки междоменных файлов cookie, которые в вашем случае на самом деле вам не нужны, поскольку домен одинаков как для серверной части, так и для внешнего интерфейса, и, следовательно, установка файлов cookie в обычном режиме будет работать просто отлично).
Обратите внимание, что если вы обращаетесь к интерфейсу React, набирая http://localhost:3000
в адресной строке вашего браузера, то ваши запросы Axios к серверной части FastAPI должны использовать localhost
домен в URL, например, axios.post('http://localhost:8000',...
, а не axios.post('http://127.0.0.1:8000',...
, поскольку localhost
и 127.0.0.1
- это два разных домена, и, следовательно, файл cookie в противном случае не был бы создан для localhost
домена, как это было бы сделано для 127.0.0.1
, т. Е. домена, используемого в axios
запросе (и тогда это было бы случаем перекрестного доступа). -файлы cookie домена, как описано в связанном ответе выше, который, опять же, в вашем случае не понадобился бы).
Таким образом, чтобы принять файлы cookie, отправленные сервером, вам необходимо использовать withCredentials: true
в вашем запросе Axios; в противном случае файлы cookie будут проигнорированы в ответе (что является поведением по умолчанию, когда withCredentials
установлено значение false
; следовательно, различные домены не могут устанавливать файлы cookie для своего собственного домена). То же самое withCredentials: true
свойство должно включаться в каждый последующий запрос к вашему API, если вы хотите, чтобы файл cookie отправлялся на сервер, чтобы пользователь мог пройти аутентификацию и получить доступ к защищенным маршрутам.
Следовательно, запрос Axios, включающий учетные данные, должен выглядеть следующим образом:
await axios.post(url, data, {withCredentials: true}))
Эквивалент в fetch()
запросе (т. Е. С использованием Fetch API) равен credentials: 'include'
. Значение по умолчанию для credentials
равно same-origin
. Использование credentials: 'include'
приведет к тому, что браузер будет включать учетные данные как в запросы одного источника, так и в запросы разных источников, а также установит любые файлы cookie, отправленные обратно в ответах разных источников. Например:
fetch('https://example.com', {
credentials: 'include'
});
Важное примечание
Поскольку вы выполняете запрос из разных источников, для того чтобы любой из вышеперечисленных способов сработал, вам нужно будет явно указать разрешенные источники, как описано в этом ответе (за кулисами, то есть установить Access-Control-Allow-Origin
заголовок ответа). Например:
origins = ['http://localhost:3000', 'http://127.0.0.1:3000',
'https://localhost:3000', 'https://127.0.0.1:3000']
Using the *
wildcard instead would mean that all origins are allowed; however, that would also only allow certain types of communication, excluding everything that involves credentials
, such as cookies, authorization headers, etc—hence, you should not use the *
wildcard.
Also, make sure to set allow_credentials=True
when using the CORSMiddleware
(which sets the Access-Control-Allow-Credentials
response header to true
).
Example (see here):
app.add_middleware(
CORSMiddleware,
allow_origins=origins,
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)