目前項目中使用Express 實現簡單API功能,需要提供一套登錄鑒權方案。
這邊是API側實現 相關路由的登錄鑒權。
大體思路:就是,登錄接口中通過jwt加密?token返回前端,前端其他接口把加密好的放入請求頭Authorization中。中間件通過請求頭解密 token獲取userId。成功后運行默認接口,失敗后返回特定狀態碼和錯誤信息。
一、jwt工具方法
路徑:src/utils/jwt.ts
import jwt from 'jsonwebtoken';
import dotenv from 'dotenv';dotenv.config({path: `.env.${process.env.NODE_ENV || 'development'}`,override: true
});const SECRET = process.env.APP_SECRET || '';export const generateToken = (userId: string) => {return jwt.sign({ userId }, SECRET, { expiresIn: '1h' });
};export const verifyToken = (token: string) => {return jwt.verify(token, SECRET) as { userId: string };
};
generateToken:是登錄使用 userId加密生成token的方法。
verifyToken:是中間件解密獲取userId的方法。
二、中間件處理
路徑:src/middlewares/auth.ts
import { Request, Response, NextFunction } from 'express';
import { verifyToken } from '../utils/jwt';export const authMiddleware = (req: Request, res: Response, next: NextFunction
) => {const authHeader = req.header('Authorization');if (!authHeader?.startsWith('Bearer ')) {res.status(401).json({ error: 'Invalid authorization format' });} else {const token = authHeader.split(' ')[1]; // 取第二部分if (!token) {res.status(401).json({ error: 'Access denied' });} else {try {const decoded = verifyToken(token);(req as any).userId = decoded.userId;next();} catch (err) {res.status(400).json({ error: 'Invalid token' });}}}
};
這里獲取userId后沒有做其他操作,優化思路可以在接口其他頭中獲取緩存的userId進行比對,判斷是否是當前登錄用戶。
三、登錄API實現
src/app.ts
import loginRouter from './routes/loginRoutes.js';
//其他代碼省略//路由
app.use('/api/login', loginRouter);
src/routes/loginRoutes.ts
import { Router } from 'express';
import loginController from '../controllers/loginController.js';const router = Router();router.post('/', loginController.login);export default router;
src/controllers/loginController.ts
import { Request, Response } from 'express';
import { generateToken } from '../utils/jwt';
import { LoginData } from '../types/login';
import { RowDataPacket } from 'mysql2';
import pool from '../config/db.js';class LoginController {public async login(req: Request, res: Response): Promise<void> {try {const { username, password } = req.body;const [rows] = await pool.query(`SELECT ...`);//sql部分省略const users = (rows as RowDataPacket[]).map((row:any) => ({... //轉換數據結構部分省略})) as LoginData[];//登錄成功后返回token 和user用戶信息。res.status(200).json({ token: (users[0]?.id ? generateToken(users[0]?.id) : ''), user: users[0] || null });} catch (err) {console.log(err);res.status(500).json({ error: 'Database error' });}}
}export default new LoginController();
這里錯誤處理和登錄不成功沒有處理,可以進行再次優化。
四、前端代碼處理
提交憑證后存儲Token至localStorage
:
// LoginComponent.js
const handleLogin = async (credentials) => {const { data } = await axios.post('/api/login', credentials);localStorage.setItem('jwtToken', data.token);// 后續跳轉邏輯
};
通過axios
攔截器自動附加Token:
// apiClient.js
axios.interceptors.request.use(config => {const token = localStorage.getItem('jwtToken');if (token) config.headers.Authorization = `Bearer ${token}`;return config;
});
通過上述代碼就大致可以完成一個簡單的登錄鑒權的實現。