Python ✖️ ReactでTokenを持たせたログイン機能の作成
## ポートフォリオのため初めての実装なのでお手柔らかに見てください。
実装の手順
1Flaskのバックエンドのセットアップ
2ログインAPIエンドポイントの作成
3Reactフロントエンドのセットアップ
4Reactでの認証状態の管理
5ログインおよびログアウト機能の実装
1
FlaskとFlask-JWTをインストール(まだの方)
pip install Flask Flask-JWT-Extended
login.py
from flask import Flask, jsonify, request
from flask_jwt_extended import JWTManager, create_access_token, jwt_required, get_jwt_identity
from werkzeug.security import generate_password_hash, check_password_hash
app = Flask(__name__)
app.config['JWT_SECRET_KEY'] = 'your_secret_key'
app.config['JWT_ACCESS_TOKEN_EXPIRES'] = 60 * 60 # 1 hour
jwt = JWTManager(app)
users = {
'user1': {'username': 'nakamura', 'password': generate_password_hash('naaaaa')},
'user2': {'username': 'kuniyoshi', 'password': generate_password_hash('kuuuuu')}
}
@app.route('/login', methods=['POST'])
def login():
username = request.json.get('username')
password = request.json.get('password')
if username not in users or not check_password_hash(users[username]['password'], password):
return jsonify({'error': 'Invalid username or password'}), 401
access_token = create_access_token(identity={'username': username})
return jsonify({'access_token': access_token})
if __name__ == '__main__':
app.run(debug=True)
corsの有効化も忘れずに
2
/loginのエンドポイントはユーザー名とパスワードを検証することで
資格情報が正しい場合はJWTトークンを返す
1フロントで入力
2バックで合ってるかどうか確認あってたらトークンプレゼント
3トークンもらえたら画面が変わる
こんな感じ
必要な依存関係のものはインストールしてね
認証状態管理Hooksはこんな感じ
import { createContext, useState, useEffect } from 'react';
export const AuthContext = createContext();
export const AuthProvider = ({ children }) => {
const [isLoggedIn, setIsLoggedIn] = useState(!!localStorage.getItem('token'));
const [username, setUsername] = useState(localStorage.getItem('username') || '');
useEffect(() => {
const token = localStorage.getItem('token');
if (token) {
setIsLoggedIn(true);
setUsername(localStorage.getItem('username'));
}
}, []);
const login = (user, token) => {
setIsLoggedIn(true);
setUsername(user);
localStorage.setItem('token', token);
localStorage.setItem('username', user);
};
const logout = () => {
setIsLoggedIn(false);
setUsername('');
localStorage.removeItem('token');
localStorage.removeItem('username');
};
return (
<AuthContext.Provider value={{ isLoggedIn, username, login, logout }}>
{children}
</AuthContext.Provider>
);
};
フロント画面に相当する部分
index.js
import React, { useState, useContext } from 'react';
import axios from 'axios';
import { useNavigate } from 'react-router-dom';
import { AuthContext } from './AuthContext';
const Login = () => {
const [username, setUsername] = useState('');
const [password, setPassword] = useState('');
const { login } = useContext(AuthContext);
const navigate = useNavigate();
const handleSubmit = async (e) => {
e.preventDefault();
try {
const response = await axios.post('http://localhost:5000/login', { username, password });
login(username, response.data.access_token);
navigate('/home');
} catch (error) {
console.error('Login failed', error);
}
};
return (
<form onSubmit={handleSubmit}>
<input type="text" value={username} onChange={(e) => setUsername(e.target.value)} placeholder="Username" />
<input type="password" value={password} onChange={(e) => setPassword(e.target.value)} placeholder="Password" />
<button type="submit">Login</button>
</form>
);
};
export default Login;
ポイントはこの部分
<AuthContext.Provider value={{ isLoggedIn, username, login, logout }}>
{children}
</AuthContext.Provider>
ログインの認証状態などを管理してるフックスをオブジェクトのコンポーネントにすることで可読性と、他機能でグッと使いやすくなる模様
今回の場合ログイン状態がture出ない場合は、
ログイン画面にしか入れないようにしたい。
ProtectedRoute.js
import React, { useContext } from 'react';
import { Navigate } from 'react-router-dom';
import { AuthContext } from './AuthContext';
const ProtectedRoute = ({ children }) => {
const { isLoggedIn } = useContext(AuthContext);
if (!isLoggedIn) {
return <Navigate to="/" />;
}
return children;
};
export default ProtectedRoute;
ポイントとしては
ProtectedRouteが使われた時に
isLoggedInがfalseのときは/に滞在するようにする
これをchildren経由のAuthContext を使用して機能として保持される
これをApp.jsでこのようにして囲むことでこの中のルートなら
AuthContextを使用して認証のやりとりができる。
<ProtectedRoute>
で囲われたコンポーネントはログイン状態がtureでないと表示されなくなる。
このようにしているので
<AuthProvider>
<Router>
<div className="App">
<h1>Header display area</h1>
<Routes>
<Route path="/" element={<Login />} />
<Route
path="/Home"
element={
<ProtectedRoute>
<Home />
</ProtectedRoute>
}
/>
<Route
path="/editor"
element={
<ProtectedRoute>
<Python1thPage />
</ProtectedRoute>
}
/>
</Routes>
</div>
</Router>
</AuthProvider>
ちょっとまだまだ定着が甘いのでしっかり勉強したい。
Discussion