带你一文使用NodeJS、JWT、Vue搞定基于角色的授权( 二 )


如果认证和授权都失败则一个 401 Unauthorized 响应会被返回 。
全局错误处理中间件路径: /_helpers/error-handler.js
module.exports = errorHandler;function errorHandler(err, req, res, next) {if (typeof (err) === 'string') {// 自定义应用错误return res.status(400).json({ message: err });}if (err.name === 'UnauthorizedError') {// JWT 认证错误return res.status(401).json({ message: 'Invalid Token' });}// 默认处理为 500 服务器错误return res.status(500).json({ message: err.message });}全局错误处理逻辑用来 catch 所有错误 , 也能避免在应用中遍布各种冗杂的处理逻辑 。 它被配置为主文件 server.js 里的中间件 。
角色对象/枚举值路径: /_helpers/role.js
module.exports = {Admin: 'Admin',User: 'User'}角色对象定义了例程中的所有角色 , 用起来类似枚举值 , 以避免传递字符串;所以可以使用 Role.Admin 而非 'Admin' 。
用户目录路径: /users
users 目录包含了所有特定于基于角色授权之用户特性的代码 。
用户服务路径: /users/user.service.js
const config = require('config.json');const jwt = require('jsonwebtoken');const Role = require('_helpers/role');// 这里简单的硬编码了用户信息 , 在产品环境应该存储到数据库const users = [{ id: 1, username: 'admin', password: 'admin', firstName: 'Admin', lastName: 'User', role: Role.Admin },{ id: 2, username: 'user', password: 'user', firstName: 'Normal', lastName: 'User', role: Role.User }];module.exports = {authenticate,getAll,getById};async function authenticate({ username, password }) {const user = users.find(u => u.username === usernameif (user) {const token = jwt.sign({ sub: user.id, role: user.role }, config.secret);const { password, ...userWithoutPassword } = user;return {...userWithoutPassword,token};}}async function getAll() {return users.map(u => {const { password, ...userWithoutPassword } = u;return userWithoutPassword;});}async function getById(id) {const user = users.find(u => u.id === parseInt(id));if (!user) return;const { password, ...userWithoutPassword } = user;return userWithoutPassword;}用户服务模块中包含了一个认证用户凭证并返回一个 JWT 令牌的方法、一个获得应用中所有用户的方法 , 和一个根据 id 获取单个用户的方法 。
因为要聚焦于认证和基于角色的授权 , 本例中硬编码了用户数组 , 但在产品环境中还是推荐将用户记录存储在数据库中并对密码加密 。
用户控制器路径: /users/users.controller.js
const express = require('express');const router = express.Router();const userService = require('./user.service');const authorize = require('_helpers/authorize')const Role = require('_helpers/role');// 路由router.post('/authenticate', authenticate);// 公开路由router.get('/', authorize(Role.Admin), getAll); // admin onlyrouter.get('/:id', authorize(), getById);// 所有通过认证的用户module.exports = router;function authenticate(req, res, next) {userService.authenticate(req.body).then(user => user? res.json(user): res.status(400).json({ message: 'Username or password is incorrect' })).catch(err => next(err));}function getAll(req, res, next) {userService.getAll().then(users => res.json(users)).catch(err => next(err));}function getById(req, res, next) {const currentUser = req.user;const id = parseInt(req.params.id);// 仅允许 admins 访问其他用户的记录if (id !== currentUser.sub}userService.getById(req.params.id).then(user => user ? res.json(user) : res.sendStatus(404)).catch(err => next(err));}