Node.js(六)-數據庫與身份認證

一 、學習目標

◆ 能夠知道如何配置MySQL數據庫環境
◆ 能夠認識并使用常見的 SQL語操作數據庫
◆ 能夠在Express中操作MySQL數據庫
◆ 能夠了解 Session的實現原理
◆ 能夠了解JWT的實現原理

二、數據庫的基本概念

1.1?什么是數據庫

數據庫(database)是用來組織存儲管理數據的倉庫。為了方便管理互聯網世界中的數據,就有了數據庫管理系統的概念(簡稱:數據庫)。用戶可以對數據庫中的數據進行新增查詢更新刪除等操作。?

?1.2 常見的數據庫及分類

?市面上的數據庫有很多種,最常見的數據庫有如下幾個: ?

MySQL 數據庫(目前使用最廣泛、流行度最高的開源免費數據庫;Community + Enterprise ) ?

● Oracle 數據庫(收費) ?

● SQL Server 數據庫(收費) ?

● Mongodb 數據庫(Community + Enterprise)

其中,MySQL、Oracle、SQL Server 屬于傳統型數據庫(又叫做:關系型數據庫SQL 數據庫),這三者的設計理念相同,用法比較類似。?

而 Mongodb 屬于新型數據庫(又叫做:非關系型數據庫NoSQL 數據庫),它在一定程度上彌補了傳統型數據庫的缺陷。?

1.3 傳統型數據庫的數據組織結構

數據的組織結構:指的就是數據以什么樣的結構進行存儲。?

傳統型數據庫的數據組織結構,與 Excel 中數據的組織結構比較類似。 因此,我們可以對比著 Excel 來了解和學習傳統型數據庫的數據組織結構。

1.3.1 Excel的數據組織結構

每個 Excel 中,數據的組織結構分別為工作簿工作表數據行這 4 大部分組成。

?整個 Excel 叫做工作簿

?users 和 books 是工作表

?users 工作表中有 3 行數據

?每行數據由 6 列信息組成

?每列信息都有對應的數據類型

1.3.2??傳統型數據庫的數據組織結構?

傳統型數據庫中,數據的組織結構分為數據庫(database)數據表(table)數據行(row)字段(field)這 4 大部分組成。

?數據庫類似于 Excel 的工作簿 ?

?數據表類似于 Excel 的工作表 ?

?數據行類似于 Excel 的每一行數據 ?

?字段類似于 Excel 的列 ?

?每個字段有對應的數據類型

1.3.3?實際開發中庫、表、行、字段的關系?

  • 在實際項目開發中,一般情況下,每個項目都對應獨立的數據庫。
  • 不同的數據,要存儲到數據庫的不同表中,例如:用戶數據存儲到 users 表中,圖書數據存儲到 books 表中。
  • 每個表中具體存儲哪些信息,由字段來決定,例如:我們可以為 users 表設計 id、username、password 這 3 個字段。
  • 表中的行,代表每一條具體的數據。

三、安裝并配置MySQL

3.1?了解需要安裝哪些MySQL相關的軟件

?????????對于開發人員來說,只需要安裝 MySQL ServerMySQL Workbench 這兩個軟件,就能滿足開發的需要了。

MySQL Server:專門用來提供數據存儲和服務的軟件。

MySQL Workbench:可視化的 MySQL 管理工具,通過它,可以方便的操作存儲在 MySQL Server 中的數據。

3.2??MySQL 在 Mac 環境下的安裝

?在 Mac 環境下安裝 MySQL 的過程比 Windows 環境下的步驟簡單很多:

  • 先運行 mysql-8.0.19-macos10.15-x86_64.dmg 這個安裝包,將 MySQL Server 安裝到 Mac 系統
  • 再運行 mysql-workbench-community-8.0.19-macos-x86_64.dmg 這個安裝包,將可視化的 MySQL Workbench 工具安裝到 Mac 系統

3.3?MySQL 在 Windows 環境下的安裝?

?在 Windows 環境下安裝 MySQL,只需要運行 mysql-installer-community-8.0.19.0.msi 這個安裝包,就能一次性將 MySQL Server ?和 MySQL Workbench 安裝到自己的電腦上。

具體的安裝教程:mysql安裝教程-CSDN博客

四、MySQL的基本使用

4.1 使用 MySQL Workbench 管理數據庫

4.1.1?連接數據庫?

?4.1.2??了解主界面的組成部分

4.1.3?創建數據庫?

4.1.4?創建數據表?

DataType 數據類型:

  1. ?int 整數 ?
  2. varchar(len) 字符串
  3. ?tinyint(1) 布爾值

字段的特殊標識:

  1. PK(Primary Key)主鍵、唯一標識 ?
  2. NN(Not Null)值不允許為空 ?
  3. UQ(Unique)值唯一 ?
  4. AI(Auto Increment)值自動增長

?4.1.5?向表中寫入數據

?4.2 使用 SQL 管理數據庫

4.2.1 什么是 SQL

????????SQL(英文全稱:Structured Query Language)是結構化查詢語言,專門用來訪問和處理數據庫的編程語言。能夠讓我們以編程的形式,操作數據庫里面的數據

三個關鍵點

? SQL 是一門數據庫編程語言

? 使用 SQL 語言編寫出來的代碼,叫做 SQL 語句

