5 рдорд┐рдирдЯ рдореЗрдВ рд╡реНрдпрд╡рд╕реНрдерд╛рдкрдХ рдХрд░реЗрдВред рдлреНрд░рдВрдЯреЗрдВрдб - рдкреНрд░рддрд┐рдХреНрд░рд┐рдпрд╛-рд╡реНрдпрд╡рд╕реНрдерд╛рдкрдХ, рдмреИрдХреЗрдВрдб - рдлреНрд▓рд╛рд╕реНрдХ-рд░реЗрд╕реНрдЯрдлреБрд▓



рдпрджрд┐ рдЖрдкрдХреЛ рдЕрдкрдиреЗ рдШреБрдЯрдиреЗ рдкрд░ рдПрдХ рддреНрд╡рд░рд┐рдд рд╡реНрдпрд╡рд╕реНрдерд╛рдкрдХ рдкреИрдирд▓ рдкреНрд░рд╛рдкреНрдд рдХрд░рдиреЗ рдХреА рдЖрд╡рд╢реНрдпрдХрддрд╛ рд╣реИ, рдЬрд╣рд╛рдВ рдлреНрд░рдВрдЯ-рдПрдВрдб рд░рд┐рдПрдХреНрд╢рди-рдПрдбрдорд┐рди рд╣реИ рдФрд░ рдмреИрдХ - рдПрдВрдб рдлреНрд▓рд╛рд╕реНрдХ-рд░реЗрд╕реНрдЯрдлреБрд▓ рдПрдкреА рд╣реИ , рддреЛ рдЗрд╕реЗ рд▓рд╛рдЧреВ рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рдиреНрдпреВрдирддрдо рдХреЛрдб рдХрдИ рджрд╕рд┐рдпреЛрдВ рд▓рд╛рдЗрдиреЗрдВ рд╣реИрдВред

рдмреИрдХреЗрдВрдб рдлреНрд▓рд╛рд╕реНрдХ-рд░реЗрд╕реНрдЯрдлреБрд▓ рдПрдкреА


рдХреЛрдб рдореЗрдВ рдПрдХ рд╣реА рдореБрдЦреНрдп рдлрд╝рд╛рдЗрд▓ рд╣реИ:

from flask import Flask, request from flask_restful import Resource, Api from flask_jwt_extended import JWTManager from flask_jwt_extended import create_access_token, jwt_required from flask_cors import CORS app = Flask(__name__) app.config['JWT_SECRET_KEY'] = 'my_cool_secret' jwt = JWTManager(app) CORS(app) api = Api(app) class UserLogin(Resource): def post(self): username = request.get_json()['username'] password = request.get_json()['password'] if username == 'admin' and password == 'habr': access_token = create_access_token(identity={ 'role': 'admin', }, expires_delta=False) result = {'token': access_token} return result return {'error': 'Invalid username and password'} class ProtectArea(Resource): @jwt_required def get(self): return {'answer': 42} api.add_resource(UserLogin, '/api/login/') api.add_resource(ProtectArea, '/api/protect-area/') if __name__ == '__main__': app.run(debug=True, host='0.0.0.0') 

