(상태 관리를 위해 Redux 사용)
사용자가 URL에 액세스하려고하면 먼저 액세스 토큰을 사용할 수 있는지 확인하고 로그인 페이지로 리디렉션하지 않으면 사용자가 로그인 페이지를 사용하여 로그인하면 redux 상태뿐만 아니라 localstorage에 저장합니다. (localstorage 또는 cookies .. 지금은이 주제를 컨텍스트에서 제외).
redux 상태가 업데이트되고 privateroutes가 다시 렌더링되기 때문입니다. 이제 액세스 토큰이 있으므로 홈페이지로 리디렉션합니다.
디코딩 된 권한 부여 페이로드 데이터도 redux 상태에 저장하고이를 반응 컨텍스트에 전달합니다. (컨텍스트를 사용할 필요는 없지만 중첩 된 자식 구성 요소의 권한에 액세스하려면 각 자식 구성 요소를 redux에 연결하는 대신 컨텍스트에서 쉽게 액세스 할 수 있습니다.)
특별한 역할이 필요하지 않은 모든 라우트는 로그인 후 바로 접근 할 수 있습니다 .. 관리자와 같은 역할이 필요한 경우 (권한이없는 컴포넌트로 리디렉션하지 않으면 원하는 역할을 가지고 있는지 확인하는 보호 경로를 만들었습니다)
버튼이나 역할에 따라 비활성화해야하는 경우 모든 구성 요소에서 유사하게.
단순히 이런 식으로 할 수 있습니다
const authorization = useContext(AuthContext);
const [hasAdminRole] = checkAuth({authorization, roleType:"admin"});
const [hasLeadRole] = checkAuth({authorization, roleType:"lead"});
<Button disable={!hasAdminRole} />Admin can access</Button>
<Button disable={!hasLeadRole || !hasAdminRole} />admin or lead can access</Button>
따라서 사용자가 localstorage에 더미 토큰을 삽입하려고하면 어떻게 될까요? 액세스 토큰이 있으므로 홈 구성 요소로 리디렉션됩니다. 내 홈 구성 요소는 jwt 토큰이 더미이기 때문에 나머지 호출을 수행하여 데이터를 가져옵니다. 나머지 호출은 권한이없는 사용자를 반환합니다. 그래서 나는 로그 아웃을 호출합니다 (localstorage를 지우고 로그인 페이지로 다시 리디렉션됩니다). 홈페이지에 정적 데이터가 있고 API 호출을하지 않는 경우 (홈페이지를로드하기 전에 토큰이 REAL인지 확인할 수 있도록 백엔드에 token-verify api 호출이 있어야 함)
index.js
import React from 'react';
import ReactDOM from 'react-dom';
import { Router, Route, Switch } from 'react-router-dom';
import history from './utils/history';
import Store from './statemanagement/store/configureStore';
import Privateroutes from './Privateroutes';
import Logout from './components/auth/Logout';
ReactDOM.render(
<Store>
<Router history={history}>
<Switch>
<Route path="/logout" exact component={Logout} />
<Route path="/" exact component={Privateroutes} />
<Route path="/:someParam" component={Privateroutes} />
</Switch>
</Router>
</Store>,
document.querySelector('#root')
);
History.js
import { createBrowserHistory as history } from 'history';
export default history({});
Privateroutes.js
import React, { Fragment, useContext } from 'react';
import { Route, Switch, Redirect } from 'react-router-dom';
import { connect } from 'react-redux';
import { AuthContext, checkAuth } from './checkAuth';
import App from './components/App';
import Home from './components/home';
import Admin from './components/admin';
import Login from './components/auth/Login';
import Unauthorized from './components/Unauthorized ';
import Notfound from './components/404';
const ProtectedRoute = ({ component: Component, roleType, ...rest })=> {
const authorization = useContext(AuthContext);
const [hasRequiredRole] = checkAuth({authorization, roleType});
return (
<Route
{...rest}
render={props => hasRequiredRole ?
<Component {...props} /> :
<Unauthorized {...props} /> }
/>)};
const Privateroutes = props => {
const { accessToken, authorization } = props.authData;
if (accessToken) {
return (
<Fragment>
<AuthContext.Provider value={authorization}>
<App>
<Switch>
<Route exact path="/" component={Home} />
<Route path="/login" render={() => <Redirect to="/" />} />
<Route exact path="/home" component={Home} />
<ProtectedRoute
exact
path="/admin"
component={Admin}
roleType="admin"
/>
<Route path="/404" component={Notfound} />
<Route path="*" render={() => <Redirect to="/404" />} />
</Switch>
</App>
</AuthContext.Provider>
</Fragment>
);
} else {
return (
<Fragment>
<Route exact path="/login" component={Login} />
<Route exact path="*" render={() => <Redirect to="/login" />} />
</Fragment>
);
}
};
// my user reducer sample
// const accessToken = localStorage.getItem('token')
// ? JSON.parse(localStorage.getItem('token')).accessToken
// : false;
// const initialState = {
// accessToken: accessToken ? accessToken : null,
// authorization: accessToken
// ? jwtDecode(JSON.parse(localStorage.getItem('token')).accessToken)
// .authorization
// : null
// };
// export default function(state = initialState, action) {
// switch (action.type) {
// case actionTypes.FETCH_LOGIN_SUCCESS:
// let token = {
// accessToken: action.payload.token
// };
// localStorage.setItem('token', JSON.stringify(token))
// return {
// ...state,
// accessToken: action.payload.token,
// authorization: jwtDecode(action.payload.token).authorization
// };
// default:
// return state;
// }
// }
const mapStateToProps = state => {
const { authData } = state.user;
return {
authData: authData
};
};
export default connect(mapStateToProps)(Privateroutes);
checkAuth.js
import React from 'react';
export const AuthContext = React.createContext();
export const checkAuth = ({ authorization, roleType }) => {
let hasRequiredRole = false;
if (authorization.roles ) {
let roles = authorization.roles.map(item =>
item.toLowerCase()
);
hasRequiredRole = roles.includes(roleType);
}
return [hasRequiredRole];
};
디코딩 된 JWT 토큰 샘플
{
"authorization": {
"roles": [
"admin",
"operator"
]
},
"exp": 1591733170,
"user_id": 1,
"orig_iat": 1591646770,
"email": "hemanthvrm@stackoverflow",
"username": "hemanthvrm"
}