? SQL 語言只能在關系型數據庫中使用(例如 MySQL、Oracle、SQL Server)。非關系型數據庫(例如 Mongodb)不支持 SQL 語言

4.3.2?SQL 的學習目標?

重點掌握如何使用 SQL 從數據表中:

查詢數據select) 、插入數據insert into) 、更新數據update) 、刪除數據delete

額外需要掌握的 4 種 SQL 語法:

where 條件、and 和 or 運算符、order by 排序、count(*) 函數

4.3?SQL 的 SELECT 語句?

4.3.1?語法

SELECT 語句用于從表中查詢數據。執行的結果被存儲在一個結果表中(稱為結果集)。語法格式如下:

-- 這是注釋
-- 從 FROM 指定的[表中],查詢出[所有的]數據。*表示[所有列]
SELECT * FROM 表名稱
-- 從 FROM 指定的[表中],查詢出指定 列名稱 (字段)的數據。
SELECT 列名稱 FROM 表名稱

?注意:SQL 語句中的關鍵字大小寫不敏感。SELECT 等效于 select,FROM 等效于 from。

4.3.2?SELECT * 示例?

我們希望從 users 表中選取所有的列,可以使用符號 * 取代列的名稱,示例如下:?

4.3.3?SELECT 列名稱 示例?

如需獲取名為 "username" 和 "password" 的列的內容(從名為 "users" 的數據庫表),請使用下面的 SELECT 語句:

4.4 SQL 的?INSERT INTO?語句?

4.4.1?語法

INSERT INTO 語句用于向數據表中插入新的數據行,語法格式如下:?

-- 語法解讀:向指定的表中,插入如下幾列數據,列的值通過 values 一一指定
-- 注意: 列和值要一一對應,多個列和多個值之間,使用英文的逗號分隔
INSERT INTO tablename (列1,列2,...) VALUES (值1,值2,...)

4.4.2?示例?

-- 向 users 表中插入新數據 username:tony stark    password:098123
INSERT INTO users (username,password) VALUES ('tony stark','098123')

?

4.5?SQL 的 UPDATE 語句

4.5.1?語法?

--語法解讀:
--1.用 UPDATE 指定要更新哪個表中的數據
--2.用 SET 指定列對應的新值
--3.用 WHERE 指定更新的條件
UPDATE 表名稱 SET 列名稱=新值 WHERE 列名稱=某值

4.5.2?UPDATE 示例 - 更新某一行中的一個列?

?把 users 表中 id 7 的用戶密碼,更新為 888888。示例如下:

UPDATE users SET password='888888' WHERE id=7

4.5.3?UPDATE 示例 - 更新某一行中的若干列

-- 把 users 表中 id 為 2 的用戶密碼和用戶狀態,分別更新為 123admin 和 1
UPDATE users SET password='123admin',status=1 WHERE id = 3 

4.6?SQL 的 DELETE 語句?

4.6.1?語法?

-- 語法解讀:
-- 從指定的表中,根據 WHERE 條件,刪除對應的數據行
DELETE FROM 表名稱 WHERE 列名稱=值

?4.6.2?示例代碼

-- 從 users 表中,刪除 id 為 4 的用戶
DELETE FROM users WHERE id=7

4.7 SQL的 WHERE子句?

4.7.1?語法

????????WHERE語法子句用于限定選擇的標準。在SELECTUPDATEDELETE語句中,皆可使用WHERE子句進行語法限定。

-- 查詢語句中的 WHERE 條件
SELECT 列名稱 FROM 表名稱 WHERE 列名稱 運算符 值-- 更新語句中的 WHERE 條件
UPDATE 表名稱 SET 列=新值 WHERE 列名稱 運算符 值-- 刪除語句中的 WHERE 條件
DELETE FROM 表名稱 WHERE 列名稱 運算符 值

4.7.2?可以在WHERE子句中使用的運算符

下面的運算符可在 WHERE 子句中使用,用來限定選擇的標準:?

?注意!!!:在某些版本的 SQL 中,操作符 <> 可以寫為 !=

4.7.3 WHERE?子句示例

可以通過 WHERE?子句來限定 SELECT?查詢條件?

-- 查詢 status = 1的所有用戶
SELECT * FROM users WHERE status = 1-- 查詢 id>2 的所有用戶
SELECT * FROM users WHERE id>2-- 查詢 username 不為 admin 的所有用戶信息
SELECT * FROM users WHERE username<>'admin'

4.8 SOL?的 AND?或者 OR?運算?

?4.8.1?語法

AND 和 OR 可在 WHERE 子語句中把兩個或多個條件結合起來。

AND 表示必須同時滿足多個條件,相當于 JavaScript 中的 && 運算符,例如:

???????? if (a !== 10 && a !== 20)

OR 表示只要滿足任意一個條件即可,相當于 JavaScript 中的 || 運算符,例如:

???????? if(a !== 10 || a !== 20)

4.8.2 AND?運算符示例

-- 查詢 status為0 并且 id 小于3的
SELECT * FROM users WHERE status = 0 AND id < 3

4.8.3 OR?運算符?示例

-- 使用 OR 來顯示所有 status 為 1,或者 username 為 zs 的用戶
SELECT * FROM users WHERE status = 1 OR username = 'zs'

4.9 SQL?的 ORDER BY?子句

4.9.1?語法?

