node实现基于token的身份验证


Posted in Javascript onApril 09, 2018

最近研究了下基于token的身份验证,并将这种机制整合在个人项目中。现在很多网站的认证方式都从传统的seesion+cookie转向token校验。对比传统的校验方式,token确实有更好的扩展性与安全性。

传统的session+cookie身份验证

由于HTTP是无状态的,它并不记录用户的身份。用户将账号与密码发送给服务器后,后台通过校验,但是并没有记录状态,于是下一次用户的请求仍然需要校验身份。为了解决这一问题,需要在服务端生成一条包含用户身份的记录,也就是session,再将这条记录发送给用户并存储在用户本地,即cookie。接下来用户的请求都会带上这条cookie,若客户端的cookie与服务端的session能对应上,则说明用户身份验证通过。

token身份校验

流程大致如下:

  1. 第一次请求时,用户发送账号与密码
  2. 后台校验通过,则会生成一个有时效性的token,再将此token发送给用户
  3. 用户获得token后,将此token存储在本地,一般存储在localstorage或cookie
  4. 之后的每次请求都会将此token添加在请求头里,所有需要校验身份的接口都会被校验token,若token解析后的数据包含用户身份信息,则身份验证通过。

对比传统的校验方式,token校验有如下优势:

  1. 在基于token的认证,token通过请求头传输,而不是把认证信息存储在session或者cookie中。这意味着无状态。你可以从任意一种可以发送HTTP请求的终端向服务器发送请求。
  2. 可以避免CSRF攻击
  3. 当在应用中进行 session的读,写或者删除操作时,会有一个文件操作发生在操作系统的temp 文件夹下,至少在第一次时。假设有多台服务器并且 session 在第一台服务上创建。当你再次发送请求并且这个请求落在另一台服务器上,session 信息并不存在并且会获得一个“未认证”的响应。我知道,你可以通过一个粘性 session 解决这个问题。然而,在基于 token 的认证中,这个问题很自然就被解决了。没有粘性 session 的问题,因为在每个发送到服务器的请求中这个请求的 token 都会被拦截。

下面介绍一下利用node+jwt(jwt教程)搭建简易的token身份校验

示例

当用户第一次登录时,提交账号与密码至服务器,服务器校验通过,则生成对应的token,代码如下:

const fs = require('fs');
const path = require('path');
const jwt = require('jsonwebtoken');
//生成token的方法
function generateToken(data){
  let created = Math.floor(Date.now() / 1000);
  let cert = fs.readFileSync(path.join(__dirname, '../config/pri.pem'));//私钥
  let token = jwt.sign({
    data,
    exp: created + 3600 * 24
  }, cert, {algorithm: 'RS256'});
  return token;
}

//登录接口
router.post('/oa/login', async (ctx, next) => {
  let data = ctx.request.body;
  let {name, password} = data;
  let sql = 'SELECT uid FROM t_user WHERE name=? and password=? and is_delete=0', value = [name, md5(password)];
  await db.query(sql, value).then(res => {
    if (res && res.length > 0) {
      let val = res[0];
      let uid = val['uid'];
      let token = generateToken({uid});
      ctx.body = {
        ...Tips[0], data: {token}
      }
    } else {
      ctx.body = Tips[1006];
    }
  }).catch(e => {
    ctx.body = Tips[1002];
  });

});

用户通过校验将获取到的token存放在本地:

store.set('loginedtoken',token);//store为插件

之后客户端请求需要验证身份的接口,都会将token放在请求头里传递给服务端:

service.interceptors.request.use(config => {
  let params = config.params || {};
  let loginedtoken = store.get('loginedtoken');
  let time = Date.now();
  let {headers} = config;
  headers = {...headers,loginedtoken};
  params = {...params,_:time};
  config = {...config,params,headers};
  return config;
}, error => {
  Promise.reject(error);
})

服务端对所有需要登录的接口均拦截token并校验合法性。

function verifyToken(token){
  let cert = fs.readFileSync(path.join(__dirname, '../config/pub.pem'));//公钥
  try{
    let result = jwt.verify(token, cert, {algorithms: ['RS256']}) || {};
    let {exp = 0} = result,current = Math.floor(Date.now()/1000);
    if(current <= exp){
      res = result.data || {};
    }
  }catch(e){

  }
  return res;

}

app.use(async(ctx, next) => {
  let {url = ''} = ctx;
  if(url.indexOf('/user/') > -1){//需要校验登录态
    let header = ctx.request.header;
    let {loginedtoken} = header;
    if (loginedtoken) {
      let result = verifyToken(loginedtoken);
      let {uid} = result;
      if(uid){
        ctx.state = {uid};
        await next();
      }else{
        return ctx.body = Tips[1005];
      }
    } else {
      return ctx.body = Tips[1005];
    }
  }else{
    await next();
  }
});

