登录功能 | node+express+mysql实现用户注册以及token校验

实现需求

  • 登录
  • 注册
  • 权限校验

实现过程

  • 客户端使用用户名和密码请求登录
  • 服务端收到请求后验证是否登录成功
    • 成功:返回一个token给客户端
    • 失败:提示失败信息
  • 客户端收到token后存储token(token采用jwt进行加密,而不是普通的base64)
  • 每次发起请求时将token发给服务端
  • 服务端接收到请求后,token的合法性
    • 成功:返回客户端所需数据
    • 失败:返回验证失败的信息

流程图解

实现流程

准备工作

安装

  • node
  • express
  • mysql

初始化项目

1
npm init -y

安装express

1
npm i express

启动node服务_新建app.js文件

1
2
3
4
5
6
7
8
9
10
11
// app.js
const express = require('express');

const app = express();

/*
port: 4000
*/
app.listen(4000, () => {
console.log('serve is running on port: 4000');
})

app.listen参数

启动端口

1
node app.js 

连接MySQL

下面的过程都可以看文档:sequelize文档

安装Sequelize和MySql

1
2
npm i sequelize # 这将安装最新版本的 Sequelize
npm i mysql2 # MySQL

连接到数据库_database/init.js

要连接到数据库,必须创建一个 Sequelize 实例. 这可以通过将连接参数分别传递到 Sequelize 构造函数或通过传递一个连接 URI 来完成

1
2
3
4
5
6
7
8
// database/init.js
const { Sequelize } = require('sequelize');

const sequelize= new Sequelize('数据库名称', 'root', '你的密码', {
host: 'localhost',
port: '3306',
dialect: 'mysql'
})

测试连接

引入到app.js

1
require('./database/init')

你可以使用 .authenticate() 函数测试连接是否正常:

1
2
3
4
5
6
try {
await sequelize.authenticate();
console.log('Connection has been established successfully.');
} catch (error) {
console.error('Unable to connect to the database:', error);
}

关闭连接

默认情况下,Sequelize 将保持连接打开状态,并对所有查询使用相同的连接. 如果你需要关闭连接,请调用 sequelize.close()(这是异步的并返回一个 Promise).

完整代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// database/init.js
const { Sequelize } = require('sequelize');

const sequelize= new Sequelize('test_login', 'root', 'yyy674531', {
host: 'localhost',
// @ts-ignore
port: '3306',
dialect: 'mysql'
})


// 测试连接
try {
sequelize.authenticate();
console.log('Connection has been established successfully.');
} catch (error) {
console.error('Unable to connect to the database:', error);
}


module.exports = {Sequelize, sequelize}; // 这里导出,下面有用!

同步表模型

创建模型实例_database/models/User.js

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
// database/models/User.js
const { Op, Model, DataTypes } = require('sequelize');
const { Sequelize, sequelize } = require('../init.js');

const User = sequelize.define('users_info', {
// 应用属性: 禁止null 唯一约束 验证器
username: {
type: DataTypes.STRING,
allowNull: false, // 1. 禁止 null 值
unique: true, // 2. 唯一约束
},
password: {
type: DataTypes.STRING,
allowNull: false,
// validate: { // 3. 验证器
// is: /^[0-9a-f]{64}$/i
// }
}
});

// 将模型与数据库同步
/*
注意:如果表已经存在,使用{force: true}将该表删除
*/
(async () => {
await User.sync().then(() => {
console.log('user表模型已经同步');
})
})();

导出到app.js并启动

实现路由部署(准备)

Express 教程 4:路由和控制器

路由部署_express.Router

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// router/user.js
const express = require('express');
const router = express.Router();

// middleware that is specific to this router
// router.use(function timeLog(req, res, next) {
// console.log('Time:', Date.now());
// next();
// });

// 注册
router.get('/home', (req, res) => {
res.send({msg: 'Welcome!'});
})

module.exports = router;

引入使用

1
2
3
4
5
// app.js
const router = require('./router/user');

app.use(express.urlencoded({extended: false}));
app.use('/user', router);

postman测试

实现注册功能

路由部署

1
2
3
4
5
// router/user.js
router.post('/register', async (req, res) => {
const {username, password} = req.body;
console.log(username); // postman测试能够拿到username信息
})

可以看到控制台已经打印了~说明能够获取到username的信息

注册功能说明:

服务器获取到浏览器传过来的username

首先检验是否存在这个username,如果存在就返回“该用户已经存在,请直接登录”

如果不存在,再进行创建

  1. 去哪里检验——MySQL数据库里面
  2. 怎么创建——利用MySQL的create

接下来会详细说明

用户查询

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// router/user.js
// 注册
const bcryptjs = require('bcryptjs');

router.post('/register', async (req, res) => {
const {username, password} = req.body;
// console.log(username); // postman测试能够拿到username信息

// 用户查询
const model = await User.findOne({where: {username: username}});

if (model) {
return res.send({msg: '该用户已经存在,请直接登录!'})
}else {
const user = await User.create({username, password:bcryptjs.hashSync(password, 5)}); // 加密
// console.log(user.dataValues);
res.send({msg: '注册成功!'});
}
})

postman测试:创建成功~

查询数据库:创建成功~

当我再次用同样的username进行注册请求的时候,返回结果为:该用户已经存在,请直接登录!

注册功能验证成功~

实现登录功能

登录功能说明:

通过发送username和password进行登录

  • 如果uername不存在,就返回“该用户不存在,请先注册”

  • 如果username存在,要先进行密码校验

    • 密码校验不成功——返回“密码错误”
    • 密码校验成功——返回token

登录功能部署

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// router/user.js
// 登录功能
const bcryptjs = require('bcryptjs');

router.post('/login', async (req, res) => {
const {username, password} = req.body;
console.log(req.body);
const model = await User.findOne({where: {username: username}});
console.log(model);

if (!model) {
return res.send({msg: '该用户不存在,请先注册!'})
} else {
// 如果存在username就要进行密码校验
const passwordValid = bcryptjs.compareSync(password, model.dataValues.password);

if (!passwordValid) {
return res.send({msg: '密码错误!'})
} else {
res.send({msg: '登录成功!'})
}
}

})

正确输入用户名和密码:

输入错误密码:

输入未注册用户名:

token生成及返回_jsonwebtoken

jwt中文文档

1
jwt.sign(payload, secretOrPrivateKey, [options, callback]);
1
2
3
// router/user.js
const token = jwt.sign({username}, 'ccken')
res.send({token})

实现权限校验

权限校验说明:

  1. token包含在请求头里面,所以第一步我们要获取token
    • 如果没有token说明前面没有登录过或者是登录已经失效,返回“请登录”
  2. 利用token 密钥 和 payload对token进行解析
  3. 对解析结果进行校验查询
    • 如果查询结果为空,返回’请注册‘
    • 如果不为空,则说明“权限校验成功!”
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// router/user.js
// 权限校验
router.post('/auth', async (req, res) => {
// 获取token,token是在请求头里面的
const token = String(req.headers.authorization).split(' ').pop();
// 如果没有token说明前面没有登录过或者是登录已经失效
if (!token) return res.send({msg: '请登录!'})
// 根据密钥和字段解析token
const {username} = jwt.verify(token, 'ccken'); // 之前使用username进行注册的
// 对解析结果进行校验查询
const model = User.findOne({where: {username: username}});

if (!model) {
return res.send({msg: '请注册!'});
} else {
res.send({msg: '权限校验成功!'})
}
})

代码

以上过程的代码放在github了~

参考

app.listen参数

sequelize文档

Express 教程 4:路由和控制器

jwt中文文档

cookie、session、token、jwt、单点登录