目前项目中使用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;
});
通过上述代码就大致可以完成一个简单的登录鉴权的实现。