问题 Redux:登录请求后保存cookie的正确位置是什么?


我有以下情况:用户输入他的凭据并单击a 登录 按钮。 API调用在动作创建者中完成 redux-thunk。 API调用成功后,将调度另一个包含服务器响应的操作。在(成功)登录后,我想将用户会话ID存储在cookie中(通过 react-cookie)。

行动创造者

export function initiateLoginRequest(username, password) {
  return function(dispatch) {
    dispatch(loginRequestStarting())

    return fetch('http://path.to/api/v1/login',
      {
        method: 'POST',
        headers: {
          'Accept': 'application/json',
          'Content-Type': 'application/json'
        },
        body: JSON.stringify({
          username: username,
          password: password
        })
      })
      .then(checkStatus)
      .then(parseJSON)
      .then(function(data) {
        dispatch(loginRequestSuccess(data))
      })
      .catch(function(error) {
        dispatch(loginRequestError(error))
      })
  }
}

export function loginRequestSuccess(user) {
  return {
    type: ActionTypes.LOGIN_REQUEST_SUCCESS,
    user
  }
}

减速器

export default function user(state = initialState, action) {
  switch (action.type) {
    case ActionTypes.LOGIN_REQUEST_SUCCESS:
      cookie.save('sessionId', action.user.sid, { path: '/' })
      return merge({}, state, {
        sessionId: action.user.sid,
        id: action.user.id,
        firstName: action.user.first_name,
        lastName: action.user.last_name,
        isAuthenticated: true
      })
    default:
      return state
  }
}

现在减速机负责 LOGIN_REQUEST_SUCCESS 保存cookie。我知道减速机必须是 一个纯粹的功能

是否在减速器中保存了一个违反此原则的cookie?将cookie保存在动作创建者中会更好吗?


7500
2017-07-15 13:43


起源



答案:


看一下 终极版 - 坚持

您可以保留/保存您的减速器(或其中的一部分) LocalStorage

概念

  • 启动登录。
  • 从服务器接收cookie。
  • 调度登录成功。
  • Reducer将cookie存储在内存中。
  • 持久中间件在LocalStorage中存储reducer状态。

安装

npm install --save-dev redux-persist

示例用法

创建一个包装持久性/补液逻辑的组件。

AppProvider.js

import React, { Component, PropTypes } from 'react';
import { Provider } from 'react-redux';
import { persistStore } from 'redux-persist';

class AppProvider extends Component {
    static propTypes = {
        store: PropTypes.object.isRequired,
        children: PropTypes.node
    }

    constructor(props) {
        super(props);

        this.state = { rehydrated: false };
    }

    componentWillMount() {
        const opts = {
            whitelist: ['user'] // <-- Your auth/user reducer storing the cookie
        };

        persistStore(this.props.store, opts, () => {
            this.setState({ rehydrated: true });
        });
    }

    render() {
        if (!this.state.rehydrated) {
            return null;
        }

        return (
            <Provider store={this.props.store}>
                {this.props.children}
            </Provider>
        );
    }
}

AppProvider.propTypes = {
    store: PropTypes.object.isRequired,
    children: PropTypes.node
}

export default AppProvider;

然后,在你的 index.js 或您在其中设置的文件 store,将渲染的组件包装在新的组件中 AppProvider

index.js

...
import AppProvider from 'containers/AppProvider.jsx';
...

render((
    <AppProvider store={store}>
        ...
    </AppProvider>
), document.getElementById('App'));

这将序列化你的 user 减速机状态到 LocalStorage 每次更新商店/州时。您可以打开您的开发工具(Chrome)并查看 Resources => Local Storage


8
2017-07-15 17:38



