看文章之前,强烈建议先把项目拉取下来!案例来自小弟的开源项目
「项目Github」
文章内容只是个人学习的一些总结经验,不具有权威性,这是 Node 服务端的实现,后面会写前端的实现
什么是 Token 验证
常见的 Token 验证方式种:
OAuth2
,例如:微信授权登录 (貌似也属于 Token 验证)基于 JWT 的 Token 验证
,KiteBlog
里面使用的就是这种。
什么是 JWT (JSON WEB TOKEN) ?
NodeJS (Express) 中实现 Token 的生成和验证
安装 jsonwebtoken 和 express-jwt
首先我们先安装 jsonwebtoken
和 express-jwt
这两个中间件
jsonwebtoken
: 用于生成 Token 。它也有解析 Token 的功能
express-jwt
: 用于解析 Token(比 jsonwebtoken
解决方便) , 它把解析之后的数据,存放到 requset.user
中
# 安装 jsonwebtoken
npm i jsonwebtoken
# 安装 express-jwt
npm i express-jwt
生成 token
如果你看了上面 JWT 介绍的文章,就知道 JWT 是由三部分组成的,分别是载荷(Payload)
、头部(Header)
、签名(Signature)
。
jsonwebtoken
给我们提供了sign(payload, secretOrPrivateKey, [options, callback])
方法。sign 方法对应的其实就是 JWT 签名(Signature)
的动作
payload:荷载 ,参数类型:对象 secretOrPrivateKey:自定义的密钥,密钥属于敏感信息。参数类型:字符串 options:可以配置 header 、荷载、指定算法类型。参数类型:对象 callback:回调
眼尖的朋友应该发现,payload
和 options
两个参数
都可以配置荷,下面有例子。根据自己的习惯选择就好
Payload 部分 JWT 规定了7个官方字段,这些字段都是可选字段。可直接以对象的形式传给 payload
参数。
iss (issuer):签发人
exp (expiration time):过期时间
sub (subject):主题
aud (audience):受众
nbf (Not Before):生效时间
iat (Issued At):签发时间
jti (JWT ID):编号
options
中也可以接受以上七个字段,不过字段名称有所区别。
iss ---- issuer
exp ---- expiresIn
sub ---- subject
aud ---- audience
nbf ---- notBefore
iat ---- noTimestamp
jti ---- jwtid
除此之后 options
提供了algorithm
和header
,分别对应使用的加密算法和 JWT 的 Header 部分,其实 algorithm
应该也是属于 Header 部分的。
说了这么多,其实我们一般常用的只有 exp(expiresIn)
和 algorithm
这两个字段,
例子一:
token 的有效时间是配置在 option
里
//先引入 jsonwebtoken
var jsonWebToken = require('jsonwebtoken');
//密钥,当然实际的项目中密钥应该变态一些
const SECRET_KEY = 'kite1874'
const token = jsonWebToken.sign({
// Payload 部分,官方提供七个字段这边省略,可以携带一些可以识别用户的信息。例如 userId。
// 千万不要是用敏感信息,例如密码,Payload 是可以解析出来的。
userId:user.userId
role:user.role
},SECRET_KEY,{
expiresIn:"24h", //token有效期
// expiresIn: 60 * 60 * 24 * 7, 两种写法
// algorithm:"HS256" 默认使用 "HS256" 算法
})
console.log(token)
例子二:
我们也可以在 payload
里配置有效时间
const token = jsonWebToken.sign({
//exp 的值是一个时间戳,这里表示 1h 后 token 失效
exp:Math.floor(Date.now() / 1000) + (60 * 60)
userId:user.userId
role:user.role
},SECRET_KEY)
jsonwebtoken
除了生成 token 外,还提供了解析验证 token 的方法,jwt.verify(token, secretOrPublicKey, [options, callback])
。
验证 token
express-jwt
是针对 express
框架开发的 JWT Token
验证中间件。我先来简单说以下它的用法。
主要有两种方式,一种是哪些请求需要验证就往哪里加验证;另外一种是先给全部请求加上验证,再给不需要验证的请求配置白名单
。
方式一:
var express = require('express');
var jwt = require('express-jwt');
var app = express();
//SECRET_KEY 要与生成 Token 时保持一致
const SECRET_KEY = 'kite1874'
// secret 为密钥,algorithms 为算法。需要注意的是,如果你生成 Token 的时候没有手动设置 algorithm
// 默认是使用 HS256 来加密的。「express-jwt 6.0」版本需要加 algorithms: ['HS256'] , 说起来都是泪!
app.get("/test",
jwt({ secret: SECRET_KEY, algorithms: ['HS256']}),
function(req,res){
//do something...
})
看完上面的例子,很显然不符合我们的逾期,一个正常的项目有个几十个 api 是分分钟的事。我们不可能一个个给他加上检验
方式二:
//注册中间件,相当于配置一个全局 token 验证,unless 就是上面说的白名单
//把不需要 token 验证的请求填进 path 里即可, 支持数组、字符串、正则
app.use(jwt({ secret: SECRET_KEY, algorithms: ['HS256']})
.unless({path: ['/auth/adminLogin',/^\/public\/.*/]}));
// /auth/adminLogin api 和 public 下的文件都不需要 token 验证
这种方式是不是方便很多,而且更美观,维护起来也更方便
Token 解析出来的用户信息,默认存放在 req.user
, 可以直接 req.user.userId
来使用生成 Token 时填进去的用户id
你也通过 requestProperty
和 resultProperty
来设置用户信息存放的对象。
验证错误处理
可以使用 app.use()
来注册处理验证不通过的情况
app.use(function (err, req, res, next) {
if (err.name === 'UnauthorizedError') {
res.status(401).send("干嘛呢?你想硬闯?!")
}
})
注意(个人理解,不具有权威性)
到这里 Token 的生成、验证、检验不通过错误处理就完成了。 Token 生成一般是在登录之后生成,并返回给前端,前端拿到 Token ,并在每次请求 api 的时候携带上 Token , Token 就相当于这个用户的身份,不要轻易泄露。
Token一旦签发,不能主动让它失效,只能等待它有效期过才能失效。也就是说就算你修改了密码,之前的 Token 也还是有效的。你可以修改后端生成 Token 时使用的密钥,不让之前的 Token 检验通过,但是这就表示之前所有生成 Token 都失效了,做不到针对某个用户进行注销。这显然也不合适的。 所以用户修改密码时,前端一般都要清除之前保存的 Token,再重获取新的 Token
有朋友应该会想到在后端把 Token 储存起来,每一个用户对应一个 token。修改账号时,再生成一个新的 Token 覆盖之前的 Token,但这就违背了使用 Token 的目的,Token 的使用很大程度就为了减少服务器的压力。把尽可能多的信息存储在客户端而不是服务端。