基于jsonwebtoken+passport实现token验证拦截

1 篇文章 0 订阅

1. JSON Web Token

JWT是实现token技术的一种解决方案,是目前最流行的跨域身份验证解决方案。今天我们主要聊一聊基于nodeJS使用jsonwebtoken实现token。

1.1. JWT的基本组成

JWT对象是一个很长的字符串,看起来没有任何规律,通过“.”分隔符分为三个字串。字串之间不换行。如图所示,JWT Token包括三个部分。
在这里插入图片描述

1.1.1. Header

JWT的头部承载两部分信息

  • 声明类型,这里是jwt
  • 声明加密的算法

JWT元数据的JSON对象参考

 {
 	"alg": "HS256",
 	"typ": "JWT"
 }

1.1.2. Payload

和Header一样,Payload 部分也是一个 JSON 对象,用来存放实际需要传递的数据。JWT 规定了7个官方字段:

  1. iss (issuer):签发人
  2. exp (expiration time):过期时间
  3. sub (subject):主题
  4. aud (audience):受众
  5. nbf (Not Before):生效时间
  6. iat (Issued At):签发时间
  7. jti (JWT ID):编号

也可以自定义私有字段

1.1.3. signature

Signature 部分是对前两部分的签名,防止数据篡改。基本步骤如下:

  • 指定一个密钥(secret)作为混淆。这个密钥只有服务器才知道,不能泄露给用户。
  • 使用 Header 里面指定的签名算法(默认是 HMAC SHA256)按照一定的公式进行计算得到签名。
  • 得到签名以后,把 Header、Payload、Signature 三个部分进行拼接,也就是前文讲到的长字符串
  • 将该长字符串作为token返回给前端。

1.2. 用法

前端接收到token,可以将其存储在localStorage、sessionStorage中(注意两者区别),也可以存储在Cookie中。
注意:存储在cookie中可以自动发送,但是无法进行跨域
一般来讲,我们会将token放入请求头部的Authorization中,接下来我们会进行实验。

2. jsonwebtoken

实验的项目是基于nodeJS开发的后台,我们选用jsonwebtoken作为生成token的辅助库。

  • npm安装

https://www.npmjs.com/package/jsonwebtoken
官方文档是个好东西,根据文档,基本配置如下:

/**
 * 签名
 * @param {*} rules 签名规则
 * @param {*} secretOrKey 密钥
 * @param {*} options token的属性
 * @param {*} callback 回调函数
 */
jwt.sign(rules, secretOrKey, options, callback)
  • 登录验证成功生成token

由此,我们根据项目需求,在登录确认账号密码无误之后,使用jsonwebtoken生成签名。
首先应该在相关文件导入jsonwebtoken。

// 导入依赖包
const jwt = require("jsonwebtoken")

登录验证核心代码如下,注意一点,生成的签名前面,必须加”Bearer “,这是作者的要求哦!

// 核心代码
if (doc.password === req.body.password) {
	const rule = { id: doc._id, email: doc.email }
	jwt.sign(rule, "secret", { expiresIn: 3600 }, (err, token) => {
	    if (err) {
	        res.json({
	            status: 500,
	            msg: "登录失败!"
	        })
	    } else {
	        res.json({
	            status: 200,
	            token: "Bearer " + token,
	            email: doc.email,
	            name: doc.name
	        })
	    }
	})
}
  • 测试

推荐一款不错的插件,可以替代postman。
在这里插入图片描述
在VSCode使用REST Client进行测试,代码如下:

POST http://localhost:5000/api/user/login HTTP/1.1
content-type: application/json

{
    "email": "xxxxxx@qq.com",
    "password": "123456"
}

发起请求,结果如下:

HTTP/1.1 200 OK
X-Powered-By: Express
Access-Control-Allow-Origin: *
Access-Control-Allow-Methods: GET, PUT, POST, OPTIONS
Access-Control-Allow-Headers: Content-Type, Authorization, Content-Length, X-Requested-With
Content-Type: application/json; charset=utf-8
Content-Length: 280
ETag: W/"118-3gBqMYUKarbx0KPF29gjiAi8UjI"
Date: Sun, 09 Feb 2020 11:20:11 GMT
Connection: close

{
  "status": 200,
  "token": "Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6IjVlMjdiYzRhZjIwN2U5ZDc5OGUzN2RmMCIsImVtYWlsIjoiNzQxNTgxODc5QHFxLmNvbSIsImlhdCI6MTU4MTI0NzE4NiwiZXhwIjoxNTgxMjUwNzg2fQ.5hfAiTeSO9CEowEv9DW4vW9T2dh92awZEDkWiQqYXpg",
  "email": "xxxxxx@qq.com",
  "name": "Hans"
}

