JWT(JSON Web Token):解决跨域认证的一种方案。
工作原理:
JWT会将用户的信息通过Token字符串的形式,保存在客户端浏览器中,然后服务器通过还原Token字符串的形式来认证用户的身份。
JWT的组成部分:
通常由三部分组成,分别是Header(头部)、Payload(有效荷载)、Signature(签名)。三者之间用英文的“ ."分隔。格式如下:
Header.Payload.Signature
其中Payload部分才是真正的用户信息,它是用户信息经过加密之后生成的字符串。Header和Signature是安全性相关的部分,只是为了保证Token的安全性。
JWT的使用方式:
客户端收到服务器返回的JWT之后,通常会将它储存在localStorage或 sessionStorage中。
此后,客户端每次与服务器通信,都要带上这个JWT的字符串,从而进行身份认证。推荐的做法是把JWT放在HTTP请求头的Authorization字段中,格式如下:
Authorization:Bearer <token>
在Express中使用JWT
1.安装JWT相关包:
npm install isonwebtoken express-jwt
其中:
jsonwebtoken:用于生成JWT字符串;
express-jwt:用于将JWT字符串解析还原成JSON
2.导入JWT相关的包:
// 导入用于生成JWT字符串的包
const jwt = require('jsonwebtoken')
// 导入用于将客户端发送过来的JWT字符串,解析还原成JSON对象的包
const expressJWT = require('express-jwt')
3.定义secret密钥
为了保证WT字符串的安全性,防止JWT字符串在网络传输过程中被别人破解。我们需要专门定义一个用于加密和解密的secret密钥:
(1)当生成JWT字符串的时候,需要使用secret密钥对用户的信息进行加密,最终得到密好的JWT字符串;
(2)当把JWT字符串解析还原成JSON对象的时候,需要使用secret密钥进行解密
// secret密钥的本质:就是一个字符串
const secretKey = '字符串'
4.在登录成功后生成JWT字符串
调用jsonwebtoken包提供的sign()方法,将用户的信息加密成JWT字符串,响应给客户端。
// 登录接口
app.post('/api/login',function(req,res) {
if (err) return console.log(err.message) // 失败
// 登录成功后,生成JWT字符串,通过token属性响应给客户端
res.send( {
status:200,
message:'登录成功',
// 调用jwt.sign()生成JWT字符串,三个参数分别是:用户信息对象,加密密钥,配置对象
token:jwt.sign({ username:userinfo.username},secreKey,{expiresIn:'30s'})
})
})
5.将JWT字符串还原为JSON对象
客户端每次在访问那些有权限接口的时候,都需要主动通过请求头中的Authorization字段,将Token字符串发送到服务器进行身份认证。
此时,服务器可以通过express-jwt这个中间件,自动将客户端发送过来的Token解析还原成JSON对象:
// 使用app.use()来注册中间件
// expressJWT({ secret:secretKey })就是用来解析Token中间件的
//.unless({path:[/^\/api\//] })用来指定哪些接口不需要访问权限
app.use(expressJWT({secret:secretKey}).unless({path:[/^\/api\//] }))
6.使用req.user获取用户信息:
// 这是一个有权限的API接口
app.get('/admin/getinfo',function(req,res) {
console.log(req.user)
res.send({
stauts:200,
message:'获取用户信息成功',
data:req.user
})
})
7.捕获解析JWT失败后产生的错误
当使用express-jwt解析Token字符串时,如果客户端发送过来的Token字符串过期或不合法,会产生一个解析失败的错误,影响项目的正常运行。我们可以通过Express 的错误中间件,捕获这个错误并进行相关的处理,示例代码如下:
app.use((err, req. res,next) =>{
// token解析失败导致的错误
if(err.name === 'UnauthorizedError' ) {
return res.send({
status: 401,
message: '无效的token'
})
}
// 其它原因导致的错误
res.send( {
status:500,
message: '未知错误'
})
})