十五、Express 中使用JWT进行登录验证

news2024/11/24 10:01:07


 cookie 篇  :  Node.js 中 cookie的验证登录      |        session 篇 :  Node.js 中 session验证登录


         在前面讲过了两种验证登录的方式,其一是cookie,其二是session;那么在讲JWT之前先来简单的回顾这两种方式区别;cookie和session的区别在于cookie是存储在客户端的而session是存在于服务器端的,cookie的安全性比session的安全性低;那么在这里要多提关于session的内容,我们知道session的工作原理(如下):

        session需要配合cookie才能实现,同时cookie是不支持跨域访问(具有独立性),所以当涉及到前后端跨域请求后端接口时,难免需要做其他的一些额外配置,才能实现跨域的session认证;当然如果后端接口不存在跨域问题,那么就推荐使用session的这种认证机制,如果没有的话又觉得配置太麻烦可以使用接下来要讲的JWT( JSON Web Token).

JWT (JSON Web Token)

        JWT是目前最流行的跨域认证解决方案,那么先来了解一下它的工作原理(如图):

        将用户的信息进行验证,验证成功后会将用户信息进行加密,生成Token之后服务器将它响应给到客户端,客户端拿到这个Token之后将它保存起来,可保存在LocalStorage或SessionStorage中,那么在下次客户端再次发起请求之后,会再将未过期的Token发给服务器去还原验证,如果验证成功服务器会将请求相应的用户信息内容返回给客户端这样一个过程。

JWT组成

        JWT是由3个部分组成的,分别为 Header,Payload,Signature ;

 Express 中安装引入JWT

        先来创建express框架的项目环境:

安装导入 JWT 相关的包

 命令如下:

npm install jsonwebtoken express-jwt

jsonwebtoken包是用于生成JWT字符串;express-jwt包是将jwt字符串进行解析还原为JSON对象;

引入如下:

const jwt = require('jsonwebtoken');
const expressJWT = require('express-jwt');

定义密钥

         为了保证JWT字符串的安全性,防止JWT字符串在网络传输过程中被人破解,就需要定义一个用于加密和解密的secret密钥;

  • 当生成JWT字符串的时候,需要使用secret密钥对用户信息进行加密,最终得到加密好的JWT字符串;
  • 当把JWT字符串解析还原成JSON对象的时候,需要使用secret密钥进行解密;
  • secret密钥的本质就是一个字符串(越复杂越好);

        那么这里密钥的设置方式可以有很多种,可以是通过一个文件存放,然后通过fs模板读取,使用openSSL生成私钥然后通过私钥生成公钥等,那么在接下来的案例当中会使用一种比较简单的尽可能的在本篇当中能让你会基本的使用和了解到这个jwt,下面就来开始案例:

Express框架中jwt验证登录

        这里将之前cookie和session中使用过的案例进行改造:首先进入首页会检测是否在本地存储了登录的token,如果则会提示去登录,进入登录页面,输入错误的信息会有弹窗提示,输入正确的信息后会提交到服务器去验证,验证成功之后会生成一个token返回去给客户端,通过客户端来查看一下token信息,以及token失效后会提示然后返回到登录处去;

         那如果还未清楚,下面就一同来通过代码来进一步了解:现在 /public/index.html 编写以下该文件;

index.html 首页编写

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>CSDN</title>
    <script src="https://cdn.staticfile.org/jquery/1.10.2/jquery.min.js"></script>
    <script src="javascripts/checklogin.js"></script>
    <link rel="stylesheet" href="stylesheets/index.css"/>
</head>
<body>
    <header>
        <h3>首页</h3>
    </header>
    <div class="content">
        <p>正在维修中...</p>         
    </div>
</body>
</html>

login.html 登陆页编写

        同样在 /public/login.html 下编写文件;

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>登录</title>
    <link rel="stylesheet" href="stylesheets/login.css"/>
