在Redux存储凭据是一个好的做法吗?

14

我正在尝试开发一个React/Redux应用程序来进行学习,并且想知道迄今为止我所做的是否是良好的实践或不建议使用。 我首先要处理的是处理授权请求。 我有一个restful api Rails后端。该服务器响应登录请求并在标头参数中附加access-token等参数。 这个access-token仅对下一个请求有效,而下一个请求又返回一个新的令牌,以此类推。

我用以下方式实现了这个流程: 存储器分派执行登录请求的操作,传递用户名和密码。然后当响应准备就绪时,将凭据存储在redux存储器中。 当我需要执行授权请求时,我将这些参数设置在标头请求中。 当我收到响应时,我使用从响应获取的新凭据更新存储器中的凭据。

这是我的代码,以便更清楚地理解:

import { combineReducers } from 'redux'
import { createStore, applyMiddleware } from 'redux';
import thunk from 'redux-thunk';

const initialState = {
  currentUser: {
    credentials: {},
    user: {}
  },
  test: {},
  users: []
}

export const SUBMIT_LOGIN = 'SUBMIT_LOGIN'
export const SET_USER = 'SET_USER'
export const TEST = 'TEST'
export const SET_USERS = 'SET_USERS'
export const SET_CREDENTIALS = 'SET_CREDENTIALS'

//actions
const submitLogin = () => (dispatch) => {
  return postLoginRequest()
    .then(response => {
      dispatch(setCredentials(
        response.headers.get('access-token'),
        response.headers.get('client'),
        response.headers.get('expiry'),
        response.headers.get('token-type'),
        response.headers.get('uid')
      ));
      return response
    })
    .then(response => {
      return response.json();
    })
    .then(
      (user) => dispatch(setUser(user.data)),
    );
}

const performRequest = (api) => (dispatch) => {
  return api()
    .then(response => {
      dispatch(setCredentials(
        response.headers.get('access-token'),
        response.headers.get('client'),
        response.headers.get('expiry'),
        response.headers.get('token-type'),
        response.headers.get('uid')
      ));
      return response
    })
    .then(response => {return response.json()})
    .then(json => {console.log(json)})
}

const setUsers = (users) => {
  return {
    type: SET_USERS,
    users
  }
}

const setUser = (user) => {
  return {
    type: SET_USER,
    user
  }
}

const setCredentials = (
  access_token,
  client,
  expiry,
  token_type,
  uid
) => {
  return {
    type: SET_CREDENTIALS,
    credentials: {
      'access-token': access_token,
      client,
      expiry,
      'token-type': token_type,
      uid
    }
  }
}

const currentUserInitialState = {
  credentials: {},
  user: {}
}

const currentUser = (state = currentUserInitialState, action) => {
  switch (action.type) {
    case SET_USER:
      return Object.assign({}, state, {user: action.user})
    case SET_CREDENTIALS:
      return Object.assign({}, state, {credentials: action.credentials})
    default:
      return state
  }
}

const rootReducer = combineReducers({
  currentUser,
  test
})

const getAuthorizedHeader = (store) => {
  const credentials = store.getState().currentUser.credentials
  const headers = new Headers(credentials)
  return headers
}

//store creation

const createStoreWithMiddleware = applyMiddleware(
  thunk
)(createStore);

const store = createStoreWithMiddleware(rootReducer);

const postLoginRequest = () => {
  return fetch('http://localhost:3000/auth/sign_in', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({
      email: 'test@test.com',
      password: 'password',
    })
  })
}

const getUsers = () => {
  const autorizedHeader = getAuthorizedHeader(store)
  debugger
  return fetch('http://localhost:3000/users',
    {
      method: 'GET',
      headers : autorizedHeader
    }
  )
}


store.dispatch(submitLogin())

setTimeout(() => {
  console.log(store.dispatch(performRequest(getUsers)))
}, 2000)

我想知道在存储中保存敏感数据是否是一个好的做法,如果不是,我愿意听取任何建议来更好地开发这个工作流程。

我的服务器上也有这个参数

config.batch_request_buffer_throttle = 5.seconds

有时需要同时向API发出多个请求。在这种情况下,批处理中的每个请求都需要共享相同的身份验证令牌。此设置确定请求之间可以有多远的距离,同时仍使用相同的身份验证令牌。

如果令牌仍然有效,我就无法在performRequest函数中调用setCredentials操作,但我确实没有想法如何检查它。

谢谢


2
Redux存储在内存中(除非您将其存储在其他地方)。如果是这种情况,凭据和/或会话将在重新加载页面时丢失。对我来说听起来很合理。 - danielepolencic
6
我认为这听起来不太合理。使用移动应用或单页应用程序时,可能需要几周时间才能让用户“重新加载页面”,在此期间,用户的凭据保存在某个地方的内存中,这听起来可能存在漏洞。 - VaclavSir
4
我同意@AndreyLuiz的想法。将令牌/凭据放入localStorage中,并在每次需要进行服务器请求时检查localStorage中的凭据。如果凭据不再存在或无效,则从存储中删除所有内容并执行LOGOUT操作。在您的reducer中,LOGOUT操作应返回一个空状态,以便将有关先前会话的所有信息从状态中删除。如果您真的想确保没有人使用过期的凭据使用应用程序,还可以实现对localStorage中凭据的定期检查,以确保它们是有效的。 - Dude
1
我认为将变量存储在会话状态中比本地存储更安全,因为任何网站都可以从localstorage访问您的信息。https://dev59.com/umMm5IYBdhLWcg3wRdQz - Joey Gough
2
@JoeyGough 任何网站都可以访问您的本地存储,但本地存储是基于域名的。因此,如果网站www.abc.com在您的本地存储中保存了任何内容,则当您访问www.def.com时,它将无法访问www.abc.com保存的任何信息。 - aquilesb
显示剩余6条评论
2个回答

1
不,这不是一个好的实践。如果你想更好地存储用户会话,请使用Cookie会话来存储JWT(针对React.js)。
对于react-native,我建议使用react-native-keychain来存储您的密码,也建议使用JWT令牌或加密文本而不是明文。
您的实现大多取决于后端。如果您使用像passport.js这样的库(在node.js服务器的情况下),则使用加密令牌来授权请求。但无论您如何进行身份验证,都不要将其明文存储在状态中。

你能详细说明一下为什么吗?你看到了哪些缺点/安全问题? - igk

0

使用现代存储API将会对您有所帮助。

因为当您在Redux Store中获取您的授权数据时,如果页面被刷新或历史记录移动(返回||前进),那么Store值将不再存在。并且您的应用程序将在每次刷新后请求授权数据..刷新..

...

使用 window.sessionStorage 可以在刷新或历史导航后保留数据。

  [AFTER LOGIN EVENT]

    window.sessionStorage.setItem(key,vlaue)

  [AFTER LOGOUT EVENT]

    window.sessionStorage.clear() <- removes all records in sessionStorage.
    window.sessionStorage.removeItem(key) 


2
我不建议使用这种方法来存储凭据。它是不安全的。 - GBourke

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接