рдХреЛрдб рдкрд░ рдЪрд▓рддреЗ рд╣реИрдВ:

  • рд╣рдорд╛рд░рд╛ рдмреИрдХрдПрдВрдб рдмрд╛рд╣рд░реА рджреБрдирд┐рдпрд╛ рдХреЗ рд╕рд╛рде рд╕рднреА рдмрд╛рддрдЪреАрдд рдХреЗрд╡рд▓ Restful рдПрдкреАрдЖрдИ рдХреЗ рдорд╛рдзреНрдпрдо рд╕реЗ рдХрд░реЗрдЧрд╛, рдпрд╣рд╛рдВ рддрдХ тАЛтАЛрдХрд┐ рд╡реНрдпрд╡рд╕реНрдерд╛рдкрдХ рдкреИрдирд▓ рдореЗрдВ рдкреНрд░рд╛рдзрд┐рдХрд░рдг рднреА рдЗрд╕рдХреЗ рдорд╛рдзреНрдпрдо рд╕реЗ рд╣реИред рдЗрд╕рдХреЗ рд▓рд┐рдП, рдлреНрд▓рд╛рд╕реНрдХ рдореЗрдВ рдПрдХ рд╕реБрд╡рд┐рдзрд╛рдЬрдирдХ рдореЙрдбреНрдпреВрд▓ рд╣реИ: рдлреНрд▓рд╛рд╕реНрдХ-рд░реЗрд╕реНрдЯрдлреБрд▓ рдПрдкреА
  • рдлреНрд▓рд╛рд╕реНрдХ_jwt_extended рдореЙрдбреНрдпреВрд▓ рдЙрди рдорд╛рд░реНрдЧреЛрдВ рдХреА рд░рдХреНрд╖рд╛ рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рд╣рдорд╛рд░реА рд╕реЗрд╡рд╛ рдХрд░реЗрдЧрд╛, рдЬрд┐рдиреНрд╣реЗрдВ рдкреНрд░рд╛рдзрд┐рдХрд░рдг рдХреЗ рдмрд╛рдж рд╣реА рдПрдХреНрд╕реЗрд╕ рдХрд┐рдпрд╛ рдЬрд╛ рд╕рдХрддрд╛ рд╣реИред рдпрд╣рд╛рдВ рдХреБрдЫ рднреА рдкрд╡рд┐рддреНрд░ рдирд╣реАрдВ рд╣реИ, рдмрд╕ рдкреНрд░рддреНрдпреЗрдХ HTTP рдЕрдиреБрд░реЛрдз рдореЗрдВ рдПрдХ jwt рдЯреЛрдХрди ( JSON рд╡реЗрдм рдЯреЛрдХрди ) рдЬреЛрдбрд╝рд╛ рдЬрд╛рдПрдЧрд╛, рдЬрд┐рд╕рд╕реЗ рд╣рдорд╛рд░рд╛ рдПрдкреНрд▓рд┐рдХреЗрд╢рди рд╕рдордЭ рдЬрд╛рдПрдЧрд╛ рдХрд┐ рдЙрдкрдпреЛрдЧрдХрд░реНрддрд╛ рдЕрдзрд┐рдХреГрдд рд╣реИред
    рдКрдкрд░ рджрд┐рдП рдЧрдП рдХреЛрдб рд╕реЗ рдкрддрд╛ рдЪрд▓рддрд╛ рд╣реИ рдХрд┐ @jwt_required рдбреЗрдХреЛрд░реЗрдЯрд░ рдХрд╛ рдЙрдкрдпреЛрдЧ рдЗрд╕ рдЙрджреНрджреЗрд╢реНрдп рдХреЗ рд▓рд┐рдП рдХрд┐рдпрд╛ рдЬрд╛рддрд╛ рд╣реИред рдЖрдк рдЗрд╕реЗ рдЙрди рдПрдкреАрдЖрдИ рдорд╛рд░реНрдЧреЛрдВ рд╕реЗ рдЬреЛрдбрд╝ рд╕рдХрддреЗ рд╣реИрдВ рдЬрд┐рдиреНрд╣реЗрдВ рд╕рдВрд░рдХреНрд╖рд┐рдд рдХрд┐рдпрд╛ рдЬрд╛рдирд╛ рдЪрд╛рд╣рд┐рдПред
  • рдлреНрд▓рд╛рд╕реНрдХ_рдХреЛрд░реНрд╕ рдХреЗ рдмрд┐рдирд╛ , рд╣рдореЗрдВ рдирд┐рдореНрдирд▓рд┐рдЦрд┐рдд рддреНрд░реБрдЯрд┐ рдорд┐рд▓рддреА рд╣реИ:
    Access to XMLHttpRequest at 'http://localhost:5000/api/login/' from origin 'http://localhost:3000' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource.
    рдпрд╣рд╛рдВ рдХреЙрд░реНрд╕ рдХреЗ рдмрд╛рд░реЗ рдореЗрдВ рдФрд░ рдкрдврд╝реЗрдВред

рд╣рдо рд╕рднреА рдЖрд╡рд╢реНрдпрдХ рдкреБрд╕реНрддрдХрд╛рд▓рдп рд░рдЦрддреЗ рд╣реИрдВ рдФрд░ рдХрдорд╛рдВрдб рдХреЗ рд╕рд╛рде рдХреЛрдб рдЪрд▓рд╛рддреЗ рд╣реИрдВ:

 python main.py 

