Node.js Koa2使用JWT进行鉴权的方法示例


Posted in Javascript onAugust 17, 2018

前言

在前后端分离的开发中,通过 Restful API 进行数据交互时,如果没有对 API 进行保护,那么别人就可以很容易地获取并调用这些 API 进行操作。那么服务器端要如何进行鉴权呢?

Json Web Token 简称为 JWT,它定义了一种用于简洁、自包含的用于通信双方之间以 JSON 对象的形式安全传递信息的方法。JWT 可以使用 HMAC 算法或者是 RSA 的公钥密钥对进行签名。

说得好像跟真的一样,那么到底要怎么进行认证呢?

Node.js Koa2使用JWT进行鉴权的方法示例

首先用户登录时,输入用户名和密码后请求服务器登录接口,服务器验证用户名密码正确后,生成token并返回给前端,前端存储token,并在后面的请求中把token带在请求头中传给服务器,服务器验证token有效,返回正确数据。

既然服务器端使用 Koa2 框架进行开发,除了要使用到 jsonwebtoken 库之外,还要使用一个 koa-jwt 中间件,该中间件针对 Koa 对 jsonwebtoken 进行了封装,使用起来更加方便。下面就来看看是如何使用的。

生成token

这里注册了个 /login 的路由,用于用户登录时获取token。

const router = require('koa-router')();
const jwt = require('jsonwebtoken');
const userModel = require('../models/userModel.js');

router.post('/login', async (ctx) => {
  const data = ctx.request.body;
  if(!data.name || !data.password){
    return ctx.body = {
      code: '000002',
      data: null,
      msg: '参数不合法'
    }
  }
  const result = await userModel.findOne({
    name: data.name,
    password: data.password
  })
  if(result !== null){
    const token = jwt.sign({
      name: result.name,
      _id: result._id
    }, 'my_token', { expiresIn: '2h' });
    return ctx.body = {
      code: '000001',
      data: token,
      msg: '登录成功'
    }
  }else{
    return ctx.body = {
      code: '000002',
      data: null,
      msg: '用户名或密码错误'
    }
  }
});

module.exports = router;

在验证了用户名密码正确之后,调用 jsonwebtoken 的 sign() 方法来生成token,接收三个参数,第一个是载荷,用于编码后存储在 token 中的数据,也是验证 token 后可以拿到的数据;第二个是密钥,自己定义的,验证的时候也是要相同的密钥才能解码;第三个是options,可以设置 token 的过期时间。

获取token

接下来就是前端获取 token,这里是在 vue.js 中使用 axios 进行请求,请求成功之后拿到 token 保存到 localStorage 中。这里登录成功后,还把当前时间存了起来,除了判断 token 是否存在之外,还可以再简单的判断一下当前 token 是否过期,如果过期,则跳登录页面

submit(){
  axios.post('/login', {
    name: this.username,
    password: this.password
  }).then(res => {
    if(res.code === '000001'){
      localStorage.setItem('token', res.data);
      localStorage.setItem('token_exp', new Date().getTime());
      this.$router.push('/');
    }else{
      alert(res.msg);
    }
  })
}

然后请求服务器端API的时候,把 token 带在请求头中传给服务器进行验证。每次请求都要获取 localStorage 中的 token,这样很麻烦,这里使用了 axios 的请求拦截器,对每次请求都进行了取 token 放到 headers 中的操作。

axios.interceptors.request.use(config => {
  const token = localStorage.getItem('token');
  config.headers.common['Authorization'] = 'Bearer ' + token;
  return config;
})

验证token

通过 koa-jwt 中间件来进行验证,用法也非常简单

const koa = require('koa');
const koajwt = require('koa-jwt');
const app = new koa();

// 错误处理
app.use((ctx, next) => {
  return next().catch((err) => {
    if(err.status === 401){
      ctx.status = 401;
      ctx.body = 'Protected resource, use Authorization header to get access\n';
    }else{
      throw err;
    }
  })
})

app.use(koajwt({
  secret: 'my_token'
}).unless({
  path: [/\/user\/login/]
}));

通过 app.use 来调用该中间件,并传入密钥 {secret: 'my_token'} ,unless 可以指定哪些 URL 不需要进行 token 验证。token 验证失败的时候会抛出401错误,因此需要添加错误处理,而且要放在 app.use(koajwt()) 之前,否则不执行。

如果请求时没有token或者token过期,则会返回401。

解析koa-jwt

我们上面使用 jsonwebtoken 的 sign() 方法来生成 token 的,那么 koa-jwt 做了些什么帮我们来验证 token。

resolvers/auth-header.js
module.exports = function resolveAuthorizationHeader(ctx, opts) {
  if (!ctx.header || !ctx.header.authorization) {
    return;
  }
  const parts = ctx.header.authorization.split(' ');
  if (parts.length === 2) {
    const scheme = parts[0];
    const credentials = parts[1];
    if (/^Bearer$/i.test(scheme)) {
      return credentials;
    }
  }
  if (!opts.passthrough) {
    ctx.throw(401, 'Bad Authorization header format. Format is "Authorization: Bearer <token>"');
  }
};

在 auth-header.js 中,判断请求头中是否带了 authorization,如果有,将 token 从 authorization 中分离出来。如果没有 authorization,则代表了客户端没有传 token 到服务器,这时候就抛出 401 错误状态。

verify.js

const jwt = require('jsonwebtoken');

module.exports = (...args) => {
  return new Promise((resolve, reject) => {
    jwt.verify(...args, (error, decoded) => {
      error ? reject(error) : resolve(decoded);
    });
  });
};

在 verify.js 中,使用 jsonwebtoken 提供的 verify() 方法进行验证返回结果。jsonwebtoken 的 sign() 方法来生成 token 的,而 verify() 方法则是用来认证和解析 token。如果 token 无效,则会在此方法被验证出来。