这种方法看起来不错!我不知何故认为使用cookie为用户访问令牌将被更多浏览器支持。我将看一下目前的支持情况 localstorage。 - Steve
这在safari隐身模式下无效。 - Peege151
对于那些想要了解它的人,请关注 链接 - Claudio Santos
在localStorage中存储令牌和其他安全信息是不安全的:( - Roman
@Roman,这完全取决于你的应用是否容易受到XSS的影响。 - Mario Tacke


答案:


看一下 终极版 - 坚持

您可以保留/保存您的减速器(或其中的一部分) LocalStorage

概念

  • 启动登录。
  • 从服务器接收cookie。
  • 调度登录成功。
  • Reducer将cookie存储在内存中。
  • 持久中间件在LocalStorage中存储reducer状态。

安装

npm install --save-dev redux-persist

示例用法

创建一个包装持久性/补液逻辑的组件。

AppProvider.js

import React, { Component, PropTypes } from 'react';
import { Provider } from 'react-redux';
import { persistStore } from 'redux-persist';

class AppProvider extends Component {
    static propTypes = {
        store: PropTypes.object.isRequired,
        children: PropTypes.node
    }

    constructor(props) {
        super(props);

        this.state = { rehydrated: false };
    }

    componentWillMount() {
        const opts = {
            whitelist: ['user'] // <-- Your auth/user reducer storing the cookie
        };

        persistStore(this.props.store, opts, () => {
            this.setState({ rehydrated: true });
        });
    }

    render() {
        if (!this.state.rehydrated) {
            return null;
        }

        return (
            <Provider store={this.props.store}>
                {this.props.children}
            </Provider>
        );
    }
}

AppProvider.propTypes = {
    store: PropTypes.object.isRequired,
    children: PropTypes.node
}

export default AppProvider;

然后,在你的 index.js 或您在其中设置的文件 store,将渲染的组件包装在新的组件中 AppProvider

index.js

...
import AppProvider from 'containers/AppProvider.jsx';
...

render((
    <AppProvider store={store}>
        ...
    </AppProvider>
), document.getElementById('App'));

这将序列化你的 user 减速机状态到 LocalStorage 每次更新商店/州时。您可以打开您的开发工具(Chrome)并查看 Resources => Local Storage


8
2017-07-15 17:38



这种方法看起来不错!我不知何故认为使用cookie为用户访问令牌将被更多浏览器支持。我将看一下目前的支持情况 localstorage。 - Steve
这在safari隐身模式下无效。 - Peege151
对于那些想要了解它的人,请关注 链接 - Claudio Santos
在localStorage中存储令牌和其他安全信息是不安全的:( - Roman
@Roman,这完全取决于你的应用是否容易受到XSS的影响。 - Mario Tacke


我可能让服务器端亲自设置cookie,并使其对JavaScript透明。但如果你真的想在客户端做这件事,我会在动作助手中做到这一点。像这样的东西:

// Using redux-thunk
function login(user, password) {
  return dispatch => api.auth.login(user, password)
    .then(result => setCookie())
    .then(() => dispatch({type: 'USER_LOGGED_IN'}))
}

或类似的东西。

动作助手不需要纯粹,但减速器应该是。所以,如果我正在做副作用,我会把它们付诸行动助手。


4
2017-07-15 14:03



谢谢,我今天会试试。它确实在我的用户缩减器中保存(和删除)cookie有点错误。 - Steve


我不确定这是否是“正确的”方式,但这就是我的团队如何在我们构建的Redux应用程序中保留已登录用户:

我们有一个非常默认的架构,一个准备好在一侧接收请求的API,以及一个在另一侧使用这个API端点的React / Redux / Single Page应用程序。

当用户凭证有效时,负责登录的API端点会使用用户对象(包括访问令牌)响应应用程序。访问令牌后来在应用程序发出的每个请求中使用,以根据API验证用户。

当应用程序从API接收到此用户信息时,会发生以下两件事:1)将操作分派给用户reducer,类似于 ADD_USER,将此用户包含在用户存储中; 2)用户的访问令牌保留在 localStorage

在此之后,任何组件都可以连接到用户reducer并使用持久访问令牌来了解谁是已登录用户,当然,如果您没有访问令牌 localStorage 这意味着用户未登录。

在我们的组件层次结构的顶部,我们有一个组件负责连接到用户reducer,根据持久化的访问令牌获取当前用户 localStorage,并将此当前用户传递给 React的背景。因此,我们避免依赖于当前用户的每个组件必须连接到用户reducer并从中读取 localStorage,我们假设这些组件将始终从应用程序的上下文中接收当前用户。

令牌过期等一些挑战会给解决方案带来更多复杂性,但基本上这就是我们这样做的方式,并且它运行良好。


4
2017-07-16 05:54



谢谢,听起来非常像我们正在建设的。你能告诉我,你为什么选择 localstorage over Cookie(用于保存访问令牌)? - Steve
由于我们没有Web服务器来保持用户会话,因此逻辑解决方案是使用localStorage - Nícolas Iensen