• Node.js的基本使用(三)数据库与身份认证


    数据库

    数据库基本概念

    数据库是用来组织存储管理数据的仓库,用户可以对数据库中的数据进行增删改查等操作。

    常见的数据库及分类

    • MySQL数据库:目前使用最广泛,流行度最高
    • Oracle数据库
    • SQL Server数据库
    • Mongodb数据库

    MySQL,Oracle,SQL Server属于传统型数据库(关系型数据库),mongodb属于新型数据库(非关系型数据库或NoSQL数据库)

    传统型数据库的数据组织结构

    在传统型数据库中,数据的组织结构为数据库,数据表,数据行,字段这四个部分组成

    在项目中操作数据库的步骤

    1. 安装操作MySQL数据库的第三方模块
    2. 通过mysql模块连接到MySQL数据库
    3. 通过mysql模块执行SQL语句

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-unfCs7Qp-1659433888464)(https://raw.githubusercontent.com/Koiiiilin/MyImage/main/202207221159546.png)]

    安装mysql模块

    mysql模块是托管于npm上的第三方模块,执行npm install mysql

    配置mysql模块

    在使用mysql模块操作MySQL数据库之前,必须先对mysql模块进行必要的配置。

    // 导入mysql模块
    const mysql = require("mysql")
    
    // 建立与mysql数据库的连接关系
    const db = mysql.createPool({
        host:'127.0.0.1',
        user:'root',
        password:'root',
        database:'mydb'
    
    })
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    测试一下,mysql是否连接成功,

    db.query('select 1',(err,results)=>{
        if(err){
            return console.log(err.message);
        }
        console.log(results)
    })
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    image-20220722135442317

    使用mysql模块操作MySQL数据库

    1.查询数据
    db.query('select * from sp_type',(err,results)=>{
        if(err){
            return console.log(err.message);
        }
        console.log(results)
    })
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    执行select语句查询的结果是数组。

    image-20220722140130179

    2.插入数据
    // 插入数据
    const obj = {
        type_name:'饮水机',
        delete_time:null
    }
    db.query("insert into sp_type (type_name) values (?,?)",[obj.type_name,obj.delete_time],(err,results)=>{
        if(err) return console.log(err.message);
        if(results.affectedRows === 1){
            console.log("插入数据成功");
        }
    })
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    image-20220722142756068

    插入数据的便捷方式

    向表中新增数据时,如果数据对象的每个属性和数据表的字段一一对应,则可以通过如下方式快速插入数据。

    db.query("insert into sp_type set ?",obj,(err,res)=>{
        if(err) return console.log(err.message);
        console.log(res);
    })
    
    • 1
    • 2
    • 3
    • 4
    3.更新数据
    var today = date.format(new Date(),'YYYY-MM-DD');  
    db.query("update sp_type set type_name=? ,delete_time = ? where type_id = 3",['电脑',today],(err,result)=>{
        if(err) return console.log(err.message);
        if(result.affectedRows === 1){
            console.log("更新数据成功!");
        }
    })
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    更新数据的便捷方式

    与插入数据类似,在更新表格数据时,如果数据对象的每个属性和数据表的字段一一对应,可以使用更加简便的方式

    var type = {
        type_name :'电脑',
        delete_time:today,
        type_id:9
    }
    db.query("update sp_type set ? where type_id = ?",[type,type.type_id],(err,result)=>{
        if(err) return console.log(err.message);
        if(result.affectedRows === 1){
            console.log("更新数据成功!");
        }
    })
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    4、删除数据

    注:如果SQL语句中有多个占位符,则必须使用数组为每个占位符指定具体的值,若只有一个则可以省略数组

    db.query("delete from sp_type where type_id = ?", 8, (err, result) => {
        if (err) return console.log(err.message);
        if (result.affectedRows === 1) {
            console.log("删除数据成功!");
        }
    })
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    标记删除

    使用delete语句,会真实的把数据从表中删除,操作存在一定的风险。为了保险起见,推荐使用标记删除的行是,来模拟删除的动作。通过设置类似于isDelete的状态字段来标记当前数据是否被删除。当需要进行删除的时候,实际上执行的修改isDelete的update操作。

    db.query("update sp_user set isDelete = ? where user_id = ?", [1,1], (err, result) => {
        if (err) return console.log(err.message);
        if (result.affectedRows === 1) {
            console.log("删除数据成功!");
        }
    })
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    WEB开发模式

    1、服务端渲染的Web开发模式

    服务器发送给客户端的HTML页面,是在服务器通过字符串的拼接,动态生成的,因此,客户端不需要使用Ajax这样的技术额外请求页面的数据。

    优点

    • 前端耗时少,浏览器只需要渲染即可
    • 有利于SEO,爬虫更容易获取信息

    缺点

    • 占用服务器端资源,如果请求较多,会对服务器造成一定的访问压力
    • 不利于前后端分离,开发效率低,无法进行分工合作
    2、前后端分离的web开发模式

    前后端分离的概念:前后端分离的开发模式依赖于ajax,后端只负责提供API接口,前端使用Ajax调用接口的开发模式。

    优点

    • 开发体验好,前端专注于UI页面的开发,后端专注于api的开发,且前端有更多的选择性
    • 用户体验好,可以轻松实现页面的局部刷新
    • 减轻了服务器端的渲染压力,因为页面最终是在每个用户的浏览器中生成

    缺点

    • 不利于SEO,爬虫无法爬取页面的有效信息

    身份认证

    不同开发模式下的身份认证

    对于服务器端渲染和前后端分离这两种开发模式来说,分别有着不同的身份认证方案:

    • 服务端渲染推荐使用Session认证机制
    • 前后端分离推荐使用JWT认证机制

    Session认证机制

    Http协议的无状态性

    客户端每次HTTP请求都是独立的,连续多个请求之间没有直接关系,服务器不主动保留每次HTTP请求的状态。

    什么是Cookie

    Cookie是存储在用户浏览器中的一段不超过4KB的字符串,由一个名称,一个值和其他几个用于控制Cookie有效期、安全性、适用范围的可选属性组成。

    不同域名下的Cookie各自独立,每当客户端发起请求时,会自动把当前域名下所有未过期的Cookie发送到服务器。

    几大特性

    • 自动发送
    • 域名独立
    • 过期时限
    • 4KB限制
    Cookie在身份认证中的作用

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-phnLQIMM-1659433888468)(https://raw.githubusercontent.com/Koiiiilin/MyImage/main/202208021058834.png)]

    Cookie不具有安全性

    由于Cookie是存储在浏览器中的,而且浏览器也提供了读写Cookie的API,因此Cookie很容易被伪造,不具有安全性,因此不建议服务器将重要的隐私数据,通过Cookie 的形式发送给浏览器。

    在Express中使用Session认证
    1. 安装express-session中间件

    在Express项目中,只需要安装express-session中间件,即可在项目中使用Session认证

    npm install express-session
    
    • 1
    1. 配置express-session 中间件

    express-session中间件安装成功后,需要通过app.use()来注册session中间件

    var session = require('express-session')
    
    append.use(session({
        secret:'key',//任意字符
        resave:false,//固定写法
        saveUninitialized:true//固定写法
    }))
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    1. 向session中存数据

    当express-session中间件配置成功后,即可通过req.session来访问和使用seesion对象

    1. 清空session

    调用req.session.destroy()函数,即可清空服务器保存的session信息,只会清空当前用户对应的session

    JWT认证机制

    session认证的局限性

    Session认证机制需要配合Cookie才能实现,由于Cookie默认不支持跨域访问,所以当涉及到前端跨域请求后端接口的时候,需要做很多额外的配置,才能实现跨域Session认证。

    image-20220802140302314

    JWT的组成部分

    JWT通常由三部分组成,分别是Header,Payload,Signature三者之间使用英文‘.’分隔.

    Header.Payload.Signature

    • Payload部分才是真正的用户信息,是经过加密后生成的字符串
    • Header和Signatures是安全性相关的部分,只是为了保证Token的安全

    image-20220802140922841

    JWT的使用方法

    客户端收到服务器返回的JWT后通常将它存储在localStorage或者sessionStorage中。推荐的做法是把JWT放在HTTP请求头的Authorization字段中,AUthorization:Bearer

    在Express中使用JWT
    1. 安装JWT相关的包
    npm install jsonwebtoken express-jwt
    
    • 1
    • jsonwebtoken用于生成JWT字符串
    • express-jwt用于将JWT 字符串解析还原成json对象
    const jwt = require('jsonwebtoken')
    const expressJWT = require('express-jwt')
    
    • 1
    • 2
    1. 定义secret密钥

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

    • 当生成JWT字符串的时候,需要使用secret密钥对用户的信息进行加密,最终得到加密好的JWT字符串
    • 当把JWT字符串解析还原成JSON对象时,需要使用secret密钥进行解密
    // 定义secret密钥,本质就是一个字符串
    const secretKey = 'abcdefg ^_^'
    
    • 1
    • 2
    在登录成功后生成JWT字符串

    调用jsonwebtoken包提供的sign()方法,将用户信息加密成JWT字符串,响应给客户端

      // 在登录成功之后,调用 jwt.sign() 方法生成 JWT 字符串。并通过 token 属性发送给客户端
      // 参数1:用户的信息对象
      // 参数2:加密的秘钥
      // 参数3:配置对象,可以配置当前 token 的有效期
      // 记住:千万不要把密码加密到 token 字符中
      const tokenStr = jwt.sign({ username: userinfo.username }, secretKey, { expiresIn: '30s' })
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    将JWT字符串还原为JSON对象

    客户端每次在访问那些有权限接口的时候,都需要主动通过请求头中的Authorization字段将Token字符串发送到服务器进行身份认证

    app.use(expressJWT({ secret: secretKey }).unless({ path: [/^\/api\//] }))
    //高版本算法发生改变
    app.use(expressJWT({secret:secretKey,algorithms:['HS256']}).unless({path:[/^\/api\//]}))
    
    • 1
    • 2
    • 3

    nodejs中post请求方式,req.body接值为空如何解决

    https://blog.csdn.net/ccf19881030/article/details/109121566

    使用req.user获取用户信息

    当express-jwt中间件配置成功之后,即可在有权限的接口中使用req.user()对象,来访问JWT字符串中解析出来的用户信息

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-FBChy0J5-1659433888469)(https://raw.githubusercontent.com/Koiiiilin/MyImage/main/202208021749797.png)]

    捕获解析JWT失败后产生的错误

    当使用express-jwt解析Token字符串时,如果客户端发送过来的Token字符串过期或者不合法,会产生一个解析失败的错误,影响项目的正常运行.

    app.use((err, req, res, next) => {
      // 这次错误是由 token 解析失败导致的
      if (err.name === 'UnauthorizedError') {
        return res.send({
          status: 401,
          message: '无效的token',
        })
      }
      res.send({
        status: 500,
        message: '未知的错误',
      })
    })
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
  • 相关阅读:
    瑞芯微RK3568|SDK开发之环境安装及编译操作
    python+requests接口自动化测试
    【数据结构】——链表面试题详解
    Linux下Jenkins服务器安装与使用
    软件测试技术之单元测试—工程师 Style 的测试方法
    大数据运维实战第二十五课 HDFS 存储权限 ACL 控制策略以及与系统权限整合应用
    秋招求职经验分享
    2022年最新辽宁建筑八大员(电气施工员)模拟题库及答案
    Unity Shader - 踩坑 - BRP 管线中的 depth texture 的精度问题(暂无解决方案,推荐换 URP)
    10款功能强大的网络嗅探工具应用分析
  • 原文地址:https://blog.csdn.net/qq_46258819/article/details/126126072