</head>
<body>
    <div class="content">
        <div class="head">
            <img src="../images/CSDN.png" alt="" srcset="">
        </div>
        <form action="">
            <div class="form-input">
                <div class="form-data">
                    <span>账户:</span>
                </div>
                <input type="text" class="input-text" name="username" value=""/>
            </div>
            <div class="form-input">
                <div class="form-data"> 
                    <span>密码:</span>
                </div>
                <input type="password" class="input-text" name="password" value=""/>
            </div>
        </form>
            <input type="submit" class="form-submit" value="登录" id="btuLogin" />
    </div>
</body>
<script src="https://cdn.staticfile.org/jquery/1.10.2/jquery.min.js"></script>
<script src="javascripts/login.js"></script>
</html>

        编写完成以上两个文件之后,在前面讲过可以通过app.js中来通过app.use(express.static(path.json(__dirname,'public'))) 配置来托管静态资源信息;那么在浏览器来访问时候的路径不需要添加public,否则访问不到;

模拟登录用户数据json

        在 /public/user.json 下编写该文件:

// user.json文件
[
    {
        "id":"1001",
        "username":"YAN",
        "password":"yan"
    },
    {
        "id":"1002",
        "username":"SEN",
        "password":"sen"
    },
    {
        "id":"1003",
        "username":"LIN",
        "password":"lin"
    }
]

        接下来的登录会通过接收的数据与读取该json数据进行遍历比对来验证是否存在该用户;

路由配置

        首先就是客户端将登录的信息提交到后端验证,可以通过POST请求方式,后端通过req.body获取对应的信息,紧接着使用fs文件系统模块来读取user.json文件的进而遍历提交的信息是否在user.json中存在,最后返回状态码,在存在当中还需要生成一个token来发送给客户端,让客户端保存;

var express = require('express');
var router = express.Router();
const fs = require('fs');
const {resolve} = require('path');
var jwt = require('jsonwebtoken');
var {expressjwt : expressJWT} = require('express-jwt');

var secret = 'The author is lhxz';

router.use(expressJWT({secret,algorithms:["HS256"]}).unless({path:['/login']}));

// 配置中间件
router.use(function(err,req,res,next){
  // token解析失败
  if(err.name === 'UnauthorizedError'){
    return res.send({code:1,msg:' 登录过期 | 无效token '})
  }
  res.send({status:500,message:'未知的错误'});
  next();
})

router.get('/checklogin',function(req,res,next){
  console.log(req.auth);
  res.send({
    code:0,
    msg:'ok',
    data:req.auth
  })
})

/* GET home page. */
router.get('/', function(req, res, next) {
  res.render('index', { title: 'Express' });
});

router.post('/login',function(req,res,next){
  // 获取前台信息;
  let username = req.body.username,password = req.body.password;

  // 读取
  let result = fs.readFileSync(resolve(__dirname,'../public/user.json'));
  let results = JSON.parse(result);

  // 遍历
  for(var i = 0;i<results.length;i++){
    if(username == results[i].username && password == results[i].password){
        // 验证成功
        res.send({
          code:0,
          msg:'ok',
          // 返回token
          token:jwt.sign({username:results[i].username,password:results[i].password},secret,{expiresIn:'120s'})
        })
    }
  }
  if(i==results.length){
    res.send({
      code:1,
      msg:'error'
    })
  }
})

