node.js極速入門課程:進入學習
原文地址:https://ailjx.blog.csdn.net/article/details/127909213
作者:海底燒烤店ai
在前面的幾節中我們已經建立並優化好了簡易使用者管理系統的專案結構,也對 Cookie-Session登入驗證
的工作原理做了講解,接下來我們將繼續補充這個系統的功能,這一節我們將實戰運用Cookie-Session
來實現這個系統的登入驗證功能。【相關教學推薦:】
什麼?你還不瞭解session
、cookie
!快去看看上篇文章吧:
在vies
目錄下新建login.ejs
:
<!DOCTYPE html>
<html>
<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>Document</title>
</head>
<body>
<h1>登入頁面</h1>
<div>使用者名稱:<input type="text" id="username"></div>
<div>密碼:<input type="password" id="password"></div>
<div><button id="login">登入</button></div>
<script>
const uname = document.getElementById("username");
const pwd = document.getElementById("password");
const login = document.getElementById("login");
login.onclick = () => {
fetch('/api/login', {
method: 'POST',
body: JSON.stringify({
username: uname.value,
password: pwd.value
}),
headers: {
"Content-Type": "application/json"
}
}).then(res => res.json()).then(res => {
// console.log(res);
if (res.ok) {
location.href = "/"
} else {
alert("使用者名稱密碼不匹配!")
}
})
}
</script>
</body>
</html>
登入後複製
注意:頁面中請求的介面是
POST /api/login
請求
在routes
目錄下新建login.js
,該檔案定義login
頁面的頁面路由:
var express = require("express");
var router = express.Router();
/* GET login page. */
router.get("/", function (req, res, next) {
res.render("login");
});
module.exports = router;
登入後複製
在app.js
中掛載頁面路由:
// 引入
var loginRouter = require("./routes/login");
// 掛載
app.use("/login", loginRouter);
登入後複製
啟動專案,存取http://localhost:3000/login
正常顯示:
在services/UserService.js
中定義介面的模型(M層):
const UserService = {
// .......
// 登入查詢
login: (username, password) => {
// 向資料庫查詢該使用者
return UserModel.findOne({ username, password });
},
};
登入後複製
在controllers/UserController.js
中定義介面的控制層(C層):
const UserController = {
// ......
// 登入驗證
login: async (req, res, next) => {
try {
const { username, password } = req.body;
const data = await UserService.login(username, password);
// console.log(data);
if (data) {
res.send({ ok: 1, msg: "登入成功!", data });
} else {
res.send({ ok: 0, msg: "使用者不存在,登入失敗!" });
}
} catch (error) {
console.log(error);
}
},
};
登入後複製
在routes/users.js
中定義Api
路由:
// 登入校驗
router.post("/login", UserController.login);
登入後複製
至此登入頁面就搭建好了:
在上一節Cookie-Session
登入驗證工作原理的介紹中我們知道:
這個過程顯然是比較複雜的,在express
中有一個express-session
模組可以大大降低我們的工作量,讓我們站在巨人的肩膀上開發!
下載express-session
:
npm i express-session
登入後複製
在app.js
中進行設定:
// 引入express-session
var session = require("express-session");
// 設定session:需要放在在路由設定的前面
app.use(
session({
name: "AilixUserSystem", // cookie名字
secret: "iahsiuhaishia666sasas", // 金鑰:伺服器生成的session的簽名
cookie: {
maxAge: 1000 * 60 * 60, // 過期時間:一個小時過期
secure: false, // 為true時表示只有https協定才能存取cookie
},
resave: true, // 重新設定session後會重新計算過期時間
rolling: true, // 為true時表示:在超時前重新整理時cookie會重新計時;為false表示:在超時前無論重新整理多少次,都是按照第一次重新整理開始計時
saveUninitialized: true, // 為true時表示一開始存取網站就生成cookie,不過生成的這個cookie是無效的,相當於是沒有啟用的信用卡
})
);
登入後複製
設定好後,就會發現瀏覽器中有一個名為AilixUserSystem
的cookie
:
這是因為express-session
會自動解析cookie
和向前端設定cookie
,相當於是圖一中的3、6(前半部分:通過SessionId
查詢到Session
) ,我們不再需要手動對cookie
進行操作。
在登入成功時設定session
:
// controllers/UserController.js
// ....
// 登入校驗
login: async (req, res, next) => {
try {
const { username, password } = req.body;
const data = await UserService.login(username, password);
// console.log(data);
if (data) {
// 設定session:向session物件內新增一個user欄位表示當前登入使用者
req.session.user = data; // 預設存在記憶體中,伺服器一重新啟動就沒了
res.send({ ok: 1, msg: "登入成功!", data });
} else {
res.send({ ok: 0, msg: "使用者不存在,登入失敗!" });
}
} catch (error) {
console.log(error);
}
},
登入後複製
我們向req.session
中新增了一個user
欄位,來儲存使用者登入的資訊,這一步相當於是 圖一中的1(SessionId會由express-session
模組自動生成)、2。
req.session
是一個session
物件,需要注意的是這個物件雖然存在於req
中,但其實不同的人存取系統時他們的req.session
是不同的,因為req.session
是根據我們設定的cookie
(由express-session
模組自動生成的AilixUserSystem
)生成的,每一個人存取系統所生成的cookie
是獨一無二的,所以他們的req.session
也是獨一無二的。
在收到請求時校驗session
,在app.js
新增以下程式碼:
// 設定中介軟體:session過期校驗
app.use((req, res, next) => {
// 排除login相關的路由和介面
// 這個專案中有兩個,一個是/login的頁面路由,一個是/api/login的post api路由,這兩個路由不能被攔截
if (req.url.includes("login")) {
next();
return;
}
if (req.session.user) {
// session物件記憶體在user,代表已登入,則放行
// 重新設定一下session,從而使session的過期時間重新計算(在session設定中設定了: resave: true)
// 假如設定的過期時間為1小時,則當我12點呼叫介面時,session會在1點過期,當我12點半再次呼叫介面時,session會變成在1點半才會過期
// 如果不重新計算session的過期時間,session則會固定的1小時過期一次,無論這期間你是否進行呼叫介面等操作
// 重新計算session的過期時間的目的就是為了防止使用者正在操作時session過期導致操作中斷
req.session.myData = Date.now();
// 放行
next();
} else {
// session物件內不存在user,代表未登入
// 如果當前路由是頁面路由,,則重定向到登入頁
// 如果當前理由是api介面路由,則返回錯誤碼(因為針對ajax請求的前後端分離的應用請求,後端的重定向不會起作用,需要返回錯誤碼通知前端,讓前端自己進行重定向)
req.url.includes("api")
? res.status(401).send({ msg: "登入過期!", code: 401 })
: res.redirect("/login");
}
});
登入後複製
注意:這段程式碼需要在路由設定的前面。
這段程式碼中我們通過req.session.myData = Date.now();
來修改session
物件,從而觸發session
過期時間的更新(session
上myData
這個屬性以及它的值 Date.now()
只是我們修改session
物件的工具,其本身是沒有任何意義的),你也可以使用其它方法,只要能將req.session
修改即可。
因為我們這個專案是後端渲染模板的專案,並不是前後端分離的專案,所以在設定中介軟體進行session
過期校驗攔截路由時需要區分Api路由
和頁面路由
。
後端在攔截API路由後,向前端返回錯誤和狀態碼:
這個時候需要讓前端自己對返回結果進行判斷從而進行下一步的操作(如回到登入頁或顯示彈窗提示),該系統中前端是使用JavaScript
內建的fetch
來進行請求傳送的,通過它來對每一個請求結果進行判斷比較麻煩,大家可以自行改用axios
,在axios
的響應攔截器中對返回結果做統一的判斷。
向首頁(index.ejs
)新增一個退出登入的按鈕:
<button id="exit">退出登入</button>
登入後複製
為按鈕新增點選事件:
const exit = document.getElementById('exit')
// 退出登入
exit.onclick = () => {
fetch("/api/logout").then(res => res.json()).then(res => {
if (res.ok) {
location.href = "/login"
}
})
}
登入後複製
這裡呼叫了GET /api/logout
介面,現在定義一下這個介面,在controllers/UserController.js
中定義介面的控制層(C層):
const UserController = {
// ......
// 退出登入
logout: async (req, res, next) => {
// destroy方法用來清除cookie,當清除成功後會執行接收的引數(一個後調函數)
req.session.destroy(() => {
res.send({ ok: 1, msg: "退出登入成功!" });
});
},
};
登入後複製
在routes/users.js
中定義Api
路由:
// 退出登入
router.get("/logout", UserController.logout);
登入後複製
前面我們通過 req.session.user = data;
設定的session預設是存放到記憶體中的,當後端服務重新啟動時這些session
就會被清空,為了解決這一問題我們可以將session
存放到資料庫中。
安裝connect-mongo
:
npm i connect-mongo
登入後複製
是MongoDB對談儲存,用於用
Typescript編寫的連線
和Express
。
修改app.js
:
// 引入connect-mongo
var MongoStore = require("connect-mongo");
// 設定session
app.use(
session({
name: "AilixUserSystem", // cookie名字
secret: "iahsiuhaishia666sasas", // 金鑰:伺服器生成的session的簽名
cookie: {
maxAge: 1000 * 60 * 60, // 過期時間:一個小時過期
secure: false, // 為true時表示只有https協定才能存取cookie
},
resave: true, // 重新設定session後會重新計算過期時間
rolling: true, // 為true時表示:在超時前重新整理時cookie會重新計時;為false表示:在超時前無論重新整理多少次,都是按照第一次重新整理開始計時
saveUninitialized: true, // 為true時表示一開始存取網站就生成cookie,不過生成的這個cookie是無效的,相當於是沒有啟用的信用卡
store: MongoStore.create({
mongoUrl: "mongodb://127.0.0.1:27017/usersystem_session", // 表示新建一個usersystem_session資料庫用來存放session
ttl: 1000 * 60 * 60, // 過期時間
}), // 存放資料庫的設定
})
);
登入後複製
至此,我們就實現了運用Cookie&Session
進行登入驗證/許可權攔截的功能!
更多node相關知識,請存取:!
以上就是Node實戰:運用Cookie&Session進行登入驗證的詳細內容,更多請關注TW511.COM其它相關文章!