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 相关文章推荐
Js+Dhtml:WEB程序员简易开发工具包(预先体验版)
Nov 07 Javascript
通过js动态操作table(新增,删除相关列信息)
May 23 Javascript
JS打开图片另存为对话框实现代码
Dec 26 Javascript
javascript ie6兼容position:fixed实现思路
Apr 01 Javascript
javascript中基本类型和引用类型的区别分析
May 12 Javascript
JavaScript Array对象详解
Mar 01 Javascript
js与jQuery实现获取table中的数据并拼成json字符串操作示例
Jul 12 jQuery
解决VUE中document.body.scrollTop为0的问题
Sep 15 Javascript
d3绘制基本的柱形图的实现代码
Dec 12 Javascript
Vue-CLI 3.X 部署项目至生产服务器的方法
Mar 22 Javascript
Vue基于vuex、axios拦截器实现loading效果及axios的安装配置
Apr 26 Javascript
NUXT SSR初级入门笔记(小结)
Dec 16 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的宝库目录--PEAR
2006/10/09 PHP
windows xp下安装pear
2006/12/02 PHP
php 取得瑞年与平年的天数的代码
2009/08/10 PHP
php中使用url传递数组的方法
2015/02/11 PHP
Laravel 5.4向IoC容器中添加自定义类的方法示例
2017/08/15 PHP
PHP批斗大会之缺失的异常详解
2019/07/09 PHP
javascript与asp.net(c#)互相调用方法
2009/12/13 Javascript
jQuery 改变CSS样式基础代码
2010/02/11 Javascript
来自qq的javascript面试题
2010/07/24 Javascript
JS性能优化笔记搜索整理
2013/08/21 Javascript
javascript浏览器兼容教程之事件处理
2014/06/09 Javascript
javascript ajax的5种状态介绍
2014/08/18 Javascript
Javascript中arguments对象详解
2014/10/22 Javascript
js+html5获取用户地理位置信息并在Google地图上显示的方法
2015/06/05 Javascript
JS中如何比较两个Json对象是否相等实例代码
2016/07/13 Javascript
Javascript中常用的检测方法小结
2016/10/08 Javascript
微信小程序 wx.uploadFile无法上传解决办法
2016/12/14 Javascript
jQuery实现表格奇偶行显示不同背景色 就这么简单
2017/03/13 Javascript
javascript简单链式调用案例分析
2017/05/10 Javascript
ExtJs的Ext.Ajax.request实现waitMsg等待提示效果
2017/06/14 Javascript
koa上传excel文件并解析的实现方法
2018/08/09 Javascript
快速解决vue-cli在ie9+中无效的问题
2018/09/04 Javascript
[57:59]EG vs Secret 2018国际邀请赛淘汰赛BO3 第一场 8.22
2018/08/23 DOTA
Python中使用Beautiful Soup库的超详细教程
2015/04/30 Python
Python使用ftplib实现简易FTP客户端的方法
2015/06/03 Python
python numpy元素的区间查找方法
2018/11/14 Python
Python通过fnmatch模块实现文件名匹配
2020/09/30 Python
JD Sports法国:英国篮球和运动时尚的领导者
2017/09/28 全球购物
上海某公司.net方向笔试题
2014/09/14 面试题
Ruby如何实现动态方法调用
2012/11/18 面试题
英文自我鉴定
2013/12/10 职场文书
学子宴答谢词
2014/01/25 职场文书
文体活动总结范文
2014/05/05 职场文书
施工安全协议书
2016/03/22 职场文书
python tqdm用法及实例详解
2021/06/16 Python
云服务器部署 Web 项目的实现步骤
2022/06/28 Servers