(ใช้ Redux สำหรับการจัดการสถานะ)
หากผู้ใช้พยายามเข้าถึง url ใด ๆ ก่อนอื่นฉันจะตรวจสอบว่ามีโทเค็นการเข้าถึงหรือไม่หากไม่เปลี่ยนเส้นทางไปยังหน้าเข้าสู่ระบบเมื่อผู้ใช้เข้าสู่ระบบโดยใช้หน้าเข้าสู่ระบบเราจะจัดเก็บข้อมูลนั้นไว้ในที่จัดเก็บในตัวเครื่องและในสถานะ redux ของเรา (localstorage หรือ cookies.. เราคงไม่ให้หัวข้อนี้ขาดบริบทในตอนนี้)
เนื่องจากสถานะ redux เป็นอัปเดตและส่วนกำหนดค่าจะถูกแสดงผลอีกครั้ง ตอนนี้เราสามารถเข้าถึงโทเค็นได้แล้วดังนั้นเราจะเปลี่ยนเส้นทางไปที่โฮมเพจ
จัดเก็บข้อมูลเพย์โหลดการอนุญาตที่ถอดรหัสแล้วเช่นกันในสถานะ 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>
แล้วจะเกิดอะไรขึ้นถ้าผู้ใช้พยายามแทรกโทเค็นดัมมี่ในที่เก็บข้อมูลในเครื่อง เมื่อเรามีโทเค็นการเข้าถึงเราจะเปลี่ยนเส้นทางไปยังส่วนประกอบหลัก ส่วนประกอบที่บ้านของฉันจะโทรออกเพื่อดึงข้อมูลเนื่องจากโทเค็น 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"
}