大部分的项目中都存在登录注册这个模块,除了常规的操作之外,比较重要的一点,我们需要对用户的密码进行加密之后再存储。
除了考虑数据库被黑掉的情况,作为一个程序员来说,职业操守也要求我们要做密码的加密,这属于用户的隐私,不加密的话,几乎所有的技术人员都可以知道用户的密码,这是不合理的。
作为一个前端,可能在正式项目中,不需要由前端来考虑密码的加密,但是前端多了解一些后端的知识,对你自己肯定是有帮助的。
因为我本身是做前端的,所以本文章中,关于后端的技术选型以Nodejs
的Express
框架为例,数据库选择Mongodb
。
那么,怎么进行数据的加密呢?
在早些年,可能MD5
的加密方式比较流行。大家对加密的最基本的需求是不可逆。如果可逆的话,对于技术人员来说,依然和明文没什么区别。
MD5
虽然是不可逆的,但是它有一个唯一的对应值,比如a在加密之后对应123,b加密之后对应456,这个是固定的。所以,当越来越多的加密过程被记录下来之后,绘制成一张表。就可以找到相应的对应关系。
再之后,有一些针对MD5
的补救的措施,比如“加盐”,就是在MD5
加密过程中,多包裹一层字符串作为密钥。
今天我们介绍的加密方式是bcrypt.js
,不需要做“加盐”处理。
首先我们需要通过Express
构造一套后端服务。
npm install express --save
const express = require('express');
const app = express();
app.use(express.urlencoded({ extended: true }));
app.use(express.json());
app.listen(9000, () => {
console.log("server running!");
});
这样就开了一个端口为9000的后端服务。
然后是数据库的部分
npm install mongoose --save
Node
中我们通过mongoose
来操作mongodb
数据库
const express = require('express');
const mongoose = require("mongoose");
const app = express();
app.use(express.urlencoded({ extended: true }));
app.use(express.json());
// 数据库连接
mongoose
.connect("mongodb://localhost/UserAccount")
.then(() => {
console.log("数据库连接成功");
})
.catch((err) => {
console.log("数据库连接失败", err);
});
// 设定集合规则
const UserAccountSchema = new mongoose.Schema({
username: {
type: String,
unique: true,
},
password: {
type: String,
},
});
// 创建集合并应用规则
const UserAccount = mongoose.model("Course", UserAccountSchema);
app.listen(9000, () => {
console.log("server running!");
});
怎么去配置Mongodb
数据库,这里就不解释了,不是重点。
连接UserAccount表之后,设定集合规则,创建集合。
数据库表的设计,就简单一些,只有一个username
和一个password
。mongodb
中有一个unique
字段,来表示不能重复。理论上账号是不允许重复的。
这样,我们就完成了基本的操作。我们来写一个注册的接口。
app.post("/register", async (req, res) => {
const {
body: { username, password },
} = req;
try {
const result = await UserAccount.create({
username,
password,
});
res.send(result);
} catch (error) {
return res.status(400).send({
message: "用户名已存在",
});
}
});
这是一个最基本的注册逻辑。解构出POST
方法携带的username
和password
。之后。通过create
方法来添加新数据。
为什么要包裹一个try catch
呢,因为我们给username
设置了唯一unique
。如果新注册的username
和数据库中已存在的重复,就会报错,所以我们在catch
中给前端一个反馈。
这样就完成了基本的注册逻辑。
那么怎么加密呢
先通过npm
下载一下bcrypt.js
npm install bcryptjs --save
引入
const bcrypt = require("bcryptjs");
bcrypt.js
提供了两个核心方法,hashSync
和compareSync
,hashSync
是加密用的,compareSync
是用来做验证的。
那么怎么使用呢。
在设定集合规则的时候
const UserAccountSchema = new mongoose.Schema({
username: {
type: String,
unique: true,
},
password: {
type: String,
set(val) {
return bcrypt.hashSync(val);
},
},
});
每个字段都有提供给set
属性,跟Vue
中用来做双向绑定的getter
和setter
一样。
所以,我们选择在set
中对password
进行加密。
bcrypt.hashSync()
中传入要加密的值即可。
password
就会被加密成这样。
我们在设定集合规则的时候,在字段的set
属性中进行加密,就能保证,此表的所有数据,在新增数据时,都会进行加密。
这样加密就完成了,登录的时候怎么进行验证呢?
我们再写一个登录接口。
app.post("/login", async (req, res) => {
const {
body: { username, password },
} = req;
const user = await UserAccount.findOne({
username,
});
if (!user) {
return res.status(400).send({
message: "用户名不存在",
});
}
const isPasswordValid = bcrypt.compareSync(password, user.password);
if (!isPasswordValid) {
return res.status(400).send({
message: "密码错误",
});
}
res.send(user)
});
在登录接口login
中首先做的事情肯定是解构出POST
方法携带的参数username
和password
。
然后通过mongodb
提供的findOne
方法通过username
进行查询,因为每个username
都是唯一的。
如果查不到值,就给前端反馈一个用户名不存在。如果查到值了,说明username
是存在的,就要验证一下密码password
对不对了。
通过bcrypt.compareSync()
来验证,第一个参数是未加密的值,就是前端传过来的,用户登录时候输入的明文密码。第二个参数是查到的加密后的值,上面我们已经通过findOne
进行查询了。在有值的前提下,才会执行到这里。
然后就是判断返回值是true
还是false
了。true
代表未加密的值和加密后的值是一致的,用户登录成功,false
代表不一致,用户输入的密码不对。
然后给前端相应的反馈即可。
这样我们就通过在Express + Mongodb
在技术栈下,通过bcrypt.js
对密码进行加密和验证的过程。
本文章需要一些Express
和Mongodb
的基础。
全部代码如下:
const express = require("express");
// 引入mongoose第三方模块, 用来操作mongodb数据库
const mongoose = require("mongoose");
const bcrypt = require("bcryptjs");
const jwt = require("jsonwebtoken");
const app = express();
app.use(express.urlencoded({ extended: true }));
app.use(express.json());
// 数据库连接
mongoose
.connect("mongodb://localhost/UserAccount")
.then(() => {
console.log("数据库连接成功");
})
.catch((err) => {
console.log("数据库连接失败", err);
});
// 设定集合规则
const UserAccountSchema = new mongoose.Schema({
username: {
type: String,
unique: true,
},
password: {
type: String,
set(val) {
return bcrypt.hashSync(val);
},
},
});
// 创建集合并应用规则
const UserAccount = mongoose.model("Course", UserAccountSchema);
app.all("*", (req, res, next) => {
// 解决跨域问题
res.setHeader("Access-Control-Allow-Origin", "*");
res.setHeader("Access-Control-Allow-Headers", "*");
next();
});
app.post("/register", async (req, res) => {
const {
body: { username, password },
} = req;
try {
const result = await UserAccount.create({
username,
password,
});
res.send(result);
} catch (error) {
return res.status(400).send({
message: "用户名已存在",
});
}
});
app.post("/login", async (req, res) => {
const {
body: { username, password },
} = req;
const user = await UserAccount.findOne({
username,
});
if (!user) {
return res.status(400).send({
message: "用户名不存在",
});
}
const isPasswordValid = bcrypt.compareSync(password, user.password);
if (!isPasswordValid) {
return res.status(400).send({
message: "密码错误",
});
}
const token = jwt.sign(
{
id: String(user._id),
},
SECRET
);
res.send({
user,
token,
});
});
app.listen(9000, () => {
console.log("server running!");
});
欢迎大家关注我的公众号,有很多关于前端的内容哦
QQ:505417246
WX:18331092918
公众号:Code程序人生
B站账号:LuckyRay123
个人博客:http://rayblog.ltd/