我目前正在开发一个MERN堆栈应用程序,我使用的身份验证是JWT,并将其保存在我的cookie中。这就是我在用户登录后发送cookie的方式。
res
.cookie("token", token, {
httpOnly: true,
secure: true,
sameSite: "none",
})
.send();
并且我通过获得";令牌";cookie在我的后端。然而,我用这个应用程序实现了Redux,每次刷新页面时,它都会自动注销。我想要的是在我的前端(React)中检测";令牌";我的浏览器中的cookie,但我无法获取。我尝试过使用npm-js cookie,但仍然无法获取。有什么方法可以获取"令牌";曲奇或者根据我读到的内容使用redux-persistent?请帮忙,谢谢。
正如其他答案所解释的那样,您不能通过JS访问httpOnly cookie。
我个人建议你使用不同的方法。当然,cookie和httpOnly听起来是个好主意,你可能会认为cookie比localStorage好一千倍,但最终,你是将令牌存储在localStorage中还是存储在cookie中都无关紧要。您可能会就cookie与localStorage争论数小时,但两者都有各自的漏洞(例如:cookie:CSRF攻击(https://cheatsheetseries.owasp.org/cheatsheets/Cross-Site_Request_Forgery_Prevention_Cheat_Sheet.html),localStorage:XSS)。
现在,虽然理论上你可以在这里使用localStorage,但我并不提倡使用它。我建议你放弃cookie和localStorage,将JWT存储在你的应用程序状态中(无论是使用上下文api、redux等),并将带有身份验证头的JWT与你从前端到后端的所有请求一起发送。当然,您的后端需要验证该令牌。例如,您可以只实现一个身份验证中间件,将其添加到所有需要身份验证的路由中。过期也非常容易,因为您不再需要同步JWT和cookie的过期时间。只需在JWT上设置过期时间,在auth中间件中验证该令牌就会发现这一点。如果你想知道为什么这种方法对CSRF攻击是安全的,请看这里:JWT在浏览器中存储在哪里?如何防范CSRF?
以下是一些好文章,我真的建议你读一读第一篇:https://hasura.io/blog/best-practices-of-using-jwt-with-graphql/https://medium.com/@ryanchenkie_40935/react-authentication-how-store-jwt-in-a-cookie-346519310e81
你不能"httpOnly";是指";JavaScript无法访问它";。
使用Redux-Persist也不能真正帮助您确定您是否仍在登录或会话是否超时。该数据可能在几周前就已被持久化,或者令牌可能已被吊销。
您可以做的最明智的事情是在服务器上设置一个/whoami
端点,这只是应用程序初始化时在那里发送请求的第一个操作。关于您的用户的信息返回->太好了,保存并显示它。否则你会得到一个"401未经授权";这意味着用户没有登录并且需要登录
尽管您不能在前端使用httpOnly cookie做任何事情,但肯定有一种方法可以处理前端发送的httpOnly cookie,并从该cookie中提取您的JWT,所有这些都在您的MERN堆栈应用程序的后端。
至于持久化用户和防止"刷新时注销"问题,您必须创建一个useEffect挂钩,不断检查令牌是否存在——我们稍后会讨论。
首先,我建议在后端使用Cors:
const cors = require("cors");
app.use(
cors({
origin: ["http://...firstOrigin...", ...],
credentials: true,
})
);
准备就绪后,在创建httpOnly cookie时设置以下选项。此外,创建一个非httpOnly cookie来跟踪您的httpOnly cookie,该cookie具有相同的到期日期和布尔值,而不是JWT。这将允许您使用"通用cookie"库,并在前端实际读取非httpOnly cookie:
res
.cookie("token", token, {
origin: "http://...firstOrigin..."
expires: // set desired expiration here
httpOnly: true,
secure: true,
sameSite: "none",
})
.cookie("checkToken", true, {
origin: "http://...firstOrigin..."
expires: // same as above
secure: true,
sameSite: "none",
})
创建了一个模仿我们实际"令牌"的"checkToken"cookie后,我们可以使用它来设置状态(useState钩子),并通过useEffect钩子在用户存在且未过期的情况下保持用户。
然而,要正确发送它,我们必须首先指定一些内容。在这个例子中,我将使用axios在前端进行这样的API调用。
请注意,每个API调用的请求头都将包含我们的httpOnly cookie及其内容-我们可以通过打开chrome dev工具的网络选项卡来确认这一点,进行API调用,然后检查;请求报头";对于";Cookie"。。。
const cookies = new Cookies();
const checkToken = cookies.get("checkToken");
const AuthUser = () => {
const [user, setUser] = useState(checkToken);
useEffect(() => {
async function checkToken() {
await axios
.post("http://...yourBackend.../authToken", {
withCredentials: true, // IMPORTANT!!!
})
.then((res) => {
// handle response - if successful, set the state...
// to persist the user
)}
.catch((err) => {
// handle error
)}
};
checkToken();
}, []);
// Implement your login behavior here
}
一旦完成,我们可以确认我们在后端API调用的请求主体中获得了这样的令牌(无论在哪里处理),在控制台中记录cookie以检查它,然后将cookie的值存储在变量中以启用对所述cookie的验证:
app.post(".../authToken", (req, res) => {
// Get all cookies from request headers
const { cookie } = req.headers;
// Check to see if we got our cookies
console.log(cookie);
// Handle this as you please
if (cookie == undefined) return;
const token = cookie.split("token=")[1].split(";")[0]; // Yep, it's a string
console.log(token); // Check to see if we stored our cookie's JWT
// Some middleware:
jwt.verify(token, process.env.TOKEN, (err, user) => {
// if success upon verification,
// issue new 'token' and 'checkToken'
});
});
完成。
请注意,这是一个通用的实现,只是理解httpOnly cookie功能的指南。OP从未提供原始代码。
我希望这能有所帮助。祝你好运。
在使用React/Django 创建的项目上,当改进身份验证工作流的安全性时,我们遇到了类似的问题
问题是:什么地方是存放JWT的最佳场所
经过研究,我们最终实现了Oauth2协议,这里有一篇文章可以帮助您理解如果刷新令牌轮换的逻辑
我们的实施是
- 在后端生成2个令牌(访问令牌[短寿命]和刷新令牌[长寿命])
- 刷新令牌应该存储在HttpOnly cookie中(正如他们在响应中提到的,JS在客户端无法访问它)
- 在前端级别,我们只使用访问令牌,当它过期时,我们会调用后端重新生成另一个访问和刷新
- 后端将访问HttpOnly cookie中的刷新令牌,并决定它是否有效以生成新令牌
- 如果后端生成新的有效令牌,它会向前端发送访问令牌,并更新Cookie中的刷新令牌
Ps:根据此逻辑,您无法访问前端的刷新令牌,因此当您的访问令牌不再有效时,您会告诉服务器检查存储在HttpOnly Cookie中的刷新令牌(如果它仍然有效),然后重新生成其他有效的令牌
我希望这能激励你
您可以使用Node js和express来创建httponly cookie。要访问需要Authorization标头的受保护资源,您应该在节点中创建一个返回令牌的端点,因为httponly cookie只能从服务器访问。
然后在react中,使用fetchapi调用端点,它返回到token。