文章目录
- 初始化
- 创建项目
- 配置跨域
- 配置解析表单数据的中间件
- 安装bodyparser
- 初始化用户路由模块
- 抽离用户路由模块中的处理函数
- 登录注册
- 新建admin表
- 安装并配置mysql模块
- 注册
- 检测表单数据是否合法
- 检测用户名是否被占用
- 对密码进行加密处理bcryptjs
- 插入新用户
- 测试
- 登录
- 根据名字查询用户的数据
- 判断用户输入的密码是否正确
- 生成 JWT 的 Token 字符串
- 测试
初始化
创建项目
- 新建
node-app
文件夹作为项目根目录,并在根目录中运行以下命令,初始化包管理配置文件:
npm init -y
- 运行以下命令,安装特定版本express
npm i express@4.17.1
- 在根目录下新建
app.js
作为项目入口文件,并初始化以下代码:
// 导入express模块
const express = require('express')
// 导入expressd的服务器实例
const app = express()
// 调用app.listen方法,指定端口号并启动web服务器
app.listen(3000, () => {
console.log(`api server running at http://127.0.0.1:3000`)
})
配置跨域
- 运行以下命令,安装
cors
中间件:
npm i cors@2.8.5
- 在
app.js
中导入并配置cors
中间件:
// 导入 cors 中间件
const cors = require('cors')
// 将 cors 注册为全局中间件
app.use(cors())
配置解析表单数据的中间件
通过如下的代码,配置解析 application/x-www-form-urlencoded
格式的表单数据的中间件:
app.use(express.urlencoded({ extended: false }))
安装bodyparser
登录、注册要使用post
请求 安装body-parser
npm install body-parser
const bodyParser = require("body-parser")
// 使用body-parse中间件 要放在路由之前
//app.use(bodyParser.urlencoded({ extended: false }))
app.use(bodyParser.json())
初始化用户路由模块
- 在
router
文件夹中,新建admin.js
文件,作为管理员的路由模块,并初始化代码如下:
const express = require('express')
// 创建路由对象
const router = express.Router()
// 注册
router.post('/register', (req, res) => {
res.send('register')
})
// 登录
router.post('/login', (req, res) => {
res.send('login')
})
// 将路由对象共享出去
module.exports = router
- 在
app.js
中,导入并使用管理员路由模块:
// 处理管理员(&用户)登录注册相关的路由
const adminRouter = require('./router/admin')
app.use('/api/admin', adminRouter)
抽离用户路由模块中的处理函数
目的:为了保证 路由模块 的纯粹性,所有的 路由处理函数,必须抽离到对应的 路由处理函数模块 中
- 在
/router_handler/admin_handler.js
中,使用exports
对象,分别向外共享如下两个路由处理函数 :
/**
* 在这里定义和登录注册相关的路由处理函数
* 供/router/admin.js模块进行调用
*/
// 注册用户的处理函数
exports.register = (req, res) => {
res.send('register')
}
// 登录的处理函数
exports.login = (req, res) => {
res.send('login')
}
- 将
/router/admin.js
中的代码修改为如下结构:
const express = require('express')
// 创建路由对象
const router = express.Router()
// 导入登录注册处理函数模块
const adminHandler = require('../router_handler/admin_handler')
// 注册
router.post('/register', adminHandler.register)
// 登录
router.post('/login', adminHandler.login)
// 将路由对象共享出去
module.exports = router
登录注册
新建admin表
在manage
数据库中,新建admin
表如下:
安装并配置mysql模块
在 API 接口项目中,需要安装并配置 mysql 这个第三方模块,来连接和操作 MySQL 数据库
- 运行以下命令,安装
mysql
模块:
npm i mysql@2.18.1
- 在根目录下中新建
/db/index.js
文件,在此自定义模块中创建数据库的连接对象:
// 导入mysql模块
const mysql = require('mysql')
// 创建数据库连接对象
const db = mysql.createPool({
host: '127.0.0.1',
user: 'root',
password: 'IKUN1220',
database: 'manage'
})
// 向外共享db数据库连接对象
module.exports = db
注册
检测表单数据是否合法
- 判断
name
和password
是否为空
const adminInfo = req.body
if (!adminInfo.name || !adminInfo.password) {
return res.json({ status: 400, message: '用户名或密码为空' })
}
检测用户名是否被占用
- 导入数据库操作模块:
const db = require('../db/index')
- 定义sql语句:
const sql = `select * from admin where name = ? `
- 执行 SQL 语句并根据结果判断用户名是否被占用:
db.query(sql, [adminInfo.name], (err, results) => {
// 执行sql语句失败
if (err) {
return res.send({
status: 404,
message: err.message
})
}
// 用户名被占用
if (results.length > 0) {
return res.status(400).json('用户名已被注册,请更改后重新注册!')
}
// TODO: 用户名可用,继续之后流程
})
对密码进行加密处理bcryptjs
为了保证密码的安全性,不建议在数据库以 明文 的形式保存用户密码,推荐对密码进行 加密存储
在当前项目中,使用 bcryptjs 对用户密码进行加密,优点:
- 加密之后的密码,无法被逆向破解
- 同一明文密码多次加密,得到的加密结果各不相同,保证了安全性
- 运行如下命令,安装指定版本的
bcryptjs
:
npm i bcryptjs@2.4.3
- 在
/router_handler/admin.js
中,导入bcryptjs
:
const bcrypt = require('bcryptjs')
- 在注册用户的处理函数中,确认用户名可用之后,调用
bcrypt.hashSync(明文密码, 随机盐的长度)
方法,对用户的密码进行加密处理:
// 对用户的密码,进行 bcrype 加密,返回值是加密之后的密码字符串
adminInfo.password = bcrypt.hashSync(adminInfo.password, 10)
插入新用户
- 定义插入用户的sql语句
const sqlStr = 'insert into admin set ?'
2.db.query()
执行 SQL 语句,插入新用户:
db.query(sqlStr, { name: adminInfo.name, password: adminInfo.password, identify: adminInfo.identify }, (err, results) => {
if (err) {
return res.send({
status: 400,
message: err.message
})
}
if (results.affectedRows !== 1) {
return res.status(400).json('注册用户失败,请稍后再试')
}
res.json({
status: 200,
message: '注册成功',
name: adminInfo.name,
password: adminInfo.password,
identify: adminInfo.identify
})
})
})
此时router_handler/amdin_handler.js
为:
/**
* 在这里定义和登录注册相关的路由处理函数
* 供/router/admin.js模块进行调用
*/
const db = require('../db/index')
const bcrypt = require('bcryptjs')
// 注册用户的处理函数
exports.register = (req, res) => {
const adminInfo = req.body
if (!adminInfo.name || !adminInfo.password) {
return res.json({ status: 400, message: '用户名或密码为空' })
}
const sql = `select * from admin where name = ? `
db.query(sql, [adminInfo.name], (err, results) => {
if (err) {
return res.send({
status: 404,
message: err.message
})
}
if (results.length > 0) {
return res.status(400).json('用户名已被注册,请更改后重新注册!')
}
// 对用户的密码,进行 bcrype 加密,返回值是加密之后的密码字符串
adminInfo.password = bcrypt.hashSync(adminInfo.password, 10)
const sqlStr = 'insert into admin set ?'
db.query(sqlStr, { name: adminInfo.name, password: adminInfo.password, identify: adminInfo.identify }, (err, results) => {
if (err) {
return res.send({
status: 400,
message: err.message
})
}
if (results.affectedRows !== 1) {
return res.status(400).json('注册用户失败,请稍后再试')
}
res.json({
status: 200,
message: '注册成功',
name: adminInfo.name,
password: adminInfo.password,
identify: adminInfo.identify
})
})
})
}
// 登录的处理函数
exports.login = (req, res) => {
res.send('login')
}
测试
登录
根据名字查询用户的数据
- 接收表单数据:
const adminInfo = req.body
- 定义sql语句:
const sql = `select * from admin where name = ?`
- 执行sql语句,查询用户的数据:
db.query(sql, adminInfo.name, (err, results) => {
if (err) {
return res.status(400).json(err)
}
if (results.length !== 1) {
return res.status(400).json('用户不存在')
}
})
判断用户输入的密码是否正确
核心实现思路:调用
bcrypt.compareSync(用户提交的密码, 数据库中的密码)
方法比较密码是否一致
返回值是布尔值(true 一致、false 不一致)
// 拿着用户输入的密码,和数据库中存储的密码进行对比
const compareResult = bcrypt.compareSync(adminInfo.password, results[0].password)
// 如果对比的结果为false,则证明用户输入的密码错误
if(!compareResult) {
return res.status(400).json('用户名或密码输入错误,请重新输入')
}
生成 JWT 的 Token 字符串
核心注意点:在生成 Token 字符串的时候,一定要剔除 密码 的值
通过 ES6 的高级语法,快速剔除 密码的值:
// 剔除完毕之后,admin 中只保留了用户的 id,name, identify 这四个属性的值
const admin = { ...results[0], password: '' }
或者
const admin = {
id: results[0].id,
name: results[0].name,
identify: results[0].identify
}
运行如下的命令,安装生成 Token
字符串的包:
npm i jsonwebtoken@8.5.1
在/router_handler/admin_handler.js
模块的头部区域,导入jsonwebtoken
包:
// 用这个包来生成 Token 字符串
const jwt = require('jsonwebtoken')
创建 config.js
文件,并向外共享 加密 和 还原 Token
的 jwtSecretKey
字符串:
module.exports = {
jwtSecretKey: 'zhanglijun',
}
将用户信息对象加密成 Token 字符串(/router_handler/admin_handler.js
):
// 导入配置文件
const config = require('../config')
// 生成 Token 字符串
const tokenStr = jwt.sign(admin, config.jwtSecretKey, {
expiresIn: '10h', // token 有效期为 10 个小时
})
将生成的 Token 字符串响应给客户端:
res.json({
status: 200,
message: '登录成功!',
// 为了方便客户端使用 Token,在服务器端直接拼接上 Bearer 的前缀
token: 'Bearer ' + tokenStr,
})
测试
admin_hadler.js
代码:
/**
* 在这里定义和登录注册相关的路由处理函数
* 供/router/admin.js模块进行调用
*/
const db = require('../db/index')
const bcrypt = require('bcryptjs')
const jwt = require('jsonwebtoken')
const config = require('../config')
// 注册用户的处理函数
exports.register = (req, res) => {
const adminInfo = req.body
if (!adminInfo.name || !adminInfo.password) {
return res.json({ status: 400, message: '用户名或密码为空' })
}
const sql = `select * from admin where name = ? `
db.query(sql, [adminInfo.name], (err, results) => {
if (err) {
return res.send({
status: 404,
message: err.message
})
}
if (results.length > 0) {
return res.status(400).json('用户名已被注册,请更改后重新注册!')
}
// 对用户的密码,进行 bcrype 加密,返回值是加密之后的密码字符串
adminInfo.password = bcrypt.hashSync(adminInfo.password, 10)
const sqlStr = 'insert into admin set ?'
db.query(sqlStr, { name: adminInfo.name, password: adminInfo.password, identify: adminInfo.identify }, (err, results) => {
if (err) {
return res.send({
status: 400,
message: err.message
})
}
if (results.affectedRows !== 1) {
return res.status(400).json('注册用户失败,请稍后再试')
}
res.json({
status: 200,
message: '注册成功',
name: adminInfo.name,
password: adminInfo.password,
identify: adminInfo.identify
})
})
})
}
// 登录的处理函数
exports.login = (req, res) => {
const adminInfo = req.body
const sql = `select * from admin where name = ?`
db.query(sql, adminInfo.name, (err, results) => {
if (err) {
return res.status(400).json(err)
}
if (results.length !== 1) {
return res.status(400).json('用户不存在')
}
const compareResult = bcrypt.compareSync(adminInfo.password, results[0].password)
if (!compareResult) {
return res.status(400).json('用户名或密码输入错误,请重新输入')
}
// const admin = { ...results[0], password: '' }
const admin = {
id: results[0].id,
name: results[0].name,
identify: results[0].identify
}
const tokenStr = jwt.sign(admin, config.jwtSecretKey, {
expiresIn: '10h', // token 有效期为 10 个小时
})
// 将生成的 Token 字符串响应给客户端
res.json({
status: 200,
message: '登录成功!',
// 为了方便客户端使用 Token,在服务器端直接拼接上 Bearer 的前缀
token: 'Bearer ' + tokenStr,
})
})
}