рдЬреИрд╕рд╛ рдХрд┐ рдЖрдк рджреЗрдЦ рд╕рдХрддреЗ рд╣реИрдВ, рдореИрдВрдиреЗ рд╡реНрдпрд╡рд╕реНрдерд╛рдкрдХ рдкреИрдирд▓ рдХреЗ рд▓рд┐рдП рдЙрдкрдпреЛрдЧрдХрд░реНрддрд╛ рдирд╛рдо рдФрд░ рдкрд╛рд╕рд╡рд░реНрдб рдХреЛ рд╣рд╛рд░реНрдбрдХреЛрдб рдХрд┐рдпрд╛: рд╡реНрдпрд╡рд╕реНрдерд╛рдкрдХ / рд╣рдмреНрд░ред

рдлреНрд▓рд╛рд╕реНрдХ рд╢реБрд░реВ рд╣реЛрдиреЗ рдХреЗ рдмрд╛рдж, рдЖрдк рдХрд░реНрд▓ рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рдХреЗ рдЗрд╕рдХреА рдХрд╛рд░реНрдпрдХреНрд╖рдорддрд╛ рдХрд╛ рдкрд░реАрдХреНрд╖рдг рдХрд░ рд╕рдХрддреЗ рд╣реИрдВ:

 curl -X POST -H "Content-Type: application/json" -d '{"username": "admin", "password": "habr"}' localhost:5000/api/login/ 

рдпрджрд┐ рдРрд╕рд╛ рдкрд░рд┐рдгрд╛рдо:

 { "token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIU...." } 

рддреЛ рд╕рдм рдХреБрдЫ рд╕рд╣реА рд╣реИ рдФрд░ рдЖрдк рдЖрдЧреЗ рдмрдврд╝ рд╕рдХрддреЗ рд╣реИрдВред

рдлреНрд░рдВрдЯреЗрдВрдб рдкреНрд░рддрд┐рдХреНрд░рд┐рдпрд╛-рд╡реНрдпрд╡рд╕реНрдерд╛рдкрдХ


рдореБрдЭреЗ рд░рд┐рдПрдХреНрд╢рди-рдПрдбрдорд┐рди рдкрд╕рдВрдж рдЖрдпрд╛ред рдпрд╣рд╛рдБ рдкреНрд░рд▓реЗрдЦрди рд╣реИ , рдФрд░ рдпрд╣рд╛рдБ рдбреЗрдореЛ рд╕рдВрд╕реНрдХрд░рдг рд╣реИ:
https://marmelab.com/react-admin-demo/#/login
рд▓реЙрдЧрд┐рди: рдбреЗрдореЛ
рдкрд╛рд╕рд╡рд░реНрдб: рдбреЗрдореЛ

рдбреЗрдореЛ рдХреЗ рд╕рдорд╛рди рд╡реНрдпрд╡рд╕реНрдерд╛рдкрдХ рдкреИрдирд▓ рдкреНрд░рд╛рдкреНрдд рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП, рд╣рдо рдирд┐рдореНрдирд▓рд┐рдЦрд┐рдд рдХрдорд╛рдВрдб рдирд┐рд╖реНрдкрд╛рджрд┐рдд рдХрд░рддреЗ рд╣реИрдВ:

 git clone https://github.com/marmelab/react-admin.git && cd react-admin && make install yarn add axios make build make run-demo 

рдЕрдм рд╣рдореЗрдВ рдЙрд╕реЗ рд╕рд┐рдЦрд╛рдиреЗ рдХреА рдЬрд░реВрд░рдд рд╣реИ рдХрд┐ рд╡рд╣ рд╣рдорд╛рд░реЗ рдмреИрдХрдПрдВрдб рдХреЗ рд╕рд╛рде рдХреИрд╕реЗ рдмрд╛рддрдЪреАрдд рдХрд░реЗред

рдРрд╕рд╛ рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП, admin/examples/demo/src/authProvider.js рдХреА рд╕рд╛рдордЧреНрд░реА admin/examples/demo/src/authProvider.js рдХреЛ рдирд┐рдореНрди рдХреЛрдб рд╕реЗ admin/examples/demo/src/authProvider.js , рдЬреЛ рдкреНрд░рд╛рдзрд┐рдХрд░рдг рдкреИрдирд▓ рдХреЛ рдЫреЛрдбрд╝рдиреЗ рдХреЗ рд▓рд┐рдП рдкреНрд░рд╛рдзрд┐рдХрд░рдг рдХреЗ рд▓рд┐рдП рдЬрд┐рдореНрдореЗрджрд╛рд░ рд╣реЛрдЧрд╛, рдФрд░ рдЗрд╕реА рддрд░рд╣:

