目录
?前言
?准备工作
?实现生成token
?配置token校验
?完成获取用户信息接口
?配置校验错误的中间件
?总结
?前言
在构建Node.js应用时,安全性是一个至关重要的方面。本博客旨在解决如何在Express框架中实现令牌生成的问题,使用express-jwt和jsonwebtoken这两个流行的库来确保你的应用程序在身份验证方面具有强大的安全性。
token的作用
身份验证(Authentication):
证明身份: Token 用于证明用户的身份。当用户通过用户名和密码进行登录后,服务器会颁发一个 Token 给客户端。客户端在后续的请求中携带这个 Token,服务器通过验证 Token 来确认用户的身份,而不需要再次提供用户名和密码。这种方式避免了在每个请求中传输敏感的用户名和密码。无状态性: Tokens 可以使系统实现无状态的身份验证,因为每次请求都包含了足够的信息(Token)来识别和验证用户,而不需要在服务器端保存用户的登录状态。授权(Authorization):
访问控制: Token 用于授权用户对特定资源的访问权限。服务器在验证用户身份后,生成包含权限信息的 Token,并将其返回给客户端。客户端在后续请求中携带 Token,服务器通过解析 Token 中的权限信息来确定用户是否被授权访问请求的资源。减轻服务器压力: 使用 Token 进行授权可以减轻服务器的负担,因为服务器无需在每个请求中都重新验证用户的身份和权限,而只需验证 Token 的有效性。总的来说,Token在身份验证和授权方面提供了一种安全、高效的机制。通过使用 Token,可以实现无状态的身份验证、提高系统的安全性,同时减轻服务器的压力,提高系统的性能。
?准备工作
构建express应用,安装需要用到的库
npm i expressnpm i express-jwtnpm i jsonwebtoken
express-jwt 是用于在 Express 应用程序中验证 JSON Web Tokens (JWT) 的中间件。它可以帮助你确保只有经过身份验证的用户才能访问受保护的路由或资源。
jsonwebtoken 是用来生成客户端需要的token
搭建出基本的express应用
const express = require('express')const app = express()app.listen(3000, console.log('server is running on port 3000'))
只是做一个案例
可以新建这样的三个文件
config.js
module.exports = { jwtSecretKey: 'mima', // jwt加密的密钥 expiresIn: '1h' // token过期时间}
?实现生成token
首先看登录接口中生成token
const express = require('express')const jwt = require('jsonwebtoken')const config = require('./config')//路由const router = express.Router()//路由中间件router.get('/login', (req, res) => { //生成token const user = { id: '1', username: 'zhangsan', password: '123' } //这里的user对象会被传递给jwt.sign方法中, //生成的token中会包含id和username,password三个属性,后面不需要用户传入也能在token中获取到这些信息 const token = jwt.sign(user, config.jwtSecretKey, { expiresIn: config.expiresIn }) res.send({ code: 200, msg: '登录成功', token: 'Bearer ' + token })})module.exports = router
注意这里的user对象一般情况下是从数据库拿来的,这里只是模拟一下,生成的token中会包含id和username,password三个属性,后面不需要用户传入也能在token中获取到这些信息,等会在获取用户信息的接口中会看到。
这里为什么要在生成的token前加上Bearer,Bearer 是一种身份验证方案类型(Authentication Scheme)。Bearer 作为一个字符串附加到 Token 前面,构成了一种规范的表示方式,即 Bearer Token。 即是一种统一的标准规范。
记住需要在app.js中注册路由
const loginRouter = require('./loginRouter')app.use('/api', loginRouter)
我这里使用postman测试了
可以看到token已经生成了
?配置token校验
const express = require('express')const app = express()const expressJWT = require('express-jwt')const config = require('./config')//配置jwt中间件app.use(expressJWT({ secret: config.jwtSecretKey }).unless({ path: [/^\/api/] }))//unless({ path: [/^\/api/] }) //意思就是除了/api开头的请求都验证,其他请求都需要验证tokenconst loginRouter = require('./loginRouter')app.use('/api', loginRouter)app.listen(3000, console.log('server is running on port 3000'))
注意后面的unless,意思就是除了/api开头的请求都验证,其他请求都需要验证token。
这时候我们可以将/api改成其他的
这样登录接口就会出现问题,没有token会被拦截
这时候我们就需要拿着之前生成的token填入到headers中,才能调用接口
?完成获取用户信息接口
const express = require('express')const router = express.Router()router.get('/getUser', (req, res) => { const { username, id, password } = req.user res.send({ code: 200, msg: '获取用户成功', data: { username, id, password } })})module.exports = router
因为在生成token时,将user对象一起加进去了,所以在生成的token中会携带这些信息,我们可以在req.user中拿到这些信息。
记得在app.js中注册路由
const userRouter = require('./getUserRouter')app.use('/user', userRouter)
测试接口
如果没有携带token,就会这样
在headers中携带token
?配置校验错误的中间件
如果没有携带token,或者是token过期了,我们都得将错误信息相应给客户端,而不是直接报错,这样会影响我们整个程序的运行。
//配置验证错误中间件app.use(function (err, req, res, next) { console.log(err)})
我们可以先测试二种情况得到的err有什么不一样
无效的token
token过期
如果想要简单点可以直接这样
app.use(function (err, req, res, next) { if (err.name === 'UnauthorizedError') { res.status(401).send(err.message) }})
如果想要中文的相应可以这样
app.use(function (err, req, res, next) { if (err.name === 'UnauthorizedError' && err.message === 'invalid token') { res.status(401).send('无效的token') } if (err.name === 'UnauthorizedError' && err.message === 'jwt expired') { res.status(401).send('token过期') }})
这样整个程序就不会被打断了
?总结
JWT 的作用: JSON Web Token(JWT)在应用程序中充当了身份验证和授权的关键工具。它通过在生成的令牌中携带用户信息,实现了无状态的身份验证,避免了传输敏感信息,同时提高了应用的安全性。
express-jwt 的核心功能: express-jwt
主要用于验证从客户端发送的包含 JWT 的请求头。它帮助我们确认用户的身份是否有效,并在认证成功后将用户信息附加到请求对象上,方便后续处理。
生成和使用 Token: 我们学习了如何使用 jsonwebtoken
库生成 JWT,并将其发送给客户端。客户端在后续请求中携带 Token,而服务器通过 express-jwt
验证 Token 的合法性,实现了用户的无缝身份认证。
授权机制: 除了认证外,express-jwt
还可用于实现基于角色或权限的授权。通过配置额外的中间件或在路由处理函数中进行检查,我们可以确保用户具有访问特定资源或执行特定操作的权限。
?完整代码可以在主页资源中下载?