Node.js + Express + mongodb 的博客项目之保持用户登录,区分管理员账户,及用户登出(四)

前言

在上一节中,完成了用户的注册与登录,但是并没有保持用户登录,一刷新浏览器登录状态就没有了。所以在本节内容中将实现用户登录状态的保持,同时进行普通用户与博客管理员的区别,并且实现用户登出接口。


想要维持住登录状态,就得借用 Cookie,Cookie 是服务器给客户端发的一段数据,可以比作通行证,客户端需要保存这段数据,不得随意删除。此后客户端每次访问服务器的时候都要携带上这段数据,这样服务器就能通过这段数据来确认用户的身份了。
Cookie 一般有两个作用:
第一个就是识别用户身份,比如用户 a 访问了 tlin.com ,那么 tflin.com 的服务器就会给 a 返回一段数据 [uid = 1],以后 a 不管访问 tflin.com 的任何页面都会携带上 [uid = 1] 这段数据,这样就能根据这段数据进行身份识别了。
第二个就是记录历史,也就是比如那些购物网站上的未登录的购物车,同样是借用 Cookie 。当 a 将商品介入购物车时,可以使用 Js 对 Cookie 进行修改,保存相应的记录。这样一来,关闭网页后,即使用户没有登录,再次打开网页,商品依旧安安静静的躺在购物车里。


保持用户登录

在本项目中,使用 Cookies 模块来设置 Cookie,这是他的官方文档 Cookies API
我们先在项目的入口文件 app.js 中引入 Cookies 模块并进行中间件设置,在 app.js 添加如下代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// 引入cookies模块
const Cookies = require("cookies");

// cookies配置
app.use((req, res, next) => {
// 向请求体对象中新加一个cookies属性,对应当前请求,相应
req.cookies = new Cookies(req, res);
// 给req对象增加一个用户信息的属性,以便所有路由都能读取
req.userInfo = {};
// 如果客户端中有cookie信息
if (req.cookies.get("userInfo")) {
// 将其解析后存入req.userInfo中
req.userInfo = JSON.parse(req.cookies.get("userInfo"))
}
// 继续下一个中间件
next();
});

然后在修改登录接口,在其登录成功的时候设置 cookie 信息,将 routes/api 中的登录接口修改为如下代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
/*
用户登录的接口
提交的验证:
1.用户名不能为空
2.密码不能为空
数据库的验证:
1.用户名是否存在
2.若存在密码是否正确
*/
router.post("/user/login", (req, res, next) => {
// 用户名
let username = req.body.username;
// 密码
let password = req.body.password;
console.log(password);
// 用户名不能为空
if (username === "") {
responseData.code = 1;
responseData.message = "用户名不能为空!";
res.json(responseData);
return;
}
// 密码不能为空
if (password === "") {
responseData.code = 2;
responseData.message = "密码不能为空!";
res.json(responseData);
return;
}
// 向数据库查询,用户是否存在
userModel.findOne({username: username}, (err, user) => {
// 如果存在该用户
if (user) {
// 验证密码是否正确
if (user.password === password) {
responseData.message = "登录成功!";
// 在responseData中新建一个用户信息的属性
responseData.userInfo = {
username: user.username,
userid: user._id
};
// 设置cookie信息
req.cookies.set("userInfo", JSON.stringify(responseData.userInfo));
res.json(responseData);
return;
} else {
responseData.code = 5;
responseData.message = "密码不正确!";
res.json(responseData);
return;
}
} else {
responseData.code = 6;
responseData.message = "该用户名不存在!";
res.json(responseData);
return;
}
});
});

这个时候我们已经将 cookie 的相关设置配置完成,当在用户登录成功的时候,服务器向客户端发送一个 cookie 信息,并且将 cookie 信息存到 req..userInfo 中,就可以将 req.userInfo 当做数据绑定给模板引擎。模板引擎则根据 req.userInfo 是否存在来判断用户是否已登录。
首先先在 /routes/main.js 中将 req.userInfo 分配给模板引擎:

1
2
3
4
5
6
7
8
// 首页路由配置
router.get("/", (req, res) => {
// 渲染首页模板
res.render("main/index", {
// 给模板引擎绑定数据
userInfo: req.userInfo
});
});