ORDER BY 語句用于根據指定的列對結果集進行排序

ORDER BY 語句默認按照升序對記錄進行排序。

如果希望按照降序對記錄進行排序,可以使用 DESC 關鍵字

?4.9.2?ORDER BY 子句 - 升序排序

對 users 表中的數據,按照 status 字段進行升序排序,示例如下:

-- 對 users 表中的數據對status 字段進行升序排序
SELECT * FROM users ORDER BY status ASC

等價于??

SELECT * FROM users ORDER BY status

4.9.3 ORDER BY 子句-降序排序

-- 對 users 表中的數據,按照 id 字段進行降序排序SELECT * FROM users ORDER BY ID DESC

4.9.4?ORDER BY 子句 – 多重排序?

-- 對 users 表中的數據,先按照 status 字段進行降序排序,再按照 username 的字母順序,進行升序排序
SELECT * FROM users ORDER BY status DESC ,username ASC

4.10 SQL?的COUNT(*)?函數

COUNT(*)?函數用于返回查詢結果的總數據條數

?4.10.1?語法

SELECT COUNT(*) FROM users

?4.10.2 COUNT(*)?示例

-- 查詢 users 表中 status 為 0 的總數據條數
SELECT COUNT(*) FROM users WHERE STATUS = 0

4.10.3?使用 AS 為列設置別名?

-- 將列名稱從 COUNT(*) 修改 total 
SELECT COUNT(*) AS total FROM users WHERE status = 0

五、在express中操作MySQL

5.1?在項目中操作數據庫的步驟

?① 安裝操作 MySQL 數據庫的第三方模塊(mysql)

?② 通過 mysql 模塊連接到 MySQL 數據庫

?③ 通過 mysql 模塊執行 SQL 語句

?5.2?安裝與配置 mysql?模塊

5.2.1?安裝 mysql 模塊

????????mysql 模塊是托管于 npm 上的第三方模塊。它提供了在 Node.js 項目中連接操作 MySQL 數據庫的能力。 想要在項目中使用它,需要先運行如下命令,將 mysql 安裝為項目的依賴包:

npm install mysql

?5.2.2?配置 mysql 模塊

?????????在使用 mysql 模塊操作 MySQL 數據庫之前,必須先對 mysql 模塊進行必要的配置,主要的配置步驟如下:

// 1.導入 mysql 模塊
const mysql = require('mysql');
// 2.建立與MySQL數據庫的連接
const db = mysql.createPool({host:'127.0.0.1', // 數據庫的 IP 地址user:'root', // 登錄數據庫的賬號password:'admin123', // 登錄數據庫的密碼database:'my_db_01' // 指定要操作那個數據庫
})

5.2.3?測試?mysql?模塊是否能正常工作

?調用?db.query()?函數,指定要執行的 SQL?語句,通過回調函數拿到執行的結果:(中間換了個別人的數據庫,我的有問題)

// 1.導入 mysql 模塊
// 安裝mysql后會報錯:Client does not support authentication protocol requested by server;  consider upgrading
// 因為 mysql版本8.0以上更換了新的密碼驗證方式, node客戶端不支持新的驗證方式
const mysql = require('mysql2');
// 2.建立與MySQL數據庫的連接
const db = mysql.createPool({host:'xx.xx.xxx.xxx', // 數據庫的 IP 地址user:'xxxx', // 登錄數據庫的賬號password:'xxxx', // 登錄數據庫的密碼database:'node_test' // 指定要操作那個數據庫
})db.query('select 1',(err,results)=>{if(err) return console.log('err:'+err.message);console.log(results);
})

?測試數據庫連接是否正常:出現以下信息表示正常

5.3?使用 mysql?模塊操作MySQL?數據庫

?5.3.1?查詢數據

后面只寫sql部分代碼,配置不再重復展示。

const sqlStr = 'select * from users'db.query(sqlStr,(err,result)=>{// 查詢失敗if(err) return console.log('err:'+err.message);// 查詢成功console.log(result);
})

5.3.2?插入數據?

// 向 users 表中插入數據,其中 usename 為 Spider-Man,password的值為 pcc123
const users = {username:'Spider-Man',password:'pcc123'}
// 定義待執行的 SQL語句,其中英文的 ? 表示占位符
const sqlStr = 'insert into users (username,password) values (?,?)'
// 執行 SQL 語句 使用數組的形式,依次為 ? 占位符指定具體的值
db.query(sqlStr,[users.username,users.password],(err,result)=>{// 查詢失敗if(err) return console.log('err:'+err.message);// 查詢成功console.log(result.affectedRows); // 輸出:1
})

5.3.3?插入數據的便捷方式?

向表中新增數據時,如果數據對象的每個屬性數據表的字段一一對應,則可以通過如下方式快速插入數據:?

// 向 users 表中插入數據,其中 usename 為 Spider-Man,password的值為 pcc123
const user = {username:'Spider-Man2',password:'pcc123'}
// 定義待執行的 SQL語句,其中英文的 ? 表示占位符
const sqlStr = 'insert into users set ?'
// 執行 SQL 語句 直接將數據當做占位符的值
db.query(sqlStr,user,(err,result)=>{if(err) return console.log('err:'+err.message);// 可以通過 affectedRows 屬性,來判斷是否插入數據成功if(result.affectedRows===1) console.log('插入數據成功!'); // 輸出:插入數據成功!
})

