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 相关文章推荐
短信提示使用 特效
Jan 19 Javascript
利用javascript/jquery对上传文件格式过滤的方法
Jul 25 Javascript
使用js+jquery实现无限极联动
May 23 Javascript
Extjs根据条件设置表格某行背景色示例
Jul 23 Javascript
javascript中eval解析JSON字符串
Feb 27 Javascript
基于JS实现密码框(password)中显示文字提示功能代码
May 27 Javascript
canvas知识总结
Jan 25 Javascript
angular实现IM聊天图片发送实例
May 08 Javascript
AngularJS ng-repeat指令及Ajax的应用实例分析
Jul 06 Javascript
OkHttp踩坑随笔为何 response.body().string() 只能调用一次
Jan 08 Javascript
Vue2.5学习笔记之如何在项目中使用和配置Vue
Sep 26 Javascript
webpack中如何使用雪碧图的示例代码
Nov 11 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 加密解密内部算法
2010/04/22 PHP
php disk_free_space 返回目录可用空间
2010/05/10 PHP
PHP中substr_count()函数获取子字符串出现次数的方法
2016/01/07 PHP
PHP识别二维码的方法(php-zbarcode安装与使用)
2016/07/07 PHP
经典的解除许多网站无法复制文字的绝招
2006/12/31 Javascript
jQuery动态创建html元素的常用方法汇总
2014/09/05 Javascript
浅谈javascript回调函数
2014/12/07 Javascript
JS实现至少包含字母、大小写数字、字符的密码等级的两种方法
2015/02/03 Javascript
jquery滚动到顶部底部代码
2015/04/20 Javascript
javascript如何写热点图
2015/12/08 Javascript
第二章之Bootstrap 页面排版样式
2016/04/25 Javascript
js剪切板应用clipboardData实例解析
2016/05/29 Javascript
总结十个Angular.js由浅入深的面试问题
2016/08/26 Javascript
关于javascript事件响应的基础语法总结(必看篇)
2016/12/26 Javascript
js正则表达式验证密码强度【推荐】
2017/03/03 Javascript
详解nodejs操作mongodb数据库封装DB类
2017/04/10 NodeJs
jQuery自定义元素右键点击事件(实现案例)
2017/04/28 jQuery
在vue中获取dom元素内容的方法
2017/07/10 Javascript
微信小程序数据统计和错误统计的实现方法
2019/06/26 Javascript
Vue.js获取手机系统型号、版本、浏览器类型的示例代码
2020/05/10 Javascript
Vue之封装公用变量以及实现方式
2020/07/31 Javascript
VUE 项目在IE11白屏报错 SCRIPT1002: 语法错误的解决
2020/09/27 Javascript
python实现淘宝购物系统
2019/10/25 Python
Python列表操作方法详解
2020/02/09 Python
python 监控服务器是否有人远程登录(详细思路+代码)
2020/12/18 Python
css 如何让背景图片拉伸填充避免重复显示
2013/07/11 HTML / CSS
汽车技术服务英文求职信范文
2014/01/02 职场文书
高二物理教学反思
2014/02/08 职场文书
财务整改报告范文
2014/11/05 职场文书
2015新年联欢晚会开场白
2014/12/14 职场文书
银行求职信范文怎么写
2015/03/20 职场文书
企业爱心捐款倡议书
2015/04/27 职场文书
《玩出了名堂》教学反思
2016/02/17 职场文书
如何开发一个渐进式Web应用程序PWA
2021/05/10 Javascript
利用Python+OpenCV三步去除水印
2021/05/28 Python
Python道路车道线检测的实现
2021/06/27 Python