接着就可在前台的页面 views/main/index.ejs 中使用所绑定的数据了,并以此来判断用户是否登录,思路如下:
如果 userInfo 存在则渲染用户信息面板,若不存在则渲染登录/注册面板

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
<div class="col-md-4">
<%if (!userInfo.userid) {%>
<!-- 登录面板 -->
<div id="login">
<h2>登录</h2>
<div class="input-group">
<span class="input-group-addon">用户</span>
<input type="text" class="form-control" name="username" placeholder="请输入用户名">
</div>
<br>
<div class="input-group">
<span class="input-group-addon">密码</span>
<input type="password" class="form-control" name="password" placeholder="请输入密码">
</div>
<br>
<button type="button" name="button" class="btn btn-primary form-control" id="login-btn">登录</button>
<br><br>
<a href="javascripts:;" class="col-md-offset-4">没有账号?点击注册</a>
<div class="alert alert-success alert-dismissable hide" role="alert">
<button class="close" type="button">×</button>
<span>恭喜您操作成功!</span>
</div>
</div>
<!-- 注册面板 -->
<div id="reg" class="hide">
<h2>注册</h2>
<div class="input-group">
<span class="input-group-addon">用户名称</span>
<input type="text" class="form-control" name="username" placeholder="请输入用户名">
</div>
<br>
<div class="input-group">
<span class="input-group-addon">输入密码</span>
<input type="password" class="form-control" name="password" placeholder="请输入密码">
</div>
<br>
<div class="input-group">
<span class="input-group-addon">确认密码</span>
<input type="password" class="form-control" name="repassword" placeholder="请再次输入密码">
</div>
<br>
<button type="button" name="button" class="btn btn-primary form-control" id="reg-btn">注册</button>
<br><br>
<a href="javascripts:;" class="col-md-offset-4">已有账号?点击登录</a>
<!-- 警告框 -->
<div class="alert alert-success alert-dismissable hide" role="alert">
<button class="close" type="button">×</button>
<span>恭喜您操作成功!</span>
</div>
</div>
<%} else {%>
<!-- 用户面板 -->
<div id="user-info">
<div class="panel panel-primary">
<div class="panel-heading">用户面板</div>
<div class="panel-body">
<h3>欢迎,<%=userInfo.username%></h3>
<p>
<button type="button" name="button" class="btn btn-danger form-control">注销</button>
</p>
</div>
</div>
</div>
<%}%>
</div>

改完前台模板,我们的登录的 ajax 请求登录成功后的函数也要进行相应的修改,将登录成功后隐藏登录面板改为刷新页面,因为此时 cookie 已存在,所以刷新页面后,模板会自动渲染出用户信息面板:

1
2
3
4
5
6
7
8
9
10
success: (result) => {
if (result.code) {
$warningBox.find("span").html("警告:" + result.message);
$warningBox.addClass("alert-danger")
$warningBox.removeClass("hide alert-success");
} else {
// 重新加载页面
window.location.reload();
}
}

最后我们在客户端浏览器中进行测试,发现此时用户登陆后,无论怎么刷新,还是保持着登录状态。
保持用户登录


用户登出接口

用户登出接口则只要将 cookie 清空即可,在 /routes/api.js 中添加如下代码:

1
2
3
4
5
6
// 用户登出接口
router.get("/user/logout", (req, res, next) => {
// 将cookie设为null
req.cookies.set("userInfo", null);
res.json(responseData);
});

然后在前端用 ajax 请求:

1
2
3
4
5
6
7
8
9
10
11
12
// 用户登出
$logoutBtn.on("click", () => {
$.ajax({
type: "get",
url: "/api/user/logout",
success: (result) => {
if (result) {
window.location.reload();
}
}
});
});

重启服务器进行测试:
用户登出
到此,用户的登录注册及登出完成。


管理员与普通用户的区别

在数据库中,用户集合中有一个字段为 isadmin 这个字段就是是否是管理员,默认为 false,我们可以根据这个字段来判断当前登录的用户是否是管理员。具体步骤是将其从数据库中读取出来然后存放到 req.userInfo 中,与其他页面共享,绑定到模板引擎中,根据 isadmin 的值渲染不同的界面。
在项目的入口文件 app.js 先将用户的模型引用进来:

1
2
// 引用用户模型对象
const userModel = require("./models/user");

再cookies 配置的中间件中修改为如下代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// cookies配置
app.use((req, res, next) => {
// 向请求体对象中新加一个cookies属性,对应当前请求,相应
req.cookies = new Cookies(req, res);
// 给req对象增加一个用户信息的属性,以便所有路由都能读取
req.userInfo = {};
// 如果客户端中有cookie信息
if (req.cookies.get("userInfo")) {
// 将其解析后存入req.userInfo中
req.userInfo = JSON.parse(req.cookies.get("userInfo"));
// 根据用户id从数据库中查询出当前登录用户的信息
userModel.findById(req.userInfo.userid).then((user) => {
// 以此判断当前用户是否为管理员
req.userInfo.isadmin = user.isadmin;
next();
});

} else {
// 继续下一个中间件
next();
}
});

然后将博客主页的模板中的用户面板部分修改为如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<!-- 用户面板 -->
<div id="user-info">
<div class="panel panel-primary">
<div class="panel-heading">用户面板</div>
<div class="panel-body">
<%if (userInfo.isadmin) {%>
<h3>欢迎尊贵的管理员,<%=userInfo.username%>!</h3>
<p>
<button type="button" name="button" class="btn btn-danger form-control">进入控制台</button>
</p>
<%} else {%>
<h3>欢迎您,<%=userInfo.username%></h3>
<%}%>
<p>
<button type="button" name="button" id="logout" class="btn btn-danger form-control">注销</button>
</p>
</div>
</div>
</div>

接着把数据库中的 admin 账户的 isadmin 置为 true 然后重启服务器到客户端浏览器上测试一下:
管理员区分
此时管理员账户与普通用户账户已经区分出来了。

------ 本文结束 ------

版权声明

Tflin's Blog by Tan Feng Lin is licensed under a Creative Commons BY-NC-ND 4.0 International License.
谭丰林创作并维护的Tflin's Blog博客采用创作共用保留署名-非商业-禁止演绎4.0国际许可证
本文首发于Tflin's Blog 博客( http://tflin.com ),版权所有,侵权必究。