?5.3.4?更新數據

const user = {id:5,username:'aaaaa',password:'666666'}
// 定義 SQL 語句
const sqlStr = 'update users set username=?,password=? where id=?'
// 執行 SQL 語句
db.query(sqlStr,[user.username,user.password,user.id],(err,result)=>{if(err) return console.log(err.message);// 失敗// 注意:執行了 update 語句之后,執行的結果,也是一個對象,可以通過 affectedRows 判斷是否更新成功if(result.affectedRows===1) console.log('更新數據成功!');// 成功
})

5.3.5?更新數據的便捷方式?

// 要更新的數據對象
const user = {id:15,username:'Tony Jia',password:'666666'}
// 定義 SQL 語句
const sqlStr = 'update users set ? where id=?'
// 執行 SQL 語句
db.query(sqlStr,[user,user.id],(err,result)=>{if(err) return console.log(err.message);// 失敗// // 注意:執行了 update 語句之后,執行的結果,也是一個對象,可以通過 affectedRows 判斷是否更新成功if(result.affectedRows===1) console.log('更新數據成功!');// 成功
})

?

5.3.6?刪除數據?

在刪除數據時,推薦根據 id 這樣的唯一標識,來刪除對應的數據。?

// 要執行的 SQL 語句
const sqlStr = 'delete from users where id = ?'
// 調用db.query()執行 SQL 語句的同時,為占位符指定具體的值
// 注意:如果 SQL 語句中有多個占位符,則必須使用數組為每個占位符指定具體的值5 
// 如果 SOL 語句中只有一個占位符,則可以省略數組
db.query(sqlStr, 4, (err, result) => {if (err) return console.log(err.message);if (result.affectedRows === 1) console.log('刪除數據成功!');
})

?5.3.7?標記刪除

????????使用 DELETE 語句,會把真正的把數據從表中刪除掉。為了保險起見,推薦使用標記刪除的形式,來模擬刪除的動作。

????????所謂的標記刪除,就是在表中設置類似于 status 這樣的狀態字段,來標記當前這條數據是否被刪除。

????????當用戶執行了刪除的動作時,我們并沒有執行 DELETE 語句把數據刪除掉,而是執行了 UPDATE 語句,將這條數據對應的 status 字段標記為刪除即可。

// 要執行的SQL語句
const sqlStr = 'update users set status=1 where id=?'
// 標記刪除:使用 UPDATE 語句替代 DELETE 語句;只更新數據的狀態,并沒有真正刪除
db.query(sqlStr,15,(err,result)=>{if(err) return console.log(err.message);if(result.affectedRows===1) console.log('標記刪除成功!');
})

六、前后端的身份認證

?6.1 Web 開發模式

目前主流的 Web 開發模式有兩種,分別是:

基于服務端渲染的傳統 Web 開發模式

基于前后端分離的新型 Web 開發模式?

6.1.1?服務端渲染的 Web 開發模式

服務端渲染的概念:服務器發送給客戶端的 HTML 頁面,是在服務器通過字符串的拼接,動態生成的。因此,客戶端不需要使用 Ajax 這樣的技術額外請求頁面的數據。代碼示例如下:

?6.1.2?服務器端渲染的優缺點

?優點: ?

?前端耗時少。因為服務器端負責動態生成 HTML 內容,瀏覽器只需要直接渲染頁面即可。尤其是移動端,更省電。 ?

?有利于SEO。因為服務器端響應的是完整的 HTML 頁面內容,所以爬蟲更容易爬取獲得信息,更有利于 SEO。

缺點: ?

占用服務器端資源。即服務器端完成 HTML 頁面內容的拼接,如果請求較多,會對服務器造成一定的訪問壓力。

不利于前后端分離,開發效率低。使用服務器端渲染,則無法進行分工合作,尤其對于前端復雜度高的項目,不利于項目高效開發。

?6.1.3?前后端分離的 Web?開發模式

????????前后端分離的概念:前后端分離的開發模式,依賴于 Ajax 技術的廣泛應用。簡而言之,前后端分離的 Web 開發模式,就是后端只負責提供 API 接口,前端使用 Ajax 調用接口的開發模式

?6.1.4?前后端分離的優缺點

?優點:

?開發體驗好。前端專注于 UI 頁面的開發,后端專注于api 的開發,且前端有更多的選擇性。 ?

用戶體驗好。Ajax 技術的廣泛應用,極大的提高了用戶的體驗,可以輕松實現頁面的局部刷新。 ?

減輕了服務器端的渲染壓力。因為頁面最終是在每個用戶的瀏覽器中生成的。

缺點:

?① 不利于 SEO。因為完整的 HTML 頁面需要在客戶端動態拼接完成,所以爬蟲對無法爬取頁面的有效信息。(解決方案:利用 Vue、React 等前端框架的 SSR (server side render)技術能夠很好的解決 SEO 問題!)

6.1.5?如何選擇Web開發模式

不談業務場景而盲目選擇使用何種開發模式都是耍流氓。

  • 比如企業級網站,主要功能是展示而沒有復雜的交互,并且需要良好的 SEO,則這時我們就需要使用服務器端渲染;
  • 而類似后臺管理項目,交互性比較強,不需要考慮 SEO,那么就可以使用前后端分離的開發模式。

