ESP32认证系统仅iOS Safari页面刷新后丢失Cookie问题排查
ESP32基础认证系统iOS浏览器兼容问题解决记录
系统概述
我为ESP32开发了一套基础认证系统:用户在网页输入凭证,后端验证通过后设置带有session ID的Cookie,随后页面立即刷新。
ESP32后端Cookie设置函数
void setCookie(AsyncWebServerResponse* response, const char* name, const char* value, int secondsFromNow) { time_t now = time(nullptr); time_t expirationTimestamp = now + secondsFromNow; // 将过期时间戳转换为"expires"属性的格式化字符串 struct tm* tmInfo = gmtime(&expirationTimestamp); char expires[128]; strftime(expires, sizeof(expires), "%a, %d %b %Y %H:%M:%S GMT", tmInfo); // 设置带有"expires"属性的Cookie char cookie[512]; // 根据需要调整缓冲区大小 snprintf(cookie, sizeof(cookie), "%s=%s; expires=%s; path=/", name, value, expires); response->addHeader("Set-Cookie", cookie); printf("[Server] - Set cookie: %s\n", cookie); }
函数输出示例:Set cookie: sessionID=2B945041-3DF6-454A-BC37-2C72A0C46150; expires=Mon, 27 Nov 2023 10:36:14 GMT; path=/
前端提交后刷新代码
form.addEventListener('submit', async function(e){ e.preventDefault(); if( isLoading ){return;} const username = usernameElem.value; const password = passwordElem.value; if( username === "" || password === "" ){ return setError(lang.getWord("emptyInput")); } setLoading(true); const resp = await fetch("/auth/login", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ username: username, password: password }) }); setLoading(false); if( resp.ok ){ setTimeout(() => { location.reload(); //window.location.href = "/"; },500); }else{ return setError(lang.getWord("wrongUsernameOrPassword")); } }, false);
测试情况
- Android端Chrome、Firefox、Opera:均正常运行
- Windows端Chrome、Opera、Edge、Firefox、Brave:均正常运行
- iOS端Safari及其他浏览器:无法正常工作
页面刷新后,后端会检查session ID完成登录,若未找到则返回登录页,但iOS浏览器始终返回登录页。
已尝试的解决方法
- 设置/取消Cookie的path、domain属性
- 调整前端页面刷新的延迟时间
- 使用
window.location.href = "/"替代location.reload() - 增减Cookie字符串末尾的分号
- 添加SameSite、HttpOnly属性(前者使问题更严重,后者无效果)
问题根因及临时解决
最终定位问题出在getCookie方法:不同平台的Cookie顺序不同,原基于C字符串的解析逻辑存在缺陷。原方法代码如下:
boolean HsH_Server::getCookie(const char* name, const char* cookie, const char*& result) { if (name == nullptr || cookie == nullptr || strlen(name) == 0 || strlen(cookie) == 0) { return false; } const char* position = strstr(cookie, name); if (position == nullptr) { return false; } // 计算跳过名称和'='后的起始位置 position += strlen(name) + 1; // 找到结束位置(下一个';'或Cookie字符串末尾) const char* endPos = strchr(position, ';'); if (endPos == nullptr) { // 如果没有';',说明这是最后一个Cookie endPos = cookie + strlen(cookie); } // 计算Cookie值的长度 size_t length = endPos - position; // 设置result指针指向Cookie值的起始位置 result = position; return true; }
改用String版本的解析方法后问题暂时解决,但我希望避免使用String:
boolean HsH_Server::getCookie(String name, String cookie, String& result) { int position = cookie.indexOf(name); if (position == -1) { return false; } name += "="; int length = name.length(); int startPos = position + length; int stopPos = cookie.indexOf(";", startPos); result = cookie.substring(startPos, stopPos); return true; }
内容的提问来源于stack exchange,提问作者Dr.Random
相关产品推荐
相关产品推荐

