前言:JWT全称“JSON Web Token”,是实现Token的机制。官网:https://jwt.io/
JWT的应用
- JWT用于登录身份验证。
- 用户登录成功后,后端通过JWT机制生成一个token,返回给客户端。
- 客户端后续的每次请求都需要携带token,携带在authorization中。
- 后端从authorization中拿到token后,通过secretKey进行解密验证身份。
Token的组成原理
JWT生成的Token由三部分组成:header.payload.signature
-
header
- alg:指定signature采用的加密算法,默认是HS256,对称加密(加密和解密的密钥相同)
- typ:固定值,通常是JWT
- 通过base64Url算法进行编码
-
payload
- 用户id和name
- 默认携带iat,令牌签发时间(时间戳)
- exp设置令牌过期时间
- 通过base64Url算法进行编码
-
signature
- 设置一个secretKey,通过将前两个结果合并后进行HS256算法
- signature的组成:
HS256(baseUrl64(header)+'.'+baseUrl(payload)+','+secretKey)
- secreKey一定不能暴露,因为可以颁发token,也可以解密
采用HS256对称加密生成的Token(https://jwt.io/)
JWT对称加密
JWT默认使用的是HS256对称加密,其中secretKey是密钥,意味着公钥和私钥都是同一个,这样安全性不高。
例如在分布式服务中,其他系统服务器虽然可以用secretKey验证token,但是这样不安全,因为采用的是对称加密算法,每个服务器都可以通secretKey颁发token,黑客只要攻破任何一个服务器就可以拿到secretKey。
JWT非对称加密
所以我们需要使用非对称加密,加密和解密的密钥不一致。加密密钥称为“私钥”,解密密钥称为“公钥”。
采用RS256非对称加密生成的Token(https://jwt.io/)
生成私钥和公钥
mac电脑直接使用终端
windows电脑安装git,使用git bash终端。
- 输入openssl
- 输入
genrsa -out private.key 2048
生成私钥 - 输入
rsa -in private.key -pubout -out public.key
生成公钥
代码中加密使用的算法需要修改为RS256非对称加密算法(注意:RS256最小密钥大小为2048位)
nodejs中实现JWT(token非对称加密)
const Koa = require('koa')
const KoaRouter = require('@koa/router')
const jwt = require('jsonwebtoken');
const fs = require('fs')
const app = new Koa();
const loginRouter = new KoaRouter({ prefix: '/login' })
const usersRouter = new KoaRouter({ prefix: '/users' })
const privateKey = fs.readFileSync('./keys/private.key')
const publicKey = fs.readFileSync('./keys/public.key')
// login接口
loginRouter.post('/', (ctx, next) => {
const payload = { id: 1, name: 'zjc' }
const token = jwt.sign(payload, privateKey, {
expiresIn: 3000,
algorithm: 'RS256'
})
ctx.body = {
message: '登录成功',
code: 200,
token
}
})
// users接口
usersRouter.get('/', (ctx, next) => {
const token = ctx.header.authorization.replace('Bearer ', '')
console.log(token);
try {
// 验证token
jwt.verify(token, publicKey)
ctx.body = {
code: 200,
data: ['xx', 'yy']
}
} catch (error) {
ctx.body = {
code: -1001,
message: 'token无效'
}
}
})
app.use(loginRouter.routes())
app.use(usersRouter.routes())
app.listen(8000, () => {
console.log('服务器启动成功');
})