module.exports = router;

 [ 以上就是采用对称加密,就是生成和解析的都是同一把钥匙(密钥);var secret = 'The author is lhxz' 就是密钥,通过jwt.sign()方法就可以生成token发送给客户端,客户端每次在访问有权限的接口(例如在主页时你需要有token才能访问)的时候,都需要主动通过请求头中的Authorization字段,将token发送去到服务器去验证,在上面已经提及到了这个express-jwt中间件,服务器可以通过它来自动将客户端发过来的Token解析为JSON对象,以上代码就是通过配置的:router.use(expressJWT({secret,algorithms:["HS256"]}).unless({path:['/login']})),这样一来就会很方便,当然在最后面的演示当中会来演示通过官网验证;

登录获取token

         在登录页面当中填写信息,然后点击登录,提交信息到服务器去验证;在/javascripts/login.js 编写该文件:

// login.js文件
$('#btuLogin').click(function(){
    $.ajax({
        type:'post',
        url:'/login',
        data:$('form').serialize()  // username=xx&password=xx
    }).then(function(res){
        if(res.msg == 'ok'){
            // 跳转
            localStorage.setItem('login_Token',res.token);
            location.href = '/index.html'
        }else{
            alert("账户或密码错误,请重新输入");
            // 清空
            $('input[name=username]').val('');
            $('input[name=password]').val('');
            // 获取焦点
            $('input[name=username]').focus();
        }
    })
})

检测是否有token

        进入页面之后通过在LocalStorage中去判断是否有login_Token,有的话就去验证一下,验证成功的话则会有 "欢迎回来!" 的提示,如果没有token的存在则需要提示它未登录需要先去进行登录然后让服务器发送token给客户端保存;

// 判断是否有token
let tk = localStorage.getItem('login_Token');
if(tk!= null){
    // 有token去验证
    let rs = localStorage.getItem('login_Token');
    $.ajax({
        type:'GET',
        url:'/checklogin',
        headers:{
            "Authorization":"Bearer "+rs
        }
    }).then(function(res){
       if(res.msg == 'ok'){
            alert('欢迎回来!');
       }else{
            alert(res.msg);
            location.href = "/login.html"
       } 
    })

}else{
    // 没有跳转登录
    alert("未登录,请先登录...");
    location.href = '/login.html';
}

Express - JWT 测试演示

1. 启动项目 —— 跑起来

2. 浏览器中输入 http://127.0.0.1:3000/index.html

        - 无token —— 登录失败

        - 跳转 login.html 登录页面 —— 输入错误信息登录提示

         - 输入正确信息之后是否正常跳转,且是否保存了token?

token的验证是怎样的呢?可以来带jwt的官网测试一下,将刚刚拿到的这串token测试一下:

JWT官网 选择 【Debugger】: JSON Web Tokens - jwt.io

        将那串jwt.sign()生成的token复制到这里位置: 

         复制如下:可以看到下面的签名无效,那是缺少了什么?

         确实的就是解析的密钥了,代码中采用的是对称加密,也就是生成和解析用到都是同一套密钥,可以在代码中看到密钥了,var secret = 'The author is lhxz' ;输入之后可以看到

        这里当然不是说你可以在官网上就能破译出来,而是需要密钥;也即是当别人拿到你的密钥之后就能轻松破译,所以保管好你的密钥,如果觉得不安全的话还可以通过非对称加密,由于内容有点多就不再多说了,后续有机会的话再来提及;

3. 通过 /checklogin 中来查看是否有已经解析的JSON返回:

 4. 捕获解析失败

        当使用express-jwt解析token字符串的时候,如客户端发送的token是无效的或者不合法的,那么会导致程序的错误影响程序的运行,那么可以通过Express错误中间件来捕获这些错误;

        以下通过捕获之后提醒用户token无效,然后重新跳转回到登录界面;

         以上内容就是本篇目的一个内容了,内容与先前讲过的一些案例进行了一个结合和使用JWT进行验证登录,案例并不涉及比较难的内容,比较适中如果感兴趣的朋友可以看看,那么在这里感谢大家的支持!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/158139.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

成功解决:npm 版本不支持node.js。【 npm v9.1.2 does not support Node.js v16.6.0.】

文章目录1、出现的问题2、查看自己的npm和node版本3、解决方法3.1 寻找对应的版本3.2 升级npm版本4、再次运行项目&#xff0c;成功5、如果上述方法失败&#xff0c;请看这里早起更新代码后&#xff0c;跑前端项目。结果噶了、跑不起来了&#xff1b;不慌&#xff0c;看看日志报…

JiBX 的实操

JiBX 的实操介绍基本概念BECL 介绍JiBX 基础概念开发jar 依赖BECLJiBXPOJO 实体类OrderAddressCustomerShippingOrderFactory 工厂类使用ant 来生成XML和POJO对象的绑定关系idea 使用antant 脚本 build.xmlant 添加 build.xmlbinding.xml报错问题解决测试TestOrder测试结果 如图…

C/C++ STL

常见容器性质总结 1.vector 底层数据结构为数组 &#xff0c;支持快速随机访问 2.list 底层数据结构为双向链表&#xff0c;支持快速增删 3.deque 底层数据结构为一个中央控制器和多个缓冲区&#xff0c;详细见STL源码剖析P146&#xff0c;支持首尾&#xff08;中间 不能&…

java mybatis的SpringBoot博客论坛管理系统

java mybatis的SpringBoot博客论坛管理系统 博主介绍&#xff1a;5年java开发经验&#xff0c;专注Java开发、定制、远程、文档编写指导等,csdn特邀作者、专注于Java技术领域 作者主页 超级帅帅吴 Java毕设项目精品实战案例《500套》 欢迎点赞 收藏 ⭐留言 文末获取源码联系方式…

Java数据结构:堆与PriorityQueue优先级队列的使用

文章目录1 什么是堆2 堆的实现思路2.1 大根堆的成员变量简介2.2 树的相关知识复习2.3 向下调整创建大根堆2.4 堆的插入2.5 堆的删除3 大根堆实现代码及测试4 PriorityQueue的使用4.1 特性简介4.2 常用方法4.3 使用PriorityQueue实现大根堆写在最后1 什么是堆 堆实质上就是对完全…

python对称加密AES的使用

python对称加密AES的使用 aes安装 pip install pycryptodome加密库引用 from Crypto.Util.Padding import pad, unpad from Crypto.Cipher import AES import base64完整代码 from Crypto.Util.Padding import pad, unpad from Crypto.Cipher import AES import base64def A…

带你玩转OpenHarmony AI-基于海思NNIE的AI能力自定义

简介相信大家从玩转OpenAtom OpenHarmony&#xff08;简称“OpenHarmony”&#xff09;AI系列专题的其他文章中&#xff0c;已经拓展了OpenHarmony AI在智慧出行、智慧办公等多场景下的AI自动检测、识别和判断的新体验&#xff0c;对于OpenHarmony平台上的AI开发有了一定认识。…

通过事件总线EventBus/AsyncEventBus进行JAVA模块解耦 (史上最全)

事件总线在 进行JAVA模块解耦 &#xff0c;价值巨大 实际开发中&#xff0c;常常 通过事件总线EventBus/AsyncEventBus进行JAVA模块解耦 &#xff0c; 比如&#xff0c;在顶级开源组件 hotkey的源码中&#xff0c; 就多次用到 EventBus/AsyncEventBus进行JAVA模块解耦 所以&am…

71、AdaNeRF: Adaptive Sampling for Real-time Rendering of Neural Radiance Fields

简介 官网&#xff1a;https://thomasneff.github.io/adanerf/ 新的双网络架构&#xff0c;它采用正交方向&#xff0c;通过学习如何最好地减少所需样本点的数量&#xff0c;将网络分为联合训练的 sample 和 shading 网络&#xff0c;训练方案在每条射线上采用固定的样本位置…

Nginx 高级篇

文章目录Nginx 高级篇一、 负载均衡1、 负载均衡概述2、 处理方式2.1 用户手动选择2.2 DNS 轮询2.3 四 / 七层负载均衡3、 七层负载均衡3.1 七层负载均衡指令3.1.1 upstream3.1.2 server3.2 实现流程3.3 负载均衡的状态3.3.1 down3.3.2 backup3.3.3 max_conns3.3.4 max_fails &…

Docker前世今生

文章目录Docker背景Docker历史docker 理念Docker能做什么虚拟机的缺点容器虚拟化技术Docker学习途径Docker背景 一款产品从开发到上线&#xff0c;从操作系统&#xff0c;到运行环境&#xff0c;再到应用配置。作为开发运维之间的协作我们需要 关心很多东西&#xff0c;这也是…

ChatGPTAPI Key申请教程

ChatGPTAPI Key申请教程 一、API Key申请使用 在浏览器打开网址&#xff1a;https://openai.com/api/ 等待网页加载完成后&#xff0c;点击右上角 LOG IN 进入登录界面 进入登录界面后&#xff0c;依次输入注册的邮箱–Continue–输入密码–Continue&#xff0c;完成登录&…

< CSS小技巧:filter滤镜妙用>

文章目录&#x1f449; 前言&#x1f449; 简述&#x1f449; 基本语法及案例》语法简述》案例&#x1f449; 拓展1. drop-shadow 更加智能的阴影效果2. 网页置灰3. 元素强调、高亮4.节省空间&#xff0c;提高网页加载速度&#x1f449; 具体案例网页参考文献往期内容 &#x1…

如何有效进行团队建设:从关注事到关注人

咱打工人都想趁着年终总结这个契机&#xff0c;拿着工作数据跟领导们提涨薪&#xff01;但是入行没多久的社畜们却没有这个底气&#xff0c;虽累但没结果&#xff08;暗指身兼数职的项目经理小白们&#xff09;&#xff0c;主要是觉得自己的工作成绩不够优秀。这几天办公室的项…

Model-Agnostic Meta-Learning for Fast Adaptation of Deep Networks

摘要 我们为元学习提出了一个算法是模型无关model−agnosticmodel-agnosticmodel−agnostic. 在某种意义上&#xff0c;其与用梯度下降训练的模型是兼容的&#xff0c;可以应用在大量不同的学习问题上。包括&#xff1a;分类、回归、和加强学习。 元学习的目标是正在学习任务…

TAZ生成实践(Intel芯片Mac Python 3.7.9)

参考文章 https://blog.csdn.net/weixin_42632778/article/details/115164518 TAZ生成 https://zhuanlan.zhihu.com/p/343576683 使用ArcGIS实现线转栅格 https://pro.arcgis.com/zh-cn/pro-app/latest/tool-reference/conversion/polyline-to-raster.htm ArcGIS Pro 折线转栅格…

第②篇 Spring IoC——容器

Spring最成功的是其提出的理念&#xff0c;而不是技术本身。 概念 Spring所依赖的两个核心理念&#xff1a; 一个是控制反转&#xff08;IoC&#xff09;。另一个是面向切面编程&#xff08;Aspect Oriented Programming&#xff0c;AOP&#xff09;。 IoC是Spring的核心&am…

JS入门到精通详解(1)

JavaScript概述(需要记)什么是javascript?是一门&#xff08;基于对象&#xff09;和&#xff08;事件驱动&#xff09;的&#xff08;脚本语言&#xff09;。js诞生于哪一年&#xff1f;哪个公司&#xff1f;谁&#xff1f;第一个名字叫什么&#xff1f;1995年 网景 布兰登 l…

【Python】type、isinstance、issubclass详解

type type方法有两种重载形式&#xff1a; type(o: object)&#xff1b;type(name: str, bases:Tuple[type, ...], dict:Mapping[str: Any], **kwds) 使用第一种重载形式的时候&#xff0c;传入一个【object】类型&#xff0c;返回一个【type】对象&#xff0c;通常与object…

解决使用element-plus时使用el-select-v2组件时,选中后无法移除focus的状态的方法。

我们可以使用element-ui-plus的el-select-v2的组件&#xff0c;实现复合搜索和下拉框的功能。 使用如下模块&#xff1a; <template><el-select-v2 v-model"value" filterable :options"options" placeholder"Please select"visibleCha…