рд╡реНрдпрд╡рд╕реНрдерд╛рдкрдХ / рдЙрджрд╛рд╣рд░рдг / рдбреЗрдореЛ / src / difProvider.js
 import { AUTH_LOGIN, AUTH_LOGOUT, AUTH_ERROR, AUTH_CHECK, AUTH_GET_PERMISSIONS } from 'react-admin'; import axios from 'axios'; import decodeJwt from 'jwt-decode'; export default (type, params) => { if (type === AUTH_LOGIN) { const { username, password } = params; let data = JSON.stringify({ username, password }); return axios.post('http://localhost:5000/api/login/', data, { headers: { 'Content-Type': 'application/json', } }).then(res => { if (res.data.error || res.status !== 200) { throw new Error(res.data.error); } else { const token = res.data.token; const decodedToken = decodeJwt(token); const role = decodedToken.identity.role; localStorage.setItem('token', token); localStorage.setItem('role', role); return Promise.resolve(); } }); } if (type === AUTH_LOGOUT) { localStorage.removeItem('token'); localStorage.removeItem('role'); return Promise.resolve(); } if (type === AUTH_ERROR) { const { status } = params; if (status === 401 || status === 403) { localStorage.removeItem('token'); localStorage.removeItem('role'); return Promise.reject(); } return Promise.resolve(); } if (type === AUTH_CHECK) { return localStorage.getItem('token') ? Promise.resolve() : Promise.reject({ redirectTo: '/login' }); } if (type === AUTH_GET_PERMISSIONS) { const role = localStorage.getItem('role'); return role ? Promise.resolve(role) : Promise.reject(); } }; 


рдФрд░ рдЕрдм рдордЬрд╝реЗ рдХреЗ рд▓рд┐рдП, рд╣рдо рдЕрдкрдиреЗ рдмреИрдХреЗрдВрдб рдХреА рдУрд░ рдореБрдбрд╝рддреЗ рд╣реИрдВ, рдорд╛рд░реНрдЧ рдХреЗ рд▓рд┐рдП: /api/protect-area/ рдФрд░ рдкрд░рд┐рдгрд╛рдо рдХреЛ рдореБрдЦреНрдп рд╡реНрдпрд╡рд╕реНрдерд╛рдкрдХ рдкреГрд╖реНрда рдкрд░ рдЪрд┐рдкрдХрд╛ рджреЗрддреЗ рд╣реИрдВ, рдЬрд╣рд╛рдБ рджрд╛рдврд╝реА рд╡рд╛рд▓реЗ рдкреБрд░реБрд╖ рд╣реЛрддреЗ рд╣реИрдВред

рдРрд╕рд╛ рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП, рдлрд╝рд╛рдЗрд▓ рдХреА рд╕рд╛рдордЧреНрд░реА рдХреЛ react-admin/examples/demo/src/dashboard/Welcome.js рд╕рд╛рде рдирд┐рдореНрдирд▓рд┐рдЦрд┐рдд рдХреЛрдб рд╕реЗ рдмрджрд▓реЗрдВ:

