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将数据库中的TEXT类型数据动态赋值到TEXTAREA中
Apr 20 Javascript
jquery实用代码片段集合
Aug 12 Javascript
jquery实现心算练习代码
Dec 06 Javascript
js变形金刚文字特效代码分享
Aug 20 Javascript
在AngularJS中使用jQuery的zTree插件的方法
Apr 21 Javascript
JavaScript的instanceof运算符学习教程
Jun 08 Javascript
JavaScript 对象详细整理总结
Sep 29 Javascript
easyUI实现类似搜索框关键词自动提示功能示例代码
Dec 27 Javascript
微信小程序 页面跳转如何实现传值
Apr 05 Javascript
Vue-Router实现组件间跳转的三种方法
Nov 07 Javascript
js常用方法、检查是否有特殊字符串、倒序截取字符串操作完整示例
Jan 26 Javascript
OpenLayers3实现对地图的基本操作
Sep 28 Javascript
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
浅析PHP递归函数返回值使用方法
2013/02/18 PHP
Linux下PHP安装mcrypt扩展模块笔记
2014/09/10 PHP
PHP中浮点数计算比较及取整不准确的解决方法
2015/01/09 PHP
VSCode+PHPstudy配置PHP开发环境的步骤详解
2020/08/20 PHP
jquery的父子兄弟节点查找示例代码
2014/03/03 Javascript
jquery新的绑定事件机制on方法的使用方法
2014/04/15 Javascript
bootstrap改变按钮加载状态
2014/12/01 Javascript
jQuery插件zoom实现图片全屏放大弹出层特效
2015/04/15 Javascript
关于JS变量和作用域详解
2016/07/28 Javascript
js实现表单及时验证功能 用户信息立即验证
2016/09/13 Javascript
完美实现js焦点轮播效果(一)
2017/03/07 Javascript
创建简单的node服务器实例(分享)
2017/06/23 Javascript
微信小程序数据存储与取值详解
2018/01/30 Javascript
解决Mac下安装nmp的淘宝镜像失败问题
2018/05/16 Javascript
JavaScript深拷贝和浅拷贝概念与用法实例分析
2018/06/07 Javascript
如何从零开始手写Koa2框架
2019/03/22 Javascript
微信小程序判断页面是否从其他页面返回的实例代码
2019/07/03 Javascript
Python网络爬虫项目:内容提取器的定义
2016/10/25 Python
Python常用时间操作总结【取得当前时间、时间函数、应用等】
2017/05/11 Python
python3使用smtplib实现发送邮件功能
2018/05/22 Python
对python requests的content和text方法的区别详解
2018/10/11 Python
pyqt5中QThread在使用时出现重复emit的实例
2019/06/21 Python
django实现将修改好的新模型写入数据库
2020/03/31 Python
python3 logging日志封装实例
2020/04/08 Python
Python连接Hadoop数据中遇到的各种坑(汇总)
2020/04/14 Python
Python新手学习raise用法
2020/06/03 Python
python FTP编程基础入门
2021/02/27 Python
HTML5图片预览实例分享
2014/06/04 HTML / CSS
澳大利亚一站式数码相机商店:CameraPro
2020/03/09 全球购物
乌克兰数字设备、配件和智能技术的连锁商店:KTC
2020/08/18 全球购物
篮球比赛口号
2014/06/10 职场文书
科技活动周标语
2014/10/08 职场文书
2015年七一建党节活动总结
2015/03/20 职场文书
公司联欢会主持词
2015/07/04 职场文书
python保存图片的四个常用方法
2022/02/28 Python
Android Studio实现简易进制转换计算器
2022/05/20 Java/Android