如何在Express路由中添加授权?多用户路由权限配置方法
嘿,这个权限控制需求在Express里用中间件就能轻松搞定,我给你捋一遍具体实现步骤,都是项目里常用的方案:
1. 先定义清晰的权限规则
首先我们把用户角色和允许访问的路由前缀对应起来,还有公共路由(比如/login)单独列出来,后续维护起来会很方便:
// 可以放在单独的config/permissions.js文件里 const rolePermissions = { A: ['/blogs', '/news'], // A用户能访问/blogs和/news下的所有子路由 B: ['/profile'], // B用户只能访问/profile下的所有子路由 public: ['/login'] // 所有用户都能访问的公共路由 }; module.exports = rolePermissions;
2. 编写权限校验中间件
这是核心部分,中间件会在请求到达具体路由前,检查用户的登录状态和权限:
// middleware/checkPermission.js const rolePermissions = require('../config/permissions'); const checkPermission = (req, res, next) => { // 第一步:检查是否是公共路由,直接放行 const isPublic = rolePermissions.public.some(route => req.path.startsWith(route)); if (isPublic) { return next(); } // 第二步:检查用户是否已登录(这里假设登录后用户信息存在req.user中,比如通过session或JWT) if (!req.user) { return res.status(401).send('请先登录后再访问'); } // 第三步:根据用户角色获取允许的路由列表 const allowedRoutes = rolePermissions[req.user.role]; if (!allowedRoutes) { return res.status(403).send('您没有权限访问任何资源'); } // 第四步:验证当前请求路径是否在允许的范围内(支持子路由匹配,比如/blogs/new、/blogs/:id/edit都属于/blogs前缀) const hasAccess = allowedRoutes.some(route => req.path.startsWith(route)); if (hasAccess) { next(); // 权限通过,继续执行后续路由 } else { res.status(403).send('您没有权限访问该资源'); } }; module.exports = checkPermission;
3. 处理登录逻辑(获取用户角色)
登录路由不需要权限校验,登录成功后我们要把用户的角色信息存储起来,方便后续中间件获取。这里分两种常见场景:
场景1:使用Session存储用户信息
先安装express-session:npm install express-session
然后在登录路由里设置session:
// routes/login.js const express = require('express'); const router = express.Router(); router.post('/login', (req, res) => { // 这里替换成你的实际用户验证逻辑(比如从数据库查用户) const { username, password } = req.body; let user; if (username === 'A' && password === 'passwordA') { user = { id: 1, role: 'A' }; } else if (username === 'B' && password === 'passwordB') { user = { id: 2, role: 'B' }; } else { return res.status(400).send('用户名或密码错误'); } // 将用户信息存入session req.session.user = user; res.send(`欢迎${user.role}用户登录`); }); module.exports = router;
场景2:使用JWT令牌
如果是前后端分离项目,更推荐用JWT。先安装jsonwebtoken:npm install jsonwebtoken
先写一个JWT验证中间件:
// middleware/authenticateToken.js const jwt = require('jsonwebtoken'); const authenticateToken = (req, res, next) => { const authHeader = req.headers['authorization']; // 从请求头中获取Bearer令牌 const token = authHeader && authHeader.split(' ')[1]; if (!token) { return res.status(401).send('请提供访问令牌'); } // 验证令牌并解析用户信息 jwt.verify(token, 'your-jwt-secret-key', (err, user) => { if (err) { return res.status(403).send('令牌无效'); } req.user = user; next(); }); }; module.exports = authenticateToken;
然后登录路由返回JWT令牌:
// routes/login.js const express = require('express'); const jwt = require('jsonwebtoken'); const router = express.Router(); router.post('/login', (req, res) => { // 同样的用户验证逻辑 const { username, password } = req.body; let user; if (username === 'A' && password === 'passwordA') { user = { id: 1, role: 'A' }; } else if (username === 'B' && password === 'passwordB') { user = { id: 2, role: 'B' }; } else { return res.status(400).send('用户名或密码错误'); } // 生成JWT令牌,有效期1小时 const token = jwt.sign(user, 'your-jwt-secret-key', { expiresIn: '1h' }); res.json({ token }); }); module.exports = router;
4. 挂载路由并应用中间件
最后在主文件app.js里把所有路由和中间件整合起来:
如果用Session:
const express = require('express'); const session = require('express-session'); const app = express(); const checkPermission = require('./middleware/checkPermission'); const loginRouter = require('./routes/login'); const blogRouter = require('./routes/blog'); const newsRouter = require('./routes/news'); const profileRouter = require('./routes/profile'); // 配置Session app.use(session({ secret: 'your-session-secret-key', resave: false, saveUninitialized: false, cookie: { secure: false } // 生产环境建议设为true,配合HTTPS })); // 解析请求体 app.use(express.json()); app.use(express.urlencoded({ extended: true })); // 先挂载登录路由,不需要权限校验 app.use(loginRouter); // 挂载权限校验中间件,之后的所有路由都会经过这个中间件 app.use(checkPermission); // 挂载其他业务路由 app.use(blogRouter); app.use(newsRouter); app.use(profileRouter); app.listen(3000, () => { console.log('服务器运行在http://localhost:3000'); });
如果用JWT:
只需要把Session的配置换成JWT验证中间件,并且把它放在权限中间件前面:
const express = require('express'); const app = express(); const authenticateToken = require('./middleware/authenticateToken'); const checkPermission = require('./middleware/checkPermission'); const loginRouter = require('./routes/login'); const blogRouter = require('./routes/blog'); const newsRouter = require('./routes/news'); const profileRouter = require('./routes/profile'); // 解析请求体 app.use(express.json()); app.use(express.urlencoded({ extended: true })); // 挂载登录路由 app.use(loginRouter); // 先验证JWT,再校验权限 app.use(authenticateToken); app.use(checkPermission); // 挂载业务路由 app.use(blogRouter); app.use(newsRouter); app.use(profileRouter); app.listen(3000, () => { console.log('服务器运行在http://localhost:3000'); });
一些额外说明
- 如果需要更细粒度的权限控制(比如A用户能看博客列表但不能编辑),可以把权限规则写得更具体,比如
A: ['/blogs', '/blogs/new', '!/blogs/:id/edit'],然后在中间件里处理排除规则,不过当前需求按前缀匹配足够了。 - 建议把权限规则、中间件都拆成单独文件,不要写在app.js里,这样项目结构更清晰,维护起来也方便。
内容的提问来源于stack exchange,提问作者Rifat Bin Reza
相关产品推荐
相关产品推荐

