ฟังก์ชัน จดจำฉัน ด้วย JWT

This commit is contained in:
Flook 2025-11-16 07:41:57 +07:00
parent f615e51c5c
commit 98fcaeba3f
3 changed files with 63 additions and 3 deletions

View File

@ -102,7 +102,7 @@ export default function LoginForm() {
{/* Checkbox และ Link ลืมรหัสผ่าน */}
<div className='flex justify-between items-center text-sm mt-6'>
<label className="flex items-center space-x-2 text-gray-600">
<input type="checkbox" className="checkbox checkbox-primary checkbox-sm" />
<input type="checkbox" className="checkbox checkbox-primary checkbox-sm" {...register('remember_me')}/>
<span>จดจำฉ</span>
</label>
<Link to="/forgot-password">

View File

@ -15,8 +15,18 @@ const API_BASE_URL = import.meta.env.VITE_API_BASE_URL;
* แปลง Object ใหเปนฟอรมขอม (x-www-form-urlencoded)
*/
const toFormUrlEncoded = (data) => {
// วนซ้ำ key และ encode
return Object.keys(data)
.map(key => encodeURIComponent(key) + '=' + encodeURIComponent(data[key]))
.map(key => {
let value = data[key];
// รับค่า remember_me (boolean) มาแปลงเป็น String 'true'
if (value === true) {
value = 'true';
} else if (value === false) {
value = 'false'; // หรือจะส่งเฉพาะเมื่อเป็น true ก็ได้
}
return encodeURIComponent(key) + '=' + encodeURIComponent(value);
})
.join('&');
};
@ -121,6 +131,8 @@ export const useLoginMutation = () => {
// 1. จัดการ Token และ User Data
localStorage.setItem('token', data.access_token);
// บันทึก Refresh Token แยกต่างหาก
localStorage.setItem('refresh_token', data.refresh_token);
localStorage.setItem('user', JSON.stringify(data.user));
localStorage.setItem('role', data.role); // บันทึก Role ที่ถูกต้อง
@ -251,3 +263,21 @@ export const useConfirmPasswordReset = () => {
},
});
};
/**
* งกนสำหรบเรยก API เพอตออาย Access Token วย Refresh Token
*/
export const refreshAccessToken = async () => {
const refresh = localStorage.getItem('refresh_token'); // เก็บ Refresh Token แยกต่างหาก
if (!refresh) {
throw new Error("Refresh token not found.");
}
const response = await axios.post(`${API_BASE_URL}/api/v1/auth/jwt/refresh/`, {
refresh: refresh,
});
// คืนค่า Access Token ใหม่
return response.data.access;
};

View File

@ -1,6 +1,7 @@
import axios from 'axios';
import { getStore } from '../app/store';
import { logout } from '../features/auth/authSlice';
import { refreshAccessToken } from './authApi';
// REGEX ตรวจจับตัวเลขที่ใหญ่เกิน 15 หลัก (ซึ่งเกินขีดจำกัดความปลอดภัยของ JS)
const bigIntRegex = /"id":(\d{15,})/g;
@ -55,8 +56,37 @@ axiosClient.interceptors.request.use(
axiosClient.interceptors.response.use(
(response) => response,
(error) => {
async (error) => {
const status = error.response ? error.response.status : null;
const originalRequest = error.config;
// ถ้าเป็น 401 และ Request นั้นยังไม่ถูกลอง Refresh
if (status === 401 && !originalRequest._retry) {
originalRequest._retry = true; // ตั้ง Flag ว่ากำลังจะลอง Refresh
const storeInstance = getStore();
try {
// 1. เรียกฟังก์ชัน Refresh Token
const newAccessToken = await refreshAccessToken();
// 2. บันทึก Access Token ใหม่
localStorage.setItem('token', newAccessToken);
// 3. อัปเดต Header ของ Request เดิม
originalRequest.headers.Authorization = `Bearer ${newAccessToken}`;
// 4. ลองเรียก Request เดิมซ้ำอีกครั้ง
return axiosClient(originalRequest);
} catch (refreshError) {
// หาก Refresh Token ล้มเหลว (หมดอายุ 30 วันแล้ว)
console.error("JWT Refresh Failed. Logging out.", refreshError);
storeInstance.dispatch(logout()); // บังคับ Logout
return Promise.reject(error);
}
}
// ถ้าได้รับ 401 หรือ 403 (Token หมดอายุ/ไม่มีสิทธิ์)
if (status === 401 || status === 403) {