私はReactをフロントエンドとして使用し、API呼び出しにReact-apollo-graphql
を使用しているアプリで作業しています。
私はreact-hooks
を使用しています。つまり、React 16.8 +。
私がやっていること
auth.js
ファイルを作成しました。ユーザーがログインし、トークンが有効であるかどうかをチェックしているときに値を保存しています(有効期限が切れていることを確認しています)が、そのファイルがロードしているのは、更新中またはページの再読み込み、それはそれが機能するはずの方法ではありません
My auth.jsファイル
const initialstate = {
user: null,
};
if (localStorage.getItem("JWT_Token")) {
const jwt_Token_decoded = Jwt_Decode(localStorage.getItem("JWT_Token"));
console.log(jwt_Token_decoded.exp * 1000);
console.log(Date.now());
if (jwt_Token_decoded.exp * 1000 < Date.now()) {
localStorage.clear(); // this runs only when I refresh the page or reload on route change it dosent work
} else {
initialstate.user = jwt_Token_decoded;
}
}
const AuthContext = createContext({
user: null,
login: (userData) => {},
logout: () => {},
});
const AuthReducer = (state, action) => {
switch (action.type) {
case "LOGIN":
return {
...state,
user: action.payload,
};
case "LOGOUT":
return {
...state,
user: null,
};
default:
return state;
}
};
const AuthProvider = (props) => {
const [state, dispatch] = useReducer(AuthReducer, initialstate);
const login = (userData) => {
localStorage.setItem("JWT_Token", userData.token);
dispatch({
type: "LOGIN",
payload: userData,
});
};
const logout = () => {
localStorage.clear();
dispatch({ action: "LOGOUT" });
};
return (
<AuthContext.Provider
value={{ user: state.user, login, logout }}
{...props}
/>
);
};
export { AuthContext, AuthProvider };
トークンの有効期限を確認している行にコメントしました。
私の唯一の問題は、Reduxを使用するときにストアファイルで行うように、各ルートではなくページの再読み込みで機能する理由です。
My App.js
<AuthProvider>
<Router>
<div className="App wrapper">
<Routes/>
</div>
</Router>
</AuthProvider>
My index.js
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
import ApolloClient from 'apollo-boost'
import { ApolloProvider } from '@apollo/react-hooks';
import { InMemoryCache } from 'apollo-cache-inmemory';
const client = new ApolloClient({
uri: 'my url',
cache: new InMemoryCache(),
});
ReactDOM.render(
<ApolloProvider client={client}>
<App />
</ApolloProvider>,
document.getElementById('root')
);
重要なポイント
私はreact-apollo-graphql
を使用しているので、ant認証フローを提供しますか? reduxと同じように、データを保存するストアファイルを作成する必要があります。
React 16.8 +を使用しているため、react-hooksを使用しているため、ここではuse Reducer
のみを使用しています。
私の唯一の質問は私がそれを正しくやっているのですか?私は他のアプローチを受け入れます。
Vue Vuexを使用して認証と承認を行いました。これまでのルートで実行されるストアファイルを作成するために使用しています。
私がReduxで行ったのと同じですが、私のストアファイルでは、状態とすべてを格納するために使用します。
さて、react-hooksとreact-apollo-graphqlを使用している場合、reduxでこれを行う必要はありません。
以下のようにヘッダー(承認)を渡すためにapollo-link-context
を使用しています
const authLink = setContext(() => {
const token = localStorage.getItem('JWT_Token')
return {
headers:{
Authorization: token ? `${token}` : ''
}
}
});
ここでは、各ルートまたは各リクエストでトークンが有効かどうかを確認できると思います(exp timeを確認)無効な場合、ログアウトしてローカルストレージをクリアします。ストレージのクリアは大したことではありません。ログインページにリダイレクトする方法です。
あなたが直面している問題は簡単です。 AuthReducerは、作成時にinitialStateを1回だけ受け取ります。アプリをリロードすると、すべてが再び初期化され、有効期限はロジックによって処理されます。ただし、ルート変更時は、initialStateを再評価しません。
ただし、setContext
を使用しているときにできることは、トークンを jwtDecode
を使用してデコードすることで有効期限の検証を確認し、期限切れのトークンを更新してlocalStorageに保存することです。これはすべてのリクエストで実行されるため
const authLink = setContext(async () => {
let token = localStorage.getItem('JWT_Token')
const { exp } = jwtDecode(token)
// Refresh the token a minute early to avoid latency issues
const expirationTime = (exp * 1000) - 60000
if (Date.now() >= expirationTime) {
token = await refreshToken()
// set LocalStorage here based on response;
}
return {
// you can set your headers directly here based on the new token/old token
headers: {
...
}
}
})
ただし、トークンの有効期限が切れたときにトークンを更新せずにログインページにリダイレクトしたいので、ルートでカスタム履歴オブジェクトを使用できます
src/history.js
import { createBrowserHistory } from 'history';
const history = createBrowserHistory()
export default history;
App.js
import history from '/path/to/history.js';
import { Router } from 'react-router-dom';
<AuthProvider>
<Router history={history}>
<div className="App wrapper">
<Routes/>
</div>
</Router>
</AuthProvider>
そして、setContextであなたがすることができます
import history from '/path/to/history';
const authLink = setContext(async () => {
let token = localStorage.getItem('JWT_Token')
const { exp } = jwtDecode(token)
const expirationTime = (exp * 1000) - 60000
if (Date.now() >= expirationTime) {
localStorage.clear();
history.Push('/login');
}
return {
// you can set your headers directly here based on the old token
headers: {
...
}
}
})
あなたの問題の解決策は次のようになります:
react-router
をサブスクライブしてコンポーネントを作成し、ユーザーの認証状態を確認します。main
コンポーネントでレンダリングします。authverify.component.js
import { withRouter } from "react-router-dom";
const AuthVerifyComponent = ({ history }) => {
history.listen(() => { // <--- Here you subscribe to the route change
if (localStorage.getItem("JWT_Token")) {
const jwt_Token_decoded = Jwt_Decode(localStorage.getItem("JWT_Token"));
console.log(jwt_Token_decoded.exp * 1000);
console.log(Date.now());
if (jwt_Token_decoded.exp * 1000 < Date.now()) {
localStorage.clear();
} else {
initialstate.user = jwt_Token_decoded;
}
}
});
return <div></div>;
};
export default withRouter(AuthVerifyComponent);
app.js
<AuthProvider>
<Router>
<div className="App wrapper">
<Routes />
<AuthVerifyComponent />
</div>
</Router>
</AuthProvider>;