index.js

const decodedToken = await verify(token, secret, opts);
if (isRevoked) {
  const tokenRevoked = await isRevoked(ctx, decodedToken, token);
  if (tokenRevoked) {
    throw new Error('Token revoked');
  }
}
ctx.state[key] = decodedToken; // 这里的key = 'user'
if (tokenKey) {
  ctx.state[tokenKey] = token;
}

在 index.js 中,调用 verify.js 的方法进行验证并解析 token,拿到上面进行 sign() 的数据 {name: result.name, _id: result._id} ,并赋值给 ctx.state.user ,在控制器中便可以直接通过 ctx.state.user 拿到 name 和 _id 。

安全性

  • 如果 JWT 的加密密钥泄露的话,那么就可以通过密钥生成 token,随意的请求 API 了。因此密钥绝对不能存在前端代码中,不然很容易就能被找到。
  • 在 HTTP 请求中,token 放在 header 中,中间者很容易可以通过抓包工具抓取到 header 里的数据。而 HTTPS 即使能被抓包,但是它是加密传输的,所以也拿不到 token,就会相对安全了。

总结

这上面就是 jwt 基本的流程,这或许不是最完美的,但在大多数登录中使用已经足够了。

上面的代码可能不够具体,这里使用 Koa + mongoose + vue.js 实现的一个例子 :jwt-demo ,可以做为参考。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Javascript 相关文章推荐
动态改变textbox的宽高的js
Oct 26 Javascript
javascript 写类方式之七
Jul 05 Javascript
JS 对象介绍
Jan 20 Javascript
IE图片缓存document.execCommand(&quot;BackgroundImageCache&quot;,false,true)
Mar 01 Javascript
动态获取复选框checkbox选中个数的jquery代码
Jun 25 Javascript
IE下通过a实现location.href 获取referer的值
Sep 04 Javascript
JavaScript跨浏览器获取页面中相同class节点的方法
Mar 03 Javascript
JavaScript之Object类型介绍
Apr 01 Javascript
JavaScript预解析及相关技巧分析
Apr 21 Javascript
Dojo获取下拉框的文本和值实例代码
May 27 Javascript
深入理解JavaScript中的块级作用域、私有变量与模块模式
Oct 31 Javascript
原生js实现淘宝购物车功能
Jun 23 Javascript
jQuery实现轮播图及其原理详解
Apr 12 #jQuery
chosen实现省市区三级联动
Aug 16 #Javascript
webuploader实现上传图片到服务器功能
Aug 16 #Javascript
Vue+webpack+Element 兼容问题总结(小结)
Aug 16 #Javascript
Vue 与 Vuex 的第一次接触遇到的坑
Aug 16 #Javascript
深入理解Vue 组件之间传值
Aug 16 #Javascript
JS加密插件CryptoJS实现的Base64加密示例
Aug 16 #Javascript
You might like
PHP中的cookie不用刷新就生效的方法
2012/02/04 PHP
深入php数据采集的详解
2013/06/02 PHP
Thinkphp实现MySQL读写分离操作示例
2014/06/25 PHP
jQuery向下滚动即时加载内容实现的瀑布流效果
2016/01/07 PHP
详解PHP的Yii框架中组件行为的属性注入和方法注入
2016/03/18 PHP
ThinkPHP表单令牌错误的相关解决方法分析
2016/05/20 PHP
thinkPHP3.x常量整理(预定义常量/路径常量/系统常量)
2016/05/20 PHP
PHP 获取客户端 IP 地址的方法实例代码
2018/11/11 PHP
jquery中load方法的用法及注意事项说明
2014/02/22 Javascript
js实现touch移动触屏滑动事件
2015/04/17 Javascript
jquery 删除节点 添加节点 找兄弟节点的简单实现
2016/12/07 Javascript
Canvas 制作动态进度加载水球详解及实例代码
2016/12/09 Javascript
jQuery、layer实现弹出层的打开、关闭功能
2017/06/28 jQuery
vuejs移动端实现div拖拽移动
2019/07/25 Javascript
Vue 解决多级动态面包屑导航的问题
2019/11/04 Javascript
js判断在哪个浏览器打开项目的方法
2020/01/21 Javascript
[03:04]2018年国际邀请赛典藏宝瓶&莱恩声望物品展示 片尾有彩蛋
2018/06/04 DOTA
高性能web服务器框架Tornado简单实现restful接口及开发实例
2014/07/16 Python
跟老齐学Python之玩转字符串(2)
2014/09/14 Python
python中as用法实例分析
2015/04/30 Python
python字符串常用方法
2018/06/14 Python
python3 读取Excel表格中的数据
2018/10/16 Python
Python中的上下文管理器相关知识详解
2019/09/19 Python
python如果快速判断数字奇数偶数
2019/11/13 Python
解决Jupyter Notebook开始菜单栏Anaconda下消失的问题
2020/04/13 Python
ebookers英国:隶属全球最大的在线旅游公司Expedia
2017/12/28 全球购物
播音主持女孩的自我评价分享
2013/11/20 职场文书
施工资料员岗位职责
2014/01/06 职场文书
顶撞领导检讨书
2014/01/29 职场文书
《只有一个地球》教学反思
2014/02/14 职场文书
大学推普周活动总结
2015/05/07 职场文书
2019奶茶店创业计划书范本!
2019/07/15 职场文书
python opencv常用图形绘制方法(线段、矩形、圆形、椭圆、文本)
2021/04/12 Python
Python如何利用正则表达式爬取网页信息及图片
2021/04/17 Python
python中的被动信息搜集
2021/04/29 Python
怎么禁用Windows 11快照布局? win11不使用快照布局的技巧
2021/11/21 数码科技