2023-1-16 前端達人
目前主流的 Web 開發模式有兩種,分別是:
服務端渲染的概念:服務器發送給客戶端的 HTML 頁面,是在服務器通過字符串的拼接,動態生成的。因此,客戶端不需要使用 Ajax 這樣的技術額外請求頁面的數據。代碼示例如下:
優點:
缺點:
前后端分離的概念:前后端分離的開發模式,依賴于 Ajax 技術的廣泛應用。簡而言之,前后端分離的 Web 開發模式,就是后端只負責提供 API 接口,前端使用 Ajax 調用接口的開發模式。
優點:
缺點:
不利于 SEO。因為完整的 HTML 頁面需要在客戶端動態拼接完成,所以爬蟲對無法爬取頁面的有效信息。(解決方案:利用 Vue、React 等前端框架的 SSR (server side render)技術能夠很好的解決 SEO 問題?。?/span>
不談業務場景而盲目選擇使用何種開發模式都是耍流氓。
另外,具體使用何種開發模式并不是絕對的,為了同時兼顧了首頁的渲染速度和前后端分離的開發效率,一些網站采用了首屏服務器端渲染 + 其他頁面前后端分離的開發模式。
身份認證(Authentication)又稱“身份驗證”、“鑒權”,是指通過一定的手段,完成對用戶身份的確認。
身份認證的目的,是為了確認當前所聲稱為某種身份的用戶,確實是所聲稱的用戶。例如,你去找快遞員取快遞,你要怎么證明這份快遞是你的。
在互聯網項目開發中,如何對用戶的身份進行認證,是一個值得深入探討的問題。例如,如何才能保證網站不會錯誤的將“馬云的存款數額”顯示到“馬化騰的賬戶”上。
對于服務端渲染和前后端分離這兩種開發模式來說,分別有著不同的身份認證方案:
對于超市來說,為了方便收銀員在進行結算時給 VIP 用戶打折,超市可以為每個 VIP 用戶發放會員卡。
注意:現實生活中的會員卡身份認證方式,在 Web 開發中的專業術語叫做 Cookie
Cookie的幾大特性:
客戶端第一次請求服務器的時候,服務器通過響應頭的形式,向客戶端發送一個身份認證的 Cookie,客戶端會自動將 Cookie 保存在瀏覽器中。
隨后,當客戶端瀏覽器每次請求服務器的時候,瀏覽器會自動將身份認證相關的 Cookie,通過請求頭的形式發送給服務器,服務器即可驗明客戶端的身份。
由于 Cookie 是存儲在瀏覽器中的,而且瀏覽器也提供了讀寫 Cookie 的 API,因此 Cookie 很容易被偽造,不具有安全性。因此不建議服務器將重要的隱私數據,通過 Cookie 的形式發送給瀏覽器。
注意:千萬不要使用 Cookie 存儲重要且隱私的數據!比如用戶的身份信息、密碼等。
3.6、提高身份認證的安全性
為了防止客戶偽造會員卡,收銀員在拿到客戶出示的會員卡之后,可以在收銀機上進行刷卡認證。只有收銀機確認存在的會員卡,才能被正常使用。
這種“會員卡 + 刷卡認證”的設計理念,就是 Session 認證機制的精髓。
在 Express 項目中,只需要安裝 express-session 中間件,即可在項目中使用 Session 認證:
npm install express-session
express-session 中間件安裝成功后,需要通過 app.use() 來注冊 session 中間件,示例代碼如下:
-
// 導入 session 中間件
-
const session = require('express-session')
-
-
// 配置 session 中間件
-
app.use(
-
session({
-
secret: 'itheima', // secret 屬性的值可以為任意字符串
-
resave: false, // 固定寫法
-
saveUninitialized: true, // 固定寫法
-
})
-
)
當 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 // 用戶的信息
-
console.log(req.body)
-
req.session.islogin = true // 用戶的登錄狀態
-
-
res.send({ status: 0, msg: '登錄成功' })
-
})
可以直接從 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,
-
})
-
})
調用 req.session.destroy() 函數,即可清空服務器保存的 session 信息。
-
// 退出登錄的接口
-
app.post('/api/logout', (req, res) => {
-
// TODO_04:清空 Session 信息
-
req.session.destroy()
-
res.send({
-
status: 0,
-
msg: '退出登錄成功',
-
})
-
})
index.html
-
<!DOCTYPE html>
-
<html lang="en">
-
-
<head>
-
<meta charset="UTF-8">
-
<meta name="viewport" content="width=device-width, initial-scale=1.0">
-
<title>后臺主頁</title>
-
<script src="./jquery.js"></script>
-
</head>
-
-
<body>
-
<h1>首頁</h1>
-
-
<button id="btnLogout">退出登錄</button>
-
-
<script>
-
$(function () {
-
-
// 頁面加載完成后,自動發起請求,獲取用戶姓名
-
$.get('/api/username', function (res) {
-
// status 為 0 表示獲取用戶名稱成功;否則表示獲取用戶名稱失敗!
-
if (res.status !== 0) {
-
alert('您尚未登錄,請登錄后再執行此操作!')
-
location.href = './login.html'
-
} else {
-
alert('歡迎您:' + res.username)
-
}
-
})
-
-
// 點擊按鈕退出登錄
-
$('#btnLogout').on('click', function () {
-
// 發起 POST 請求,退出登錄
-
$.post('/api/logout', function (res) {
-
if (res.status === 0) {
-
// 如果 status 為 0,則表示退出成功,重新跳轉到登錄頁面
-
location.href = './login.html'
-
}
-
})
-
})
-
})
-
</script>
-
</body>
-
-
</html>
login.html
-
<!DOCTYPE html>
-
<html lang="en">
-
-
<head>
-
<meta charset="UTF-8">
-
<meta name="viewport" content="width=device-width, initial-scale=1.0">
-
<title>登錄頁面</title>
-
<script src="./jquery.js"></script>
-
</head>
-
-
<body>
-
<!-- 登錄表單 -->
-
<form id="form1">
-
<div>賬號:<input type="text" name="username" autocomplete="off" /></div>
-
<div>密碼:<input type="password" name="password" /></div>
-
<button>登錄</button>
-
</form>
-
-
<script>
-
$(function () {
-
// 監聽表單的提交事件
-
$('#form1').on('submit', function (e) {
-
// 阻止默認提交行為
-
e.preventDefault()
-
// 發起 POST 登錄請求
-
$.post('/api/login', $(this).serialize(), function (res) {
-
// status 為 0 表示登錄成功;否則表示登錄失?。?/span>
-
if (res.status === 0) {
-
location.href = './index.html'
-
} else {
-
alert('登錄失??!')
-
}
-
})
-
})
-
})
-
</script>
-
</body>
-
-
</html>
app.js
-
// 導入 express 模塊
-
const express = require('express')
-
// 創建 express 的服務器實例
-
const app = express()
-
-
// TODO_01:請配置 Session 中間件
-
const session = require('express-session')
-
app.use(
-
session({
-
secret: 'itheima',
-
resave: false,
-
saveUninitialized: true,
-
})
-
)
-
-
// 托管靜態頁面
-
app.use(express.static('./pages'))
-
// 解析 POST 提交過來的表單數據
-
app.use(express.urlencoded({ extended: false }))
-
-
// 登錄的 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 // 用戶的信息
-
console.log(req.body)
-
req.session.islogin = true // 用戶的登錄狀態
-
-
res.send({ status: 0, msg: '登錄成功' })
-
})
-
-
// 獲取用戶姓名的接口
-
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,
-
})
-
})
-
-
// 退出登錄的接口
-
app.post('/api/logout', (req, res) => {
-
// TODO_04:清空 Session 信息
-
req.session.destroy()
-
res.send({
-
status: 0,
-
msg: '退出登錄成功',
-
})
-
})
-
-
// 調用 app.listen 方法,指定端口號并啟動web服務器
-
app.listen(80, function () {
-
console.log('Express server running at http://127.0.0.1:80')
-
})
Session 認證機制需要配合 Cookie 才能實現。由于 Cookie 默認不支持跨域訪問,所以,當涉及到前端跨域請求后端接口的時候,需要做很多額外的配置,才能實現跨域 Session 認證。
注意:
JWT(英文全稱:JSON Web Token)是目前最流行的跨域認證解決方案。
總結:用戶的信息通過 Token 字符串的形式,保存在客戶端瀏覽器中。服務器通過還原 Token 字符串的形式來認證用戶的身份。
JWT 通常由三部分組成,分別是 Header(頭部)、Payload(有效荷載)、Signature(簽名)。
三者之間使用英文的“.”分隔,格式如下:
下面是 JWT 字符串的示例:
JWT 的三個組成部分,從前到后分別是 Header、Payload、Signature。
其中:
客戶端收到服務器返回的 JWT 之后,通常會將它儲存在 localStorage 或 sessionStorage 中。
此后,客戶端每次與服務器通信,都要帶上這個 JWT 的字符串,從而進行身份認證。推薦的做法是把 JWT 放在 HTTP 請求頭的 Authorization 字段中,格式如下:
運行如下命令,安裝如下兩個 JWT 相關的包:
npm install jsonwebtoken express-jwt
其中:
使用 require() 函數,分別導入 JWT 相關的兩個包:
-
// 安裝并導入 JWT 相關的兩個包,分別是 jsonwebtoken 和 express-jwt
-
const jwt = require('jsonwebtoken')
-
const expressJWT = require('express-jwt')
為了保證 JWT 字符串的安全性,防止 JWT 字符串在網絡傳輸過程中被別人破解,我們需要專門定義一個用于加密和解密的 secret 密鑰:
-
// 定義 secret 密鑰,建議將密鑰命名為 secretKey,本質上就是一個字符串
-
const secretKey = 'itheima No1 ^_^'
調用 jsonwebtoken 包提供的 sign() 方法,將用戶的信息加密成 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 屬性發送給客戶端
-
// 參數1:用戶的信息對象
-
// 參數2:加密的秘鑰
-
// 參數3:配置對象,可以配置當前 token 的有效期
-
// 記?。呵f不要把密碼加密到 token 字符中
-
const tokenStr = jwt.sign({ username: userinfo.username }, secretKey, { expiresIn: '30s' })
-
res.send({
-
status: 200,
-
message: '登錄成功!',
-
token: tokenStr, // 要發送給客戶端的 token 字符串
-
})
-
})
-
// 使用 app.use() 來注冊中間件
-
// expressJWT({ secret: secretKey }) 就是用來解析 Token 的中間件
-
// .unless({ path: [/^\/api\//] }) 用來指定哪些接口不需要訪問權限
-
app.use(expressJWT({ secret: secretKey }).unless({ path: [/^\/api\//] }))
6.6、使用 req.user 獲取用戶信息
當 express-jwt 這個中間件配置成功之后,即可在那些有權限的接口中,使用 req.user 對象,來訪問從 JWT 字符串中解析出來的用戶信息了,示例代碼如下:
-
// 這是一個有權限的 API 接口
-
app.get('/admin/getinfo', function (req, res) {
-
// TODO_05:使用 req.user 獲取用戶信息,并使用 data 屬性將用戶信息發送給客戶端
-
console.log(req.user)
-
res.send({
-
status: 200,
-
message: '獲取用戶信息成功!',
-
data: req.user, // 要發送給客戶端的用戶信息
-
})
-
})
分享此文一切功德,皆悉回向給文章原作者及眾讀者.
免責聲明:藍藍設計尊重原作者,文章的版權歸原作者。如涉及版權問題,請及時與我們取得聯系,我們立即更正或刪除。
藍藍設計( www.syprn.cn )是一家專注而深入的界面設計公司,為期望卓越的國內外企業提供卓越的UI界面設計、BS界面設計 、 cs界面設計 、 ipad界面設計 、 包裝設計 、 圖標定制 、 用戶體驗 、交互設計、 網站建設 、平面設計服務、UI設計公司、界面設計公司、UI設計服務公司、數據可視化設計公司、UI交互設計公司、高端網站設計公司、UI咨詢、用戶體驗公司、軟件界面設計公司
藍藍設計的小編 http://www.syprn.cn