2016-08-23 11 views
19

私はReduxでReactとReact Routerを使ってウェブサイトを作っています。ルート(ページ)の多くはログインが必要ですユーザーは、このようにログインしていない場合、私は、ログインしてリダイレクトすることができます。リアクションルータでは、ログインしたままの状態でページを更新する方法もありますか?

function requireAuth(nextState, replace) { 
    let loggedIn = store.getState().AppReducer.UserReducer.loggedIn; 

    if(!loggedIn) { 
     replace({ 
      pathname: '/login', 
      state: { 
       nextpathname: nextState.location.pathname 
      } 
     }); 
    } 
} 

ReactDOM.render(
    <Provider store={store}> 
     <Router history={history}> 
      <Route path="/" component={App}> 
       <IndexRoute component={Index} /> 
       <Route path="login" component={Login} /> 
       <Route path="register" component={Register} /> 
       <Route path="dashboard" component={Graph} onEnter={requireAuth}> 
        ... some other route requires logged in ... 
       </Route> 
      </Route> 
     </Router> 
    </Provider>, 
    document.getElementById('entry') 
); 

コードを参照してください、私は、ユーザーの場合に「/ログイン」ルートをリダイレクトするためにフックをフォーカス取得時に使用しました

これは完全に機能していますが、ページを更新すると、ストアがリセットされ、ユーザーが正しく表示されません。ログイン後の状態。

私はこれが起こることを知っています。なぜなら、Reduxストアは単なるメモリストレージなので、refeshページはすべてのデータを失うためです。

すべてのリフレッシュでサーバーセッションを確認できますが、これはリクエストが多すぎる可能性があります。そのため、悪い考えです。

localStorageに状態データを保存しても問題ありませんが、セッションが期限切れになっているか、または何かのように存在しないため、AJAX呼び出しがその要求を拒否したことを確認する必要があります。

この問題をより明確に解決する方法はありますか?私のウェブサイトでは多くの人が利用されるため、XHRの呼び出しをできるだけ減らしたいと考えています。

アドバイスをいただければ幸いです。

答えて

24

別の方法は、各ルートに必要なJSON Web Tokens (JWT)を使用し、localStorageを使用してJWTを確認することです。

TL;フロントエンドにはDR

  • あなたは、サーバー上の認証に応じてJWTのためにあなたの サーバーを照会サインインし、サインアップのルートを持っています。 が適切なJWTを渡すと、状態のプロパティを に設定します。ユーザーはこの の状態をfalseに設定できるサインアウトルートを設定できます。あなたのルートが含まれ

  • index.jsは、リフレッシュに状態 を失うことが、いくつかのセキュリティを維持して、あなたの問題を解消するため、レンダリングする前にローカルストレージ を確認することができます。アプリケーションで認証を要求

  • すべてのルートが構成部品を介して をレンダリングし、そしてサーバーAPIに許可するためのヘッダに 有するJWTsの必要に固定されています。

この設定には少し時間がかかりますが、アプリケーションを「合理的に」安全にするでしょう。あなたの問題を解決するために


:以下に示すように

は、必要に応じて、認証済みの状態を更新し、あなたのindex.jsファイル内のルートの前にローカルストレージを確認してください。

アプリケーションは、リフレッシュの問題を解決するJWTによってAPIが保護されており、サーバーとデータへの安全なリンクを維持するというセキュリティを維持します。あなたはこのようなものだろうルートでこのように

GraphIsAuthenticatedながら

index.js

import React from 'react'; 
import ReactDOM from 'react-dom'; 
import { Provider } from 'react-redux'; 
import { createStore, applyMiddleware, compose } from 'redux'; 
import { Router, Route, browserHistory, IndexRoute } from 'react-router'; 
import reduxThunk from 'redux-thunk'; 
import { AUTHENTICATE_THE_USER } from './actions/types'; 
import RequireAuth from './components/auth/require_auth'; 
import reducers from './reducers'; 

/* ...import necessary components */ 

const createStoreWithMiddleware = compose(applyMiddleware(reduxThunk))(createStore); 

const store = createStoreWithMiddleware(reducers); 

/* ... */ 

