我正在Reactjs中构建一个应用程序。在验证access_token之后,我必须进行fetch调用。在注册时,从后端服务器获取access_token。但是,在哪里存储这些access_token。是否有任何方法可以使这些access_token全局,以便所有组件都可以访问它。我使用过本地存储,缓存和会话存储,但这些不可取。 这个问题在过去的几天内得到了解决,任何解决方案。事先提前。
答案 0 :(得分:13)
可用的选项和限制:
有两种存储令牌的选项:
sessionStorage
和localStorage
。存储在此处的数据将始终对您的Javascript代码可用,并且无法从后端访问。因此,您将必须手动将其添加到例如标头中的请求中。此存储空间仅对您应用的域可用,而对子域不可用。这两种机制之间的主要区别在于数据有效期:sessionStorage
:仅适用于会话的数据(直到浏览器或标签关闭)。localStorage
:存储没有到期日期的数据,并且仅通过JavaScript或清除浏览器缓存/本地存储的数据来清除在设计身份验证机制时,您必须考虑两个方面:
出于安全考虑,OWASP不建议将敏感数据存储在Web存储中。您可以检查其CheatSheetSeries页面。您也可以阅读this detailed article了解更多详细信息。
其原因主要与XSS漏洞有关。如果您的前端没有100%不受XSS攻击的保护,则恶意代码可以在您的网页中执行,并且可以访问令牌。 要完全防XSS是非常困难的,因为它可能是由您使用的一种Javascript库引起的。
如果将Cookie设置为HttpOnly
,则Javascript可能无法访问它们。
现在,Cookie的问题在于它们可以使您的网站容易受到CSRF的攻击。 SameSite
cookie可以缓解这种类型的攻击。但是,较旧版本的浏览器不支持该类型的Cookie,因此可以使用其他方法,例如使用状态变量。此Auth0文档article中对此进行了详细说明。
建议的解决方案:
为了安全地存储令牌,我建议您结合使用两个cookie,如下所述:
JWT令牌具有以下结构:header.payload.signature
通常,有效负载中会包含有用的信息,例如用户角色(可用于改编/隐藏UI的各个部分)。因此,确保该部分可用于Javascript代码很重要。
一旦完成身份验证流程并在后端创建了JWT令牌,其目的就是:
header.payload
部分存储在SameSite
Secure
Cookie中(因此只能通过https使用,但仍可用于JS代码)signature
部分存储在SameSite
Secure
HttpOnly
Cookie中Authorization: Bearer your_token
您可以设置Cookie的有效期,以满足您的应用程序要求。
Peter Locke在this article中提出并很好地描述了这个想法。
答案 1 :(得分:2)
Michael Washburn上有一篇非常不错的文章,关于如何通过Redux here on his webpage
来保持状态在文章中,他具有Redux的合著者之一Dan Abramov创建的very descriptive video tutorial的链接,我跟着他一起将其添加到我的项目中。 这是我用来使其工作的代码:
store.js
import { createStore, combineReducers } from "redux";
import { UserReducer, CopyReducer } from "../reducers";
import { loadState, saveState } from "../utils/localStorage";
export const giveMeStore = () => {
const reducers = combineReducers({
copy: CopyReducer,
user: UserReducer
});
const persistedState = loadState();
const store = createStore(reducers, persistedState);
//user contains the TOKEN
store.subscribe(() => {
saveState({
user: store.getState().user
});
});
return store;
};
localStorage.js
export const loadState = () => {
try {
const serializedState = localStorage.getItem("state");
if (serializedState === null) {
return undefined;
}
return JSON.parse(serializedState);
} catch (err) {
return undefined;
}
};
export const saveState = state => {
try {
const serializedState = JSON.stringify(state);
localStorage.setItem("state", serializedState);
} catch (err) {
//ignoring write erros
}
};
并将商店添加到提供者:
import React from "react";
import ReactDOM from "react-dom";
import { Provider } from "react-redux";
import { giveMeStore } from "./store.js";
const Root = () => {
return (
<Provider store={giveMeStore()}>
//... your components
//...
</Provider>
);
};
ReactDOM.render(<Root />, document.querySelector("#root"));
答案 2 :(得分:1)
虽然晚了聚会,但我还是想分享我对这个话题的看法。 Anouar给出了一个很好的答案,其中包括被认为可保存为XSS的仅使用HTTP的cookie,指出了CSRF漏洞,并链接了Peter Locke的文章。
但是,就我而言,我需要应用程序100%无状态,这意味着我不能使用Cookie。
从安全角度考虑,将访问令牌存储在持久位置(如localStorage,window ..)是一种不好的做法。因此,您可以使用redux(或内置在状态/上下文中的react.js)将JWT存储在变量中。这将使令牌免受上述攻击,但在刷新页面后将其设为null。
我要解决的问题是使用刷新令牌,该令牌存储在localStorage中(您可以使用会话存储或类似的存储器)。刷新令牌的唯一目的是获取新的访问令牌,后端确保刷新令牌没有被盗(例如,实现针对其进行检查的计数器)。 我将访问令牌保存在缓存中(我的应用程序中的一个变量),并且由于重新加载而过期或丢失后,我使用刷新令牌来获取新的访问令牌。
显然,这仅在您还构建了后端的情况下才有效(或者至少在后端实现刷新令牌的情况下)。如果您处理的是未实现刷新令牌等的现有API,并且您不可以将访问令牌保存在变量中(由于重新加载时为null),那么您还可以在使用应用程序密钥之前对令牌进行加密将其保存到localStorage(或会话存储,或者...是的,您知道了)。请注意,解密令牌需要花费一些时间,并且可能会使您的应用变慢。因此,您可以将加密的令牌保存到localStorage(或...),并在刷新后仅对其解密一次,然后将其保留在state / redux变量中,直到再次刷新/再次从localStorage对其进行解密等。
关于该主题的最后一句话:Auth是应用程序的关键基础架构,尽管有趣的游戏和在线银行之间存在明显的区别(您可能希望对该银行“偏执”,而仅“关注” (关于游戏),诸如“ localStorage很好”或“在最坏的情况下会发生什么?在1小时后过期”之类的答案是危险的,根本就是错误的。机器可能会在几秒钟内造成很多损坏,而您又不想留下任何空白。如果您懒得不能保护应用程序安全,则可能要使用现有的解决方案,而不是自己构建解决方案。
也就是说,JWT /令牌身份验证对于游戏来说是相当新的东西(几年了,但还不如开发中的其他主题成熟)。找到可行的解决方案需要花费一些时间和精力,但是让我们继续构建安全的软件,而不是通过快速的黑客手段充斥整个网络。
最好,最快乐的编码。