рд╡реНрдпрд╡рд╕реНрдерд╛рдкрдХ / рдЙрджрд╛рд╣рд░рдг / рдбреЗрдореЛ / src / рдбреИрд╢рдмреЛрд░реНрдб / Welcome.js
 import React, { useState, useEffect, useCallback } from 'react'; import axios from 'axios'; import Card from '@material-ui/core/Card'; import CardActions from '@material-ui/core/CardActions'; import CardContent from '@material-ui/core/CardContent'; import CardMedia from '@material-ui/core/CardMedia'; import Button from '@material-ui/core/Button'; import Typography from '@material-ui/core/Typography'; import HomeIcon from '@material-ui/icons/Home'; import CodeIcon from '@material-ui/icons/Code'; import { makeStyles } from '@material-ui/core/styles'; import { useTranslate } from 'react-admin'; const useStyles = makeStyles({ media: { height: '18em', }, }); const mediaUrl = `https://marmelab.com/posters/beard-${parseInt( Math.random() * 10, 10 ) + 1}.jpeg`; const Welcome = () => { const [state, setState] = useState({}); const fetchFlask = useCallback(async () => { axios.defaults.headers.common['Authorization'] = 'Bearer ' + localStorage.getItem('token'); await axios.get('http://localhost:5000/api/protect-area/').then(res => { const answer = res.data.answer; setState({ answer }); }); }, []); useEffect(() => { fetchFlask(); }, []); // eslint-disable-line react-hooks/exhaustive-deps const translate = useTranslate(); const classes = useStyles(); return ( <Card> <CardMedia image={mediaUrl} className={classes.media} /> <CardContent> <Typography variant="h5" component="h2"> {state.answer} </Typography> <Typography component="p"> {translate('pos.dashboard.welcome.subtitle')} </Typography> </CardContent> <CardActions style={{ justifyContent: 'flex-end' }}> <Button href="https://marmelab.com/react-admin"> <HomeIcon style={{ paddingRight: '0.5em' }} /> {translate('pos.dashboard.welcome.aor_button')} </Button> <Button href="https://github.com/marmelab/react-admin/tree/master/examples/demo"> <CodeIcon style={{ paddingRight: '0.5em' }} /> {translate('pos.dashboard.welcome.demo_button')} </Button> </CardActions> </Card> ); }; export default Welcome; 


рдкрддреЗ рдкрд░ рдЬрд╛рдПрдВ:

localhost:3000
рд▓реЙрдЧрд┐рди / рдкрд╛рд╕ рджрд░реНрдЬ рдХрд░рдХреЗ рдкреНрд░рд╡реЗрд╢ рдХрд░реЗрдВ: рд╡реНрдпрд╡рд╕реНрдерд╛рдкрдХ / рд╣рдм

рдФрд░ рдпрджрд┐ рд╕рднреА рдирд┐рдпрдо рд╣реИрдВ, рддреЛ рд╣рдо рдореБрдЦреНрдп рдкреГрд╖реНрда рдкрд░ рд╣реЗрдбрд░ рдореЗрдВ 42 рджреЗрдЦреЗрдВрдЧреЗред

рдЗрд╕ рддрд░рд╣:



рдЗрд╕рдХреЗ рд╕рд╛рде рд╣реА


  • рдлреНрд▓рд╛рд╕реНрдХ-рд░реЗрд╕реНрдЯрдлреБрд▓ рдХреЗ рдЕрд▓рд╛рд╡рд╛, рдлреНрд▓рд╛рд╕реНрдХ-рд░реЗрд╕реНрдЯрдкреНрд▓рд╕ рднреА рд╣реИ, рдпрд╣рд╛рдВ рдЖрдк рдЪрд░реНрдЪрд╛ рдХреЛ рджреЗрдЦ рд╕рдХрддреЗ рд╣реИрдВ, рдЬреЛ рдмреЗрд╣рддрд░ рдпрд╛ рдЦрд░рд╛рдм рд╣реИ
  • рдЖрдк рд╕рд╛рдордиреЗ рдПрдХ рд╡реНрдпрд╡рд╕реНрдерд╛рдкрдХ рдкреИрдирд▓ рд▓рд┐рдЦ рд╕рдХрддреЗ рд╣реИрдВ, рдлрд┐рд░ рдЪрд▓рд╛ рд╕рдХрддреЗ рд╣реИрдВ: npm run build - рдЖрдкрдХреЛ рддреИрдпрд╛рд░-рдирд┐рд░реНрдорд┐рдд рд╕реНрдерд┐рд░ рдлрд╛рдЗрд▓реЗрдВ рдорд┐рд▓рддреА рд╣реИрдВ рдЬреЛ рдлреНрд▓рд╛рд╕реНрдХ рд╕рд┐рд░реНрдл рдПрдХ рдЯреЗрдореНрдкрд▓реЗрдЯ рдХреЗ рд░реВрдк рдореЗрдВ рдкреНрд░рд╕реНрддреБрдд рдХрд░ рд╕рдХрддреА рд╣реИрдВред рдЕрдзрд┐рдХ рдЬрд╛рдирдХрд╛рд░реА рдпрд╣рд╛рдБ ред рдФрд░ рдЗрд╕ рддрд░рд╣ рд╕реЗ рдЖрдк рд╡реЗрдм рд╕рд░реНрд╡рд░ рдХреЛ рдкреНрд░рддрд┐рдХреНрд░рд┐рдпрд╛ рдХреЗ рд▓рд┐рдП рдЬрд┐рдореНрдореЗрджрд╛рд░ рд░рдЦрдиреЗ рдХреА рдЖрд╡рд╢реНрдпрдХрддрд╛ рдХреЛ рд╕рдорд╛рдкреНрдд рдХрд░ рд╕рдХрддреЗ рд╣реИрдВред

Source: https://habr.com/ru/post/hi477126/


All Articles