安装JWT相关的包
运行如下命令,安装如下两个JWT
相关的包:
npm install jsonwebtoken express-jwt
其中:
jsonwebtoken
用于生成JWT
字符串express-jwt
用于将JWT
字符串解析还原成JSON
对象
导入JWT相关的包
使用 require()
函数,分别导入JWT
相关的两个包:
//导入用于生成JWT字符串的包 const jwt = require('jsonwebtoken'); //导入用于将客户端发送过来的JWT字符串解析还原成JSON对象的包 const expressJWT = require('express-jwt');
定义secret密钥
为了保证JWT
字符串的安全性,防止JWT
字符串在网络传输过程中被人破解,需要定义一个用于加密和解密的secret
密钥:
- 当生成
JWT
字符串的时候,需要使用secret
密钥对用户的信息进行加密,最终得到加密好的JWT
字符串 - 当把
JWT
字符串解析还原成JSON
对象的时候,需要使用secret
密钥进行解密
//secret 密钥的本质: 一个字符串 const secretKey = 'secretkey!!!'
在登录成功后生成JWT字符串
调用jsonwebtoken
包提供的 sign()
方法,将用户的信息加密成JWT字符串,响应给客户端:
app.post('/api/login', (req, res) => { //将 req.body 请求体中的数据 转存为 userinfo 常量 const userinfo = req.body; //登录失败 if (userinfo.username !== 'admin' || userinfo.password !== '000000') { return res.send({ status: 400, msg: '登录失败!' }) } //登录成功 //在登录成功之后 调用 jwt.sign() 方法生成JWT字符串 并通过 token 属性发送给客户端 //参数1: 用户的信息对象 //参数2: 加密的密钥 //参数3: 配置对象 可以配置当前 token 的有效期 const tokenStr = jwt.sign({ username: userinfo.username }, secretKey, { expiresIn: '30s' }); res.send({ status: 200, msg: '登录成功', token: 'tokenStr' //要发送给客户端的token字符串 }) })
将JWT字符串还原为JSON对象
客户端每次在访问那些有权限接口的时候,都要主动通过请求头中的 Authorization
字段,将 Token
字符串发送到服务器进行身份认证
此时,服务器可以通过 express-jwt
这个中间件,自动将客户端发送过来的 Token
解析还原成 JSON
对象
//使用 app.use() 注册将JWT字符串解析还原成JSON对象的中间件 //.unless() 方法通过正则表达式 指定哪些接口不需要通过权限 //正则中 '\'用来转义 '^'表示指定以什么开头的字符串 app.use(expressJWT({ secret: secretKey }).unless({path: [/^\/api\//]})); //如果出现报错 尝试在 secret: secretKey的后面加上 ", algorithms: ['HS256'] "
使用req.user获取用户信息
当 express-jwt
这个中间件配置成功之后,即可在那些有权限的接口中,使用 req.user
对象,来访问从 JWT
字符串中解析出来的用户信息了,示例代码如下:
//有权限的接口 app.get('/admin/getinfo', (req, res) => { //使用 req.user 获取用户信息 并使用data属性将用户信息发送给客户端 console.log(req.user); res.send({ status: 200, message: '获取用户信息成功!', data: req.user //要发送给客户端的信息 }) })
先登录一次,获取token
再在指定的时间内,进行getinfo
,需要将登录时得到的token
,通过Header
传给接口验证
捕获解析JWT
失败后产生的错误
当使用express-jwt
解析Token
字符串时,如果客户端发送的Token
字符串过期或不合法,会产生一个解析失败的错误,影响项目的正常运行,可以通过Express的错误中间件,捕获这个错误并且进行相关的处理,示例代码如下:
//在所有路由后面定义错误中间件 //使用全局错误处理中间件 捕获解析 JWT 失败后产生的错误 app.use((err, req, res, next) => { //判断是否由 Token 解析失败导致的 if (err.name == 'UnauthorizedError') { return res.send({ status: 401, message: '无效的Token' }) } res.send({ status: 500, message: '未知的错误' }) })
老版本express-jwt @6 和新版本express-jwt @8
老版本express-jwt用法:
const expressJWT = require("express-jwt") const scretKey = 'hhhhhhh' app.use(expressJWT({ secret: scretKey, algorithms:["HS256"], }).unless({ // 列表里的路由不会被token限制访问 path:["/login",{url : /^\/upload/ , methods : ["GET"]}] }));
新版本express-jwt用法:
const expressJWT = require("express-jwt") app.use(expressJWT.expressjwt({ secret: scretKey, algorithms:["HS256"], }).unless({ // 列表里的路由不会被token限制访问 path:["/login",{url : /^\/upload/ , methods : ["GET"]}] }));
其中algorithms
是配置算法,值有多种
HS256
使用同一个「secret_key」
进行签名与验证(对称加密)。一旦secret_key
泄漏,就毫无安全性可言了。
- 因此
HS256
只适合集中式认证,签名和验证都必须由可信方进行。 - 传统的单体应用广泛使用这种算法,但是请不要在任何分布式的架构中使用它!
RS256
是使用 RSA
私钥进行签名,使用 RSA
公钥进行验证。公钥即使泄漏也毫无影响,只要确保私钥安全就行。
RS256
可以将验证委托给其他应用,只要将公钥给他们就行。
ES256
和RS256
一样,都使用私钥签名,公钥验证。算法速度上差距也不大,但是它的签名长度相对短很多(省流量),并且算法强度和RS256
差不多