// Check for token and update application state if required 
const token = localStorage.getItem('token'); 
if (token) { 
    store.dispatch({ type: AUTHENTICATE_THE_USER }); 
} 

/* ... */ 

ReactDOM.render(
    <Provider store={store}> 
    <Router history={history}> 
     <Route path="/" component={App}> 
     <IndexRoute component={Index} /> 
     <Route path="login" component={Login} /> 
     <Route path="register" component={Register} /> 
     <Route path="dashboard" component={RequireAuth{Graph}} /> 
     <Route path="isauthenticated" component={RequireAuth(IsAuthenticated)} /> 
     ... some other route requires logged in ... 
     </Route> 
    </Router> 
    </Provider> 
    , .getElementById('entry')); 

RequiredAuthは、(適切な名前のコンポーネントの任意の数とすることができる)合成コンポーネントですstate.authenticatedが真であることが必要です。

この場合、state.authenticatedがtrueの場合、GraphIsAuthenticatedがレンダリングされます。それ以外の場合は、デフォルトでルートルートに戻ります。


次に、すべてのルートがレンダリングされるこのような合成コンポーネントを作成できます。レンダリングする前に、ユーザーが認証されているかどうか(ブール値)を保持している状態がtrueであることを確認します。

require_auth.jsあなたはJWTを格納し、アクション・クリエーターを使用して認証する状態設定アクション作成することができますサインアップ/サインイン側で

import React, { Component } from 'react'; 
import { connect } from 'react-redux'; 

export default function (ComposedComponent) { 

    // If user not authenticated render out to root 

    class Authentication extends Component { 
    static contextTypes = { 
     router: React.PropTypes.object 
    }; 

    componentWillMount() { 
     if (!this.props.authenticated) { 
     this.context.router.push('/'); 
     } 
    } 

    componentWillUpdate(nextProps) { 
     if (!nextProps.authenticated) { 
     this.context.router.push('/'); 
     } 
    } 

    render() { 
     return <ComposedComponent {...this.props} />; 
    } 
    } 

    function mapStateToProps(state) { 
    return { authenticated: state.authenticated }; 
    } 

    return connect(mapStateToProps)(Authentication); 
} 

- > Reduxの店を。この例のmakes use of axiosは、非同期HTTP要求応答サイクルを実行します。

export function signinUser({ email, password }) { 

    // Note using the npm package 'redux-thunk' 
    // giving direct access to the dispatch method 
    return function (dispatch) { 

    // Submit email and password to server 
    axios.post(`${API_URL}/signin`, { email, password }) 
     .then(response => { 
     // If request is good update state - user is authenticated 
     dispatch({ type: AUTHENTICATE_THE_USER }); 

     // - Save the JWT in localStorage 
     localStorage.setItem('token', response.data.token); 

     // - redirect to the route '/isauthenticated' 
     browserHistory.push('/isauthenticated'); 
     }) 
     .catch(() => { 
     // If request is bad show an error to the user 
     dispatch(authenticationError('Incorrect email or password!')); 
     }); 
    }; 
} 

また、あなたの店(この場合はReduxの)、そしてもちろんのアクションの作成者を設定する必要があります。

「本当の」セキュリティはバックエンドから来ています。これを行うには、localStorageを使用してJWTをフロントエンドに保ち、機密情報が保護されているすべてのAPI呼び出しにヘッダーに渡します。

サーバーAPIでユーザー用のJWTを作成して解析することも、もう1つのステップです。 I have found passport to be effective.

+0

あなたの例では、IsAuthenticatedはComposedComponentですか、正しいですか? – klode

+0

'RequiredAuth'は合成されたコンポーネントですが、' IsAuthenticated'は 'state.authenticated'が真であることを必要とする任意の数の適切な名前のコンポーネントです。答えを更新しました。 – alexi2

+0

ありがとうございます、新しい編集がより明確です。 – klode

1

ログインした状態と有効期限でsessionStorageを使用しないのはなぜですか?あなたはsessionStorageの状態をチェックするためのコードを書く必要がありますが、それは私の意見ではXHRの呼び出しを保存することを救うことができる唯一の方法です。

関連する問題