原文網址:https://ailjx.blog.csdn.net/article/details/127909213
作者:海底烤肉店ai
在前面的幾節中我們已經創建並優化好了簡易使用者管理系統的專案結構,也對Cookie-Session登入驗證
的工作原理做了講解,接下來我們將繼續補充這個系統的功能,這一節我們將實戰運用Cookie-Session
來實現這個系統的登入驗證功能。 【相關教學推薦:nodejs影片教學】
什麼?你還不了解session
、cookie
!快去看看上篇文章:詳解Cookie-Session登入驗證的工作原理
#在vies
目錄下新建login.ejs
:
Document 登录页面
用户名:密码:
注意:頁面中請求的介面是
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
)添加一个退出登录的按钮:
为按钮添加点击事件:
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
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相关知识,请访问:nodejs 教程!
以上是Node實戰:運用Cookie&Session進行登入驗證的詳細內容。更多資訊請關注PHP中文網其他相關文章!