【问题标题】:How to properly store access token in memory react如何在内存中正确存储访问令牌反应
【发布时间】:2021-05-06 03:46:27
【问题描述】:

我正在寻找一种干净的方式来将我的访问令牌存储在 React 中。

  • 第一件事:我不想使用本地存储。我不需要使我的访问令牌持久化,因为我可以随时刷新它。
  • 我还排除了一个 cookie,因为我想防止 CSRF 攻击。通过只将访问令牌存储在内存中,实际上需要加载页面才能获取令牌并验证请求(刷新令牌只能用于刷新)
  • 我曾想过使用 redux/context,但是,调用 API 的函数不是组件的子组件,因此我无法从中访问令牌。此外,我不想将令牌作为参数传递,因为我想保持 HTTP 逻辑的解耦。也许有一种干净的方式来使用它?

经过一番研究,我发现使用全局变量是一种有效的“作弊”来获得我想要的东西。但是,我猜测是否有更清晰的方法可以获得相同的结果。

//token.js

const access_token= "";

export const setAccessToken(token){
 access_token=token;
}

export const getAccessToken(){
 return access_token;
}

//NOTICE:
// -ON LOGIN/REFRESH: I set the access token
// -ON API CALLS: I get the access token and I add it to the header
api.js

const baseURL= "http://my_base_url";

const generateApiInterface = ()=>{
    let headers : any= {
       
    };
    
    token=getAccessToken(); //HERE I NEED TO RETRIEVE MY ACCESS TOKEN

    if(token){
        headers={
            'Authorization': 'Token '+ token,
            ...headers //append other basic proprieties
        }
    }

    return axios.create({
        baseURL: baseURL,
        headers: headers
    });
}

const api = generateApiInterface();

【问题讨论】:

  • 将其存储在 cookie 中并在每次您想要进行 HTTP 调用时从那里检索它有什么问题?
  • 我已经在一个仅限 HTTP 的 cookie 中有刷新令牌。我不想将访问令牌放在 cookie 中以防止 CSRF 攻击
  • 既然你提到了登录,你肯定有某种会话。我会将访问令牌存储在会话级别,即使您可以做到,也无需总是获取新的令牌。
  • 实际上我正在使用 JWT 在客户端存储“会话”。为了避免安全问题,我需要刷新我的令牌。为了更好地理解我的方法,我将您链接到我解释我的身份验证过程的帖子(询问它是否是一个好方法):stackoverflow.com/questions/65946041/…

标签: javascript reactjs typescript


【解决方案1】:

您的操作方式最好将任何内容保存在内存中。如果这是服务器端,那么您需要确保在完成后删除令牌,但如果不是,那么当前方法是可取的。请注意,您使用的是const,您希望将其更改为let。另一个想法是使用会话存储,但这带来了 XSS 的想法。

然而,React 提供了一种拥有“全局”状态的方法——这将是使用提供者和上下文。这是一个例子:

// provider.tsx

import React, { useContext, createContext, FC, useState } from 'react'

type AccessTokenContext = [string, React.Dispatch<React.SetStateAction<string>>]

const AccessTokenProvider: FC = (props) => {
    const [accessToken, setAccessToken] = useState<string>(null)
    return <AccessToken.Provider value={[accessToken, setAccessToken]} {...props} />
}

const AccessToken = createContext<AccessTokenContext>(null)

const useAccessToken = (): AccessTokenContext => useContext<AccessTokenContext>(AccessToken)

export { AccessTokenProvider, useAccessToken }

您必须将您的应用容器包装在AccessTokenProvider

// index.tsx

import React from 'react'
import ReactDOM from 'react-dom'

import './index.css'

import App from './App'
import { AccessTokenProvider } from './providers/AccessTokenProvider'

ReactDOM.render(
    <AccessTokenProvider>
        <App />
    </AccessTokenProvider>,
    document.getElementById('app')
)

然后您可以在AppApp 的任何子代中使用挂钩useAccessToken。当然,这个提供者不必是根级别的,但是在这里包含它是最简单的。

// app.tsx

import React, { FC } from 'react'

const App: FC = props => {
    const [accessToken, setAccessToken] = useAccessToken()
    return <div>{/* content */}</div>
}

export default App

【讨论】:

  • 嗨神户,感谢您的回答。我唯一缺少的是:我如何在使用 Axios 的“API”文件中正确使用访问令牌,因为那里没有功能组件?我已经更新了我的问题,以明确我遗漏的最“复杂”的部分是什么。看看 api.js。注意:如果您知道调用 API 并将访问令牌附加到它们的更好方法,我愿意接受建议
  • 附言。我发现在代码设计方面我的选择似乎更好。你的解决方案可以重新调整以获得类似的东西吗?我给你链接:stackoverflow.com/a/38480550/14106548(在这种特定情况下,它依赖于 redux,而不是使用上下文挂钩,但我认为可以实现类似的东西。对于这个特定的用例,我仍在深入研究上下文和 redux 等主题)
  • 在你的api函数中,不能只接受token作为参数吗?所以你会打电话给generateApiInterface(token)。 @AndreaCostanzo1
  • 回应您的第二条评论,这样做似乎更好,因此您不必担心在哪里设置令牌。我认为使用上下文已经解决了这个问题 - 当您更新令牌时,这会触发重新渲染,因此将始终使用最新的值。
  • 感谢 kobe,我不想将令牌用作参数,因为我想将 http 逻辑与 UI 分开。只是因为我想让所有的 http 逻辑都集中在 API 文件中,以简化代码的未来更新。
猜你喜欢
  • 1970-01-01
  • 2019-09-25
  • 1970-01-01
  • 2013-07-11
  • 1970-01-01
  • 1970-01-01
  • 2019-11-16
  • 2012-07-18
  • 2021-10-29
相关资源
最近更新 更多