本示例使用的公钥与私钥可自己生成,操作如下:

  1. 打开命令行工具,输入openssl,打开openssl;
  2. 生成私钥:genrsa -out rsa_private_key.pem 2048
  3. 生成公钥: rsa -in rsa_private_key.pem -pubout -out rsa_public_key.pem

点此查看node后台代码
点此查看前端代码

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

Javascript 相关文章推荐
细品javascript 寻址,闭包,对象模型和相关问题
Apr 27 Javascript
Js 获取Gridview选中行的内容操作步骤
Feb 05 Javascript
JavaScript将字符串转换为整数的方法
Apr 14 Javascript
纯js实现悬浮按钮组件
Dec 17 Javascript
jquery滚动条插件slimScroll使用方法
Feb 09 Javascript
PHP实现本地图片上传和验证功能
Feb 27 Javascript
Vue中使用sass实现换肤功能
Sep 07 Javascript
微信小程序实现卡片层叠滑动效果
Jun 21 Javascript
浅谈Vue项目骨架屏注入实践
Aug 05 Javascript
Element-ui el-tree新增和删除节点后如何刷新tree的实例
Aug 31 Javascript
TypeScript魔法堂之枚举的超实用手册
Oct 29 Javascript
vue.js 使用原生js实现轮播图
Apr 26 Vue.js
vue-cli扩展多模块打包的示例代码
Apr 09 #Javascript
webpack中的热刷新与热加载的区别
Apr 09 #Javascript
vue写一个组件
Apr 09 #Javascript
Vue 中使用 CSS Modules优雅方法
Apr 09 #Javascript
JS中双击和单击事件冲突的解决方法
Apr 09 #Javascript
vue脚手架及vue-router基本使用
Apr 09 #Javascript
vue 设置proxyTable参数进行代理跨域
Apr 09 #Javascript
You might like
《破坏领主》销量已超100万 未来将继续开发新内容
2020/03/08 其他游戏
是否存在第一台收音机的说法
2021/03/01 无线电
用PHP制作静态网站的模板框架(四)
2006/10/09 PHP
微信开发之获取JSAPI TICKET
2017/07/07 PHP
关于js中alert弹出窗口文本换行问题简单详细说明
2012/12/11 Javascript
javascript 日期时间 转换的方法
2013/02/21 Javascript
根据IP的地址,区分不同的地区,查看不同的网站页面的js代码
2013/02/26 Javascript
Jquery中$.get(),$.post(),$.ajax(),$.getJSON()的用法总结
2013/11/14 Javascript
js实现select跳转功能代码
2014/10/22 Javascript
使用JavaScript制作一个简单的计数器的方法
2015/07/07 Javascript
浅析Javascript中bind()方法的使用与实现
2016/05/30 Javascript
jQuery基于ajax操作json数据简单示例
2017/01/05 Javascript
MUI顶部选项卡的用法(tab-top-webview-main)详解
2017/10/08 Javascript
ztree加载完成后显示勾选节点的实现代码
2018/10/22 Javascript
Vue引入Stylus知识点总结
2020/01/16 Javascript
vue-video-player 断点续播的实现
2021/02/01 Vue.js
[10:42]Team Liquid Vs Newbee
2018/06/07 DOTA
Cython 三分钟入门教程
2009/09/17 Python
Python浅拷贝与深拷贝用法实例
2015/05/09 Python
Python实现大文件排序的方法
2015/07/10 Python
Python实现读取及写入csv文件的方法示例
2018/01/12 Python
对python操作kafka写入json数据的简单demo分享
2018/12/27 Python
Python OpenCV之图片缩放的实现(cv2.resize)
2019/06/28 Python
django基于cors解决跨域请求问题详解
2019/08/06 Python
基于python全局设置id 自动化测试元素定位过程解析
2019/09/04 Python
Django 实现xadmin后台菜单改为中文
2019/11/15 Python
wxpython绘制音频效果
2019/11/18 Python
捷克街头、运动和滑板一站式商店:BoardStar.cz
2019/10/06 全球购物
Blue Nile蓝色尼罗河香港官网:世界最大在线钻石珠宝销售商
2020/05/07 全球购物
英国自行车商店:AW Cycles
2021/02/24 全球购物
创业计划书中要认真思考的问题
2013/12/28 职场文书
医院学雷锋活动策划方案
2014/02/15 职场文书
初二英语教学反思
2016/02/15 职场文书
原来实习报告是这样写的呀!
2019/07/03 职场文书
JAVA长虹键法之建造者Builder模式实现
2022/04/10 Java/Android
Linux中各个目录的作用与内容
2022/06/28 Servers