我们可以看到,后台成功给我们返回了token,将其存在sessionStorage中。为了让我们接下来每次请求都带上这个token,我们在前端写一个请求拦截,主要用于在请求头部增加一个字段Authorization,值为token,代码如下:

// axios请求拦截
axios.interceptors.request.use(config => {
  NProgress.start()
  config.headers.Authorization = window.sessionStorage.getItem('token')
  return config
})

登录之后的请求都会带上token
在这里插入图片描述

3. passport-jwt

我们将使用这一辅助库对前端请求携带的token进行解析检验,如果检验通过,则放行,若不通过,则拦截,返回Unauthorized

  • npm安装

https://www.npmjs.com/package/passport-jwt

  • 基本配置

官方文档有较为详细的说明,主要是要对JwtStrategy的实例化。

var JwtStrategy = require('passport-jwt').Strategy,
    ExtractJwt = require('passport-jwt').ExtractJwt;
var opts = {}
opts.jwtFromRequest = ExtractJwt.fromAuthHeaderAsBearerToken();
opts.secretOrKey = 'secret';
opts.issuer = 'accounts.examplesoft.com';
opts.audience = 'yoursite.net';
passport.use(new JwtStrategy(opts, function(jwt_payload, done) {
    User.findOne({id: jwt_payload.sub}, function(err, user) {
        if (err) {
            return done(err, false);
        }
        if (user) {
            return done(null, user);
        } else {
            return done(null, false);
            // or you could create a new account
        }
    });
}));
  • 初始化

我们首先应该在后台的主文件初始化passport,我的项目是server.js。

// 导入passport
const passport = require("passport");
// passport初始化
app.use(passport.initialize());
require("./config/passport")(passport);
  • 解析token,通过则放行

config/passport.js文件代码如下:

var JwtStrategy = require('passport-jwt').Strategy,
    ExtractJwt = require('passport-jwt').ExtractJwt;

//设置策略选项
let opts = {
    // 密钥
    secretOrKey: "secret",
    // 定义从请求头的Authrization抽取token数据
    jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken()
}

// 实例化策略对象
var jwtConfig = new JwtStrategy(opts, (jwt_payload, done) => {
    // jwt_payload是经过解析的 token字符串,
    console.log(jwt_payload);
    User.findOne({ email: jwt_payload.email })
        .exec((err, user) => {
            if (err) {
            	// 若失败,则返回401,Authorization
                return done(result.error, false);
            } else {
                if (user) {
                	// 成功则放行
                    return done(null, user);
                } else {
                	// 若失败,则返回401,Authorization
                    return done(result.error, false);
                }
            }
        });
});

module.exports = passport => {
    passport.use(jwtConfig)
}

在server.js,应该利用passport.authenticate()进行验证拦截

passport.authenticate("加密算法策略", 验证条件,回调)

示例代码如下:

app.use("/api/records", passport.authenticate("jwt", { session: false }), recordsRouter)
  • 测试

到此为止,我们已经完成了整个JWT验证的所有配置,接下来进行测试。仍然使用REST Client进行测试,带上Authorization。

GET http://localhost:5000/api/records/histories?email=xxxxxx@qq.com HTTP/1.1
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6IjVlMjdiYzRhZjIwN2U5ZDc5OGUzN2RmMCIsImVtYWlsIjoiNzQxNTgxODc5QHFxLmNvbSIsImlhdCI6MTU4MTI0ODExNCwiZXhwIjoxNTgxMjUxNzE0fQ.ItfaSVJTWiENE9Hq9S7ygr-Fmiuk3WNXyVn85uprHdY

结果如下:

HTTP/1.1 200 OK
X-Powered-By: Express
Access-Control-Allow-Origin: *
Access-Control-Allow-Methods: GET, PUT, POST, OPTIONS
Access-Control-Allow-Headers: Content-Type, Authorization, Content-Length, X-Requested-With
Content-Type: application/json; charset=utf-8
Content-Length: 2807
ETag: W/"af7-QTQanyH1ZISPRxlui9VxauIxQSA"
Date: Sun, 09 Feb 2020 11:57:15 GMT
Connection: close

{
  "status": 200,
  "msg": []
}

如果我们不在Header中加入Authorization,会发生说明呢?如下所示:

HTTP/1.1 401 Unauthorized
X-Powered-By: Express
Access-Control-Allow-Origin: *
Access-Control-Allow-Methods: GET, PUT, POST, OPTIONS
Access-Control-Allow-Headers: Content-Type, Authorization, Content-Length, X-Requested-With
Date: Sun, 09 Feb 2020 12:00:24 GMT
Connection: close
Content-Length: 12

Unauthorized

返回状态码401,以及Unauthorized,说明未验证通过。

3. 结语

花了一点时间,总结一下jwt,还是收获不少的。希望大家能够从中获益,能够帮助到大家,是一件非常开心的事情。如果有什么不对的地方,还望指正。

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值