另外,具體使用何種開發模式并不是絕對的,為了同時兼顧首頁的渲染速度前后端分離的開發效率,一些網站采用了首屏服務器端渲染 + 其他頁面前后端分離的開發模式。?

6.2?身份認證

6.2.1?什么是身份認證?

????????身份認證(Authentication)又稱“身份驗證”、“鑒權”,是指通過一定的手段,完成對用戶身份的確認。

  • 日常生活中的身份認證隨處可見,例如:高鐵的驗票乘車,手機的密碼或指紋解鎖,支付寶或微信的支付密碼等。
  • 在 Web 開發中,也涉及到用戶身份的認證,例如:各大網站的手機驗證碼登錄、郵箱密碼登錄、二維碼登錄等。

?6.2.2?為什么需要身份認證

????????身份認證的目的,是為了確認當前所聲稱為某種身份的用戶,確實是所聲稱的用戶。例如,你去找快遞員取快遞,你要怎么證明這份快遞是你的。

????????在互聯網項目開發中,如何對用戶的身份進行認證,是一個值得深入探討的問題。例如,如何才能保證網站不會錯誤的將“馬云的存款數額”顯示到“馬化騰的賬戶”上。

6.2.3?不同開發模式下的身份認證?

????????對于服務端渲染前后端分離這兩種開發模式來說,分別有著不同的身份認證方案:

  • 服務端渲染推薦使用 Session 認證機制
  • 前后端分離推薦使用 JWT 認證機制

6.3 Session?認證機制

6.3.1 HTTP?協議的無狀態性

?了解 HTTP 協議的無狀態性是進一步學習 Session 認證機制的必要前提。

HTTP 協議的無狀態性,指的是客戶端的每次 HTTP 請求都是獨立的,連續多個請求之間沒有直接的關系,服務器不會主動保留每次 HTTP 請求的狀態

?6.3.2?如何突破 HTTP 無狀態的限制

????????對于超市來說,為了方便收銀員在進行結算時給 VIP 用戶打折,超市可以為每個 VIP 用戶發放會員卡。

?注意:現實生活中的會員卡身份認證方式在 Web 開發中的專業術語叫做 Cookie

6.3.3?什么是 Cookie

????????Cookie 是存儲在用戶瀏覽器中的一段不超過 4 KB 的字符串。它由一個名稱(Name)、一個(Value)和其它幾個用于控制 Cookie 有效期、安全性、使用范圍的可選屬性組成。

????????不同域名下的 Cookie 各自獨立,每當客戶端發起請求時,會自動把當前域名下所有未過期的 Cookie 一同發送到服務器。

Cookie的幾大特性:?

  1. 自動發送
  2. 域名獨立
  3. 過期時限
  4. 4KB 限制

6.3.4?Cookie 在身份認證中的作用?

????????客戶端第一次請求服務器的時候,服務器通過響應頭的形式,向客戶端發送一個身份認證的 Cookie,客戶端會自動將 Cookie 保存在瀏覽器中。?

????????隨后,當客戶端瀏覽器每次請求服務器的時候,瀏覽器會自動將身份認證相關的 Cookie,通過請求頭的形式發送給服務器,服務器即可驗明客戶端的身份。

?6.3.5 Cookie?不具有安全性

????????由于 Cookie 是存儲在瀏覽器中的,而且瀏覽器也提供了讀寫 Cookie 的 API,因此 Cookie 很容易被偽造,不具有安全性。因此不建議服務器將重要的隱私數據,通過 Cookie 的形式發送給瀏覽器。

注意:千萬不要使用 Cookie 存儲重要且隱私的數據!比如用戶的身份信息、密碼等。

6.3.6?提高身份認證的安全性

????????為了防止客戶偽造會員卡,收銀員在拿到客戶出示的會員卡之后,可以在收銀機上進行刷卡認證。只有收銀機確認存在的會員卡,才能被正常使用。

?這種“會員卡 + 刷卡認證”的設計理念,就是 Session 認證機制的精髓。

6.3.7 Session 的工作原理

?

?6.4 在 Express 中使用 Session 認證

?6.4.1?安裝?express-session 中間件

????????在 Express 項目中,只需要安裝 express-session 中間件,即可在項目中使用 Session 認證:

npm install express-session

?6.4.2?配置? session?中間件

????????express-session 中間件安裝成功后,需要通過 app.use()注冊 session 中間件,示例代碼如下:?

const session = require("express-session")
app.use(session({secret:'cat',save:false, // 固定寫法saveUninitialized:true,// 固定寫法
}))

6.4.3?向 session 中存數據

當 express-session 中間件配置成功后,即可通過 req.session 來訪問和使用 session 對象,從而存儲用戶的關鍵信息:?

// 登錄的 API 接口
app.post('/api/login', (req, res) => {// 判斷用戶提交的登錄信息是否正確if (req.body.username !== 'admin' || req.body.password !== '000000') {return res.send({ status: 1, msg: '登錄失敗' })}// TODO_02:請將登錄成功后的用戶信息,保存到 Session 中//注意:只有成功配置了 express-session 這個中間件之后,才能夠通過 req 點出來 session 這個屬性req.session.user = req.body; // 將用戶信息存儲到session中req.session.isLogin = true; // 將用戶登錄信息存儲到session中res.send({ status: 0, msg: '登錄成功' })
})

6.4.4 向?session?中取數據

可以直接從 req.session 對象上獲取之前存儲的數據,示例代碼如下:

// 獲取用戶姓名的接口
app.get('/api/username', (req, res) => {// TODO_03:請從 Session 中獲取用戶的名稱,響應給客戶端if (!req.session.islogin) {return res.send({ status: 1, msg: 'fail' })}res.send({ status: 0, msg: 'success', username: req.session.user.username })
})

6.4.5?清空 session

?調用 req.session.destroy() 函數,即可清空服務器保存的 session 信息。

// 退出登錄的接口
app.post('/api/logout', (req, res) => {// TODO_04:清空 Session 信息// 清空當前客戶端對應的 session 信息req.session.destroy()res.send({status:0,msg:'退出登錄成功!'})
})

6.5?JWT 認證機制

6.5.1?了解 Session認證的局限性

Session 認證機制需要配合 Cookie 才能實現。由于 Cookie 默認不支持跨域訪問,所以,當涉及到前端跨域請求后端接口的時候,需要做很多額外的配置,才能實現跨域 Session 認證。

注意:

  • 當前端請求后端接口不存在跨域問題的時候,推薦使用 Session 身份認證機制。
  • 當前端需要跨域請求后端接口的時候,不推薦使用 Session 身份認證機制,推薦使用 JWT 認證機制。?

6.5.2?什么是 JWT

?JWT(英文全稱:JSON Web Token)是目前最流行跨域認證解決方案

6.5.3? JWT?工作原理

?總結:用戶的信息通過 Token 字符串的形式,保存在客戶端瀏覽器中。服務器通過還原 Token 字符串的形式來認證用戶的身份。

?6.5.4 JWT?的組成部分

JWT 通常由三部分組成,分別是 Header(頭部)、Payload(有效荷載)、Signature(簽名)。 三者之間使用英文的“.”分隔,格式如下:

Header.Payload.Signature

?JWT?字符串示例:

?6.5.5 JWT?的三個部分各自代表的含義

?JWT 的三個組成部分,從前到后分別是 Header、Payload、Signature。

  • Payload 部分才是真正的用戶信息,它是用戶信息經過加密之后生成的字符串。 ?
  • Header 和 Signature 是安全性相關的部分,只是為了保證 Token 的安全性。

?

6.5.6 JWT?的使用方式

????????客戶端收到服務器返回的 JWT 之后,通常會將它儲存在 localStorage sessionStorage 中。

????????此后,客戶端每次與服務器通信,都要帶上這個 JWT 的字符串,從而進行身份認證。推薦的做法是把 JWT 放在 HTTP 請求頭的 Authorization 字段中,格式如下:

Authorization:Bear <token>

6.6?在?express?中使用 JWT

6.6.1?安裝 JWT?相關的包

運行如下命令,安裝如下兩個 JWT 相關的包:

npm install jsonwebtoken express-jwt
  • jsonwebtoken 用于生成 JWT 字符串
  • express-jwt 用于將 JWT 字符串解析還原成 JSON 對象

?6.6.2?導入 JWT?相關包

?使用 require() 函數,分別導入 JWT 相關的兩個包:

// 導入用于生成 JWT 字符串的包
const jsonwebtoken = require('jsonwebtoken')
// 導入用于將 JWT字符串 解析還原成 JSON 對象的包
const pxpressJWT =  require('express-jwt')

6.6.3?定義 secret 密鑰

????????為了保證 JWT 字符串的安全性,防止 JWT 字符串在網絡傳輸過程中被別人破解,我們需要專門定義一個用于加密解密的 secret 密鑰:

  • 當生成 JWT 字符串的時候,需要使用 secret 密鑰對用戶的信息進行加密,最終得到加密好的 JWT 字符串
  • 當把 JWT 字符串解析還原成 JSON 對象的時候,需要使用 secret 密鑰進行解密?
// secret key 本質上是一個字符串
const secretKey = 'good good study'

6.6.4?在登錄成功后生成 JWT 字符串

// 登錄接口
app.post('/api/login', function (req, res) {// 將 req.body 請求體中的數據,轉存為 userinfo 常量const userinfo = req.body// 登錄失敗if (userinfo.username !== 'admin' || userinfo.password !== '000000') {return res.send({status: 400,message: '登錄失敗!'})}// 登錄成功// TODO_03:在登錄成功之后,調用 jwt.sign() 方法生成 JWT 字符串。并通過 token 屬性發送給客戶端// 調用 jsonwebtoken.sign()生成 JT 字符串,三個參數分別是:用戶信息對象、加密密鑰、配置對象res.send({status: 200,message: '登錄成功!',token: jsonwebtoken.sign({username:userinfo.username},secretKey,{expiresIn:'30s'})// 要發送給客戶端的 token 字符串})
})

6.6.5?將 JWT 字符串還原為 JSON 對象

客戶端每次在訪問那些有權限接口的時候,都需要主動通過請求頭中的 Authorization 字段,將 Token 字符串發送到服務器進行身份認證。

此時,服務器可以通過 express-jwt 這個中間件,自動將客戶端發送過來的 Token 解析還原成 JSON 對象:?

// 使用 app.use()來注冊中間件
// expressJWT({ secret:secretKey })就是用來解析 Token 的中間件
// .unless({ path:[/^\/api\//] })用來指定哪些接口不需要訪問權限!
app.use(pxpressJWT({secret:secretKey}).unless({path:[/^\/api\//]}))
// 登錄接口

6.6.6?使用 req.user?獲取用戶信息

當 express-jwt 這個中間件配置成功之后,即可在那些有權限的接口中,使用 req.user 對象,來訪問從 JWT 字符串中解析出來的用戶信息了,示例代碼如下:?

//注意:只要配置成功了 express-jwt 這個中間件,就可以把解析出來的用戶信息,掛載到 req.user 屬性上
app.get('/admin/getinfo', function (req, res) {// TODO_05:使用 req.user 獲取用戶信息,并使用 data 屬性將用戶信息發送給客戶端console.log(req.user);res.send({status: 200,message: '獲取用戶信息成功!',data: req.user // 要發送給客戶端的用戶信息})
})

?啟動服務時報錯,原因是Express版本和express-jwt的版本不兼容

解決:

?postman:登錄成功并且拿到了返回的token

?調用獲取用戶信息接口:返回了解析之后的用戶名

?6.6.7?捕獲解析 JWT 失敗后產生的錯誤

????????當使用 express-jwt 解析 Token 字符串時,如果客戶端發送過來的 Token 字符串過期不合法,會產生一個解析失敗的錯誤,影響項目的正常運行。我們可以通過 Express 的錯誤中間件,捕獲這個錯誤并進行相關的處理,中間件代碼如下:

// TODO_06:使用全局錯誤處理中間件,捕獲解析 JWT 失敗后產生的錯誤
app.use((err, req, res, next) => {// token 導致的解析錯誤if (err.name == 'UnauthorizedError') {res.send({status: 401,message: '無效的Token'})}// 其它原因導致的錯誤res.send({status: 500,message: '未知錯誤'})
})

token過期后發送請求服務端返回的如圖:

加入捕獲解析token異常的全局中間件后 :

?

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/717328.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/717328.shtml
英文地址,請注明出處:http://en.pswp.cn/news/717328.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

邊緣計算網關的重要作用-天拓四方

隨著物聯網技術的迅猛發展&#xff0c;數據量的爆炸式增長對數據處理和分析提出了更高的要求。邊緣計算網關作為連接物理世界和數字世界的橋梁&#xff0c;正逐漸受到各行業的重視。本文將從行業背景、功能特點以及帶來的效益等方面&#xff0c;探討邊緣計算網關在當前及未來的…

備戰藍橋杯---狀態壓縮DP基礎2之TSP問題

先來一個題銜接一下&#xff1a; 與上一題的思路差不多&#xff0c;不過這里有幾點需要注意&#xff1a; 1.因為某一列的狀態還與上上一行有關&#xff0c;因此我們令f[i][j][k]表示第i行狀態為j,第i-1行狀態為k的最大炮兵數。 因此&#xff0c;我們可以得到狀態轉移方程&…

2024/03/01

控制機械臂 #include<myhead.h> #define SER_IP "192.168.126.2" #define SER_PORT 8888#define CLI_IP "192.168.252.165" #define CLI_PORT 9999int main(int argc, const char *argv[]) {int cfdsocket(AF_INET,SOCK_STREAM,0);if(cfd-1){perror…

成功解決git clone遇到的error: RPC failed; curl 16 Error in the HTTP2 framing layer fatal: expected flush af

成功解決git clone遇到的error: RPC failed; curl 16 Error in the HTTP2 framing layer fatal: expected flush af 問題描述解決方案 問題描述 用git的時候可能會遇到這個問題&#xff1a; (base) zhouzikang7443-8x4090-120:~/project$ git clone https://github.com/123/12…

Windows服務器:通過nginx反向代理配置HTTPS、安裝SSL證書

先看下效果&#xff1a; 原來的是 http&#xff0c;配置好后 https 也能用了&#xff0c;并且顯示為安全鏈接。 首先需要 SSL證書 。 SSL 證書是跟域名綁定的&#xff0c;還有有效期。 windows 下雙擊可以查看相關信息。 下載的證書是分 Apache、IIS、Tomcat 和 Nginx 的。 我…

【leetcode】圓圈中最后剩下的數字

目錄 1. 問題 2. 思路 3. 代碼 4. 運行 1. 問題 本題即為典型的約瑟夫問題&#xff0c;通過遞推公式倒推出問題的解。原始問題是從n個人中每隔m個數踢出一個人&#xff0c;原始問題變成從n-1個人中每隔m個數踢出一個人…… 示例 1&#xff1a; 輸入: n 5, m 3 輸出: 3…

Unity TMP文字移動效果

前言 看見很多游戲有很特殊的波浪形文字效果&#xff0c;于是來嘗試一下控制TMP文字頂點的方式達到類似效果。 原理 掛載tmp text&#xff0c;在里面隨便放入非空格字符。 tmp text組件開放了textInfo接口&#xff0c;也就是GetComponent<TextMeshProUGUI>().textInfo…

兩天學會微服務網關Gateway-Gateway簡介

鋒哥原創的微服務網關Gateway視頻教程&#xff1a; Gateway微服務網關視頻教程&#xff08;無廢話版&#xff09;_嗶哩嗶哩_bilibiliGateway微服務網關視頻教程&#xff08;無廢話版&#xff09;共計17條視頻&#xff0c;包括&#xff1a;1_Gateway簡介、2_Gateway工作原理、3…

使用.NET開發VSTO工具快速將PPT導出為圖片

本文主要介紹如何使用.NET開發 PowerPoint VSTO 外接程序&#xff0c;并實現快速的將當前頁PPT導出為圖片的功能。可以幫助你了解如何使用 VSTO 開發 Office 外接程序&#xff0c;以及如何操作 PowerPoint 的對象模型。 1. 背景 在日常的文章寫作中&#xff0c;我經常使用 PPT…

Vue 3 中的 watchEffect 和 watch 有什么區別?

Vue 3 中的 watchEffect 和 watch 有什么區別&#xff1f; Vue 3 引入了 Composition API&#xff0c;為開發者提供了更加靈活和組織化的方式來組合和復用代碼邏輯。在響應式系統中&#xff0c;watch 和 watchEffect 是兩個重要的函數&#xff0c;用于觀察和響應 Vue 組件中狀…

JUC并發編程 深入學習Java并發編程【上】

JUC并發編程&#xff0c;深入學習Java并發編程&#xff0c;與視頻每一P對應&#xff0c;全系列6w字。 P1-5 為什么學特色預備知識 進程線程概念 進程&#xff1a; 一個程序被運行&#xff0c;從磁盤加載這個程序的代碼到內存&#xff0c;就開起了一個進程。 進程可以視為程…

JVM相關問題

JVM相關問題 一、Java繼承時父子類的初始化順序是怎樣的&#xff1f;二、JVM類加載的雙親委派模型&#xff1f;三、JDK為什么要設計雙親委派模型&#xff0c;有什么好處&#xff1f;四、可以打破JVM雙親委派模型嗎&#xff1f;如何打破JVM雙親委派模型&#xff1f;五、什么是內…

Spring Cloud Gateway-系統保護Sentinel集成

文章目錄 Sentinel介紹Spring Cloud Gateway集成Sentinelpom依賴Sentinel配置Sentinel集成Nacos作為數據源自定義降級響應 Sentinel介紹 ? 隨著微服務的流行&#xff0c;服務和服務之間的穩定性變得越來越重要。Sentinel 是面向分布式、多語言異構化服務架構的流量治理組件&a…

HTML5:七天學會基礎動畫網頁6

CSS3自定義字體 ①&#xff1a;首先需要下載所需字體 ②&#xff1a;把下載字體文件放入 font文件夾里&#xff0c;建議font文件夾與 css 和 image文件夾平級 ③&#xff1a;引入字體&#xff0c;可直接在html文件里用font-face引入字體&#xff0c;分別是字體名字和路徑 例…

Django官網項目

項目準備 使用VSCODE做IDE。 檢查Python版本。 sudo apt install sudo apt update python3 --version創建項目路徑&#xff0c;創建虛擬環境&#xff0c;創建項目 路徑 \mysite 進入路徑&#xff0c;運行VSCODE 運行 "code ." 創建虛擬環境。 選擇 >python: c…

【推薦算法系列十七】:GBDT+LR 排序算法

排序算法經典中的經典 參考 推薦系統之GBDTLR 極客時間 手把手帶你搭建推薦系統 課程 邏輯回歸&#xff08;LR&#xff09;模型 邏輯回歸&#xff08;LR,Logistic Regression&#xff09;是一種傳統機器學習分類模型&#xff0c;也是一種比較重要的非線性回歸模型&#xff…

AAAI2024-分享若干篇有代碼的優秀論文-圖神經網絡、時間序列預測、知識圖譜、大模型等

圖神經網絡、大模型優化方向系列文章目錄 為了方便大家根據自己的興趣查看自己的研究方向論文&#xff0c;在這里進行了細分。如果有對其中的論文感興趣的&#xff0c;可以查看對應的文章在論文相應的代碼&#xff0c;方便快速上手學習&#xff0c;也可以借助這些代碼的學習快…

16 Educational Codeforces Round 142 (Rated for Div. 2)C. Min Max Sort(遞歸、思維、dp)

C. Min Max Sort 很不錯的一道題目&#xff0c;不過腦電波和出題人每對上&#xff0c; q w q 。 qwq。 qwq。 正難則反。 我們考慮最后一步是怎么操作的。 最后一步一定是對 1 1 1和 n n n進行操作 那么上一步呢&#xff1f; 上一步應該是對 2 2 2和 n ? 1 n-1 n?1 以此類推…

AMD“高級洞察”系列揭示Epyc Naples和Rome原型CPU早期無法啟動問題

AMD在其新的YouTube視頻系列《高級洞察》第一集中&#xff0c;由AMD首席技術官Mark Papermaster擔任主持人&#xff0c;討論了AMD在數據中心領域的突破性進展及其持續增長。然而&#xff0c;AMD在服務器業務的發展并非一帆風順&#xff0c;兩位高管公開討論了早期Epyc Naples和…

【Python】環境管理怎么選擇【virtualenv】【pipenv】【 poetry】【 conda】

前言 剛入門Python&#xff0c;看到PyCharm的環境管理選擇有好幾個選擇&#xff0c;分別是virtualenv、pipenv、venv、conda&#xff0c;只知道這些都可以用來管理Python環境的&#xff0c;但不知道這些環境有什么區別&#xff0c;所以&#xff0c;本文將對這些環境管理進行總…