(Використання Redux для управління державою)
Якщо користувач спробує отримати доступ до будь-якого URL-адреси, спершу я перевіряю, чи доступний маркер доступу, якщо він не перенаправлений на сторінку входу. Після того, як користувач увійде в систему, використовуючи сторінку входу, ми зберігаємо це як у локальному сховищі, так і в нашому резервному стані. (локальне зберігання чи файли cookie. Ми поки що не підтримуємо цю тему поза контекстом).
оскільки стан redux як оновлений та privateroutes буде надано. тепер у нас є маркер доступу, тому ми переспрямовуємо на головну сторінку.
Зберігають також декодовані дані корисного навантаження авторизації у зворотному стані та передають їх для реакції на контекст. (Нам не потрібно використовувати контекст, але для доступу до авторизації в будь-якому з вкладених дочірніх компонентів це полегшує доступ із контексту, замість того, щоб підключати кожен дочірній компонент до скорочення).
До всіх маршрутів, які не потребують спеціальних ролей, можна отримати доступ безпосередньо після входу. Якщо йому потрібна така роль, як адміністратор (ми створили захищений маршрут, який перевіряє, чи потрібна йому роль, якщо не перенаправляє на несанкціонований компонент)
аналогічно в будь-якому компоненті, якщо вам доведеться відключити кнопку або щось на основі ролі.
просто ви можете зробити так
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 був фіктивним, виклик відпочинку поверне несанкціонованого користувача. Тож я роблю вихід із виклику (який очистить локальне зберігання та перенаправлення на сторінку входу знову). Якщо домашня сторінка має статичні дані та не здійснює жодних дзвінків api (тоді ви повинні мати підтвердження дзвінка 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 TOKEN
{
"authorization": {
"roles": [
"admin",
"operator"
]
},
"exp": 1591733170,
"user_id": 1,
"orig_iat": 1591646770,
"email": "hemanthvrm@stackoverflow",
"username": "hemanthvrm"
}