- 客户端使用用户名跟密码请求登录
- 服务端收到请求,去验证用户名与密码
- 验证成功后,服务端会签发一个 Token,再把这个 Token 发送给客户端
- 客户端收到 Token 以后可以把它存储起来,比如放在 Cookie 里或者 Local Storage 里
- 客户端每次向服务端请求资源的时候需要带着服务端签发的 Token
- 服务端收到请求,然后去验证客户端请求里面带着的 Token,如果验证成功,就向客户端返回请求的数据
JWT,读作:jot ,表示:JSON Web Tokens
JWT 标准的 Token 有三个部分:
中间用点分隔开,并且都会使用 Base64 编码,所以真正的 Token 看起来像这样:
eyJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJuaW5naGFvLm5ldCIsImV4cCI6IjE0Mzg5NTU0NDUiLCJuYW1lIjoid2FuZ2hhbyIsImFkbWluIjp0cnVlfQ.SwyHTEx_RQppr97g4J5lKXtabJecpejuef8AqKYMAJc
每个 JWT token 里面都有一个 header,也就是头部数据。里面包含了使用的算法,这个 JWT 是不是带签名的或者加密的。主要就是说明一下怎么处理这个 JWT token 。
唯一在头部里面要包含的是alg 这个属性,如果是加密的 JWT,这个属性的值就是使用的签名或者解密用的算法。如果是未加密的 JWT,这个属性的值要设置成none。
eg:
{"alg":"HS256"}
意思是这个 JWT 用的算法是 HS256,在base64url编码之后变成
eyJhbGciOiJIUzI1NiJ9
Payload 里面是 Token 的具体内容,这些内容里面有一些是标准字段,你也可以添加其它需要的内容。
两个自定义的字段,一个是name ,还有一个是admin 。
{"iss":"ninghao.net","exp":"1438955445","name":"wanghao","admin":true}
使用 base64url 编码以后就变成了这个样子:
eyJpc3MiOiJuaW5naGFvLm5ldCIsImV4cCI6IjE0Mzg5NTU0NDUiLCJuYW1lIjoid2FuZ2hhbyIsImFkbWluIjp0cnVlfQ
JWT 的最后一部分是 Signature ,这部分内容有三个部分,先是用 Base64 编码的header.payload
,再用加密算法加密一下,加密的时候要放进去一个 Secret ,这个相当于是一个密码,这个密码秘密地存储在服务端。
组成结构是这样子:
const encodedString=base64UrlEncode(header)+"."+base64UrlEncode(payload);HMACSHA256(encodedString,'secret');
处理完成以后看起来像这样:
SwyHTEx_RQppr97g4J5lKXtabJecpejuef8AqKYMAJc
最后这个在服务端生成并且要发送给客户端的 Token 看起来像这样:(由以上三部分组成)
eyJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJuaW5naGFvLm5ldCIsImV4cCI6IjE0Mzg5NTU0NDUiLCJuYW1lIjoid2FuZ2hhbyIsImFkbWluIjp0cnVlfQ.SwyHTEx_RQppr97g4J5lKXtabJecpejuef8AqKYMAJc
客户端收到这个 Token 以后把它存储下来,下回向服务端发送请求的时候就带着这个 Token 。服务端收到这个 Token ,然后进行验证,通过以后就会返回给客户端想要的资源。
准备一个简单的 Node.js 项目:
C:\Users\bnkj>cd desktopC:\Users\bnkj\Desktop>mkdir jwt-demoC:\Users\bnkj\Desktop>cd jwt-demoC:\Users\bnkj\Desktop\jwt-demo>npm init-y
安装签发与验证 JWT 的功能包,我用的叫jsonwebtoken,在项目里安装一下这个包:
C:\Users\bnkj\Desktop\jwt-demo>npm install jsonwebtoken
在项目里随便添加一个 .js 文件,比如index.js,在文件里添加下面这些代码:
const jwt=require('jsonwebtoken')// Token 数据const payload={ name:'wanghao', admin:true}// 密钥const secret='ILOVENINGHAO'// 签发 Tokenconst token= jwt.sign(payload, secret,{ expiresIn:'1day'})// 输出签发的 Token console.log(token)
非常简单,就是用了刚刚为项目安装的jsonwebtoken
里面提供的jwt.sign
功能,去签发一个 token。这个sign
方法需要三个参数:
在命令行下面,用node 命令,执行一下项目里的index.js 这个文件(node index.js),会输出应用签发的token:
C:\Users\bnkj\Desktop\jwt-demo>node index.js
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJuYW1lIjoid2FuZ2hhbyIsImFkbWluIjp0cnVlLCJpYXQiOjE1MjkwMzM5MDYsImV4cCI6MTUyOTEyMDMwNn0.DctA2QlUCrM6wLWkIO78wBVN0NLpjoIq4T5B_2WJ-PU
上面的 Token 内容并没有加密,所以如果用一些 JWT 解码功能,可以看到 Token 里面包含的内容,内容由三个部分组成,像这样:
// header{"alg":"HS256","typ":"JWT"}// payload{"admin":true,"iat":1529033906,"name":"wanghao","exp":1529120306}// signature DctA2QlUCrM6wLWkIO78wBVN0NLpjoIq4T5B_2WJ-PU
打开项目的 index.js 文件,里面添加几行代码,变成下面的样子:
const jwt=require('jsonwebtoken')// Token 数据const payload={ name:'wanghao', admin:true}// 密钥const secret='ILOVENINGHAO'// 签发 Tokenconst token= jwt.sign(payload, secret,{ expiresIn:'1day'})// 输出签发的 Token console.log(token)// 验证 Token jwt.verify(token, secret,(error, decoded)=>{if(error){ console.log(error.message)return} console.log(decoded)})
把要验证的 Token 数据,还有签发这个 Token 的时候用的那个密钥告诉 *verify* 这个方法,在一个回调里面有两个参数,*error* 表示错误,*decoded* 是解码之后的 Token 数据。
执行:
C:\Users\bnkj\Desktop\jwt-demo>node index.js
输出:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJuYW1lIjoid2FuZ2hhbyIsImFkbWluIjp0cnVlLCJpYXQiOjE1MjkwMzQ3MzMsImV4cCI6MTUyOTEyMTEzM30.swXojmu7VimFu3BoIgAxxpmm2J05dvD0HT3yu10vuqU{ name:'wanghao', admin:true, iat:1529035386, exp:1529121786}
若输出:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJuYW1lIjoid2FuZ2hhbyIsImFkbWluIjp0cnVlLCJpYXQiOjE1MjkwMzQ3MzMsImV4cCI6MTUyOTEyMTEzM30.swXojmu7VimFu3BoIgAxxpmm2J05dvD0HT3yu10vuqU invalid signature
则表示签名不对,即verify 这个方法的参数是错误的。
C:\Users\bnkj>cd desktopC:\Users\bnkj\Desktop>mkdir jwt-demoC:\Users\bnkj\Desktop>cd jwt-demoC:\Users\bnkj\Desktop\jwt-demo>npm init-y
执行结果:
Wrote toC:\Users\bnkj\Desktop\jwt-demo\package.json:{"name":"jwt-demo","version":"1.0.0","description":"","main":"index.js","scripts":{"test":"echo \"Error: no test specified\" && exit 1"},"keywords":[],"author":"","license":"ISC"}
jsonwebtoken
环境C:\Users\bnkj\Desktop\jwt-demo>npm install jsonwebtoken
结果:
npm notice created a lockfile as package-lock.json. You should commit this file. npm WARN jwt-demo@1.0.0 No description npm WARN jwt-demo@1.0.0 No repository field. + jsonwebtoken@8.5.1 added 15 packages from 10 contributors in 1.194s 1 package is looking for funding run `npm fund` for details
const jwt=require('jsonwebtoken')// Token 数据const payload={ name:'wanghao', admin:true}// 密钥const secret='ILOVENINGHAO'// 签发 Tokenconst token= jwt.sign(payload, secret,{ expiresIn:'1day'})// 输出签发的 Token console.log(token)// 验证 Token jwt.verify(token, secret,(error, decoded)=>{if(error){ console.log(error.message)return} console.log(decoded)})
注意两个函数:
sign
:需要三个参数,分别是payload,secret , 和options ,表示一些其他选项
verify
:需要三个参数,分别是携带信息的token ,密钥 secret , 和一个回调的方法,这个方法需要由两个参数,分别是 error ,返回错误信息,另一个是decoded , 返回该token解码的结果
C:\Users\bnkj\Desktop\jwt-demo>node index.js
结果:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJuYW1lIjoid2FuZ2hhbyIsImFkbWluIjp0cnVlLCJpYXQiOjE2MDg0Njk5OTQsImV4cCI6MTYwODU1NjM5NH0.7Mi8WrHAv0fWZnLV6DlkPqK_ExQ4f09Zrs8eiVzXRQs{ name:'wanghao', admin:true, iat:1608469994, exp:1608556394}
空 ,后续更新