一、创建项目
1.初始化包管理配置文件:
npm init -y
2.安装espress
npm i express@4.17.1
3.创建app.js作为整个项目的入口文件,导入需要的模块,并启动3007端口
// 导入 express 模块
const express = require('express')
// 创建 express 的服务器实例
const app = express()
// 调用 app.listen 方法,指定端口号并启动web服务器 app.listen(3007, function () { console.log('api server running at http://127.0.0.1:3007') })
4.配置跨域请求,需要用到cors
4.1、安装cors
npm i cors
4.2、在app.js中导入cors中间件,并注册成全局中间件
// 导入 cors 中间件
const cors = require('cors')
// 将 cors 注册为全局中间件 app.use(cors())
5、配置解析表单数据的中间件
配置解析 application/x-www-form-urlencoded 格式的表单数据的中间件,默认情况下,如果不配置解析表单数据中间件,则 req.body 默认等于 undefined
app.use(express.urlencoded({ extended: false }))
6、初始化用户路由模块
6.1、创建user.js文件,作为用户的路由模块
const express = require('express')
const router = express.Router()
// 导入用户路由处理函数模块
const userHandler = require('../router_handler/user')
// 注册新用户
router.post('/reguser', userHandler.regUser)
// 登录
router.post('/login', userHandler.login) module.exports = router
6.2、将用户路由的处理函数抽出,目的是为了路由的纯粹性,便于维护
路由的处理函数:
// 注册用户的处理函数
exports.regUser = (req, res) =>{
res.send('reguser OK')
}
// 登录的处理函数
exports.login = (req, res) => {
res.send('login OK')
}
二、实现登录和注册的功能
1.在MySQL Workbench 创建用户存储数据表单
2.在项目中安装并且配置MySQL的模块
2.1安装MySQL模块
npm i mysql
2.2、创建新新文件,并且自定义模块中创建数据库的连接对象
// 导入 mysql 模块
const mysql = require('mysql')
// 创建数据库连接对象
const db = mysql.createPool({
host: '127.0.0.1',
user: 'root',
password: '******',
database: 'my_db_01',
})
// 向外共享 db 数据库连接对象
module.exports = db
3.实现注册功能
3.1检测表单数据是否合法
首先先判断客户端传送来的用户名和密码是否为空
// 接收表单数据
const userinfo = req.body
// 判断数据是否合法
if (!userinfo.username || !userinfo.password) {
return res.send({
status: 1,
message: '用户名或密码不能为空!'
})
}
3.2检测用户是否被占用
3.2.1导入数据库的操作模块
const db = require('../db/index')
3.2.2 定义SQL语句,注意?是占位符
const sql = `select * from ev_users where username=?`
3.2.3 执行SQL语句,并且根据返回的结果判断用户名是否被占用
db.query(sql, [userinfo.username],
function (err, results) {
// 执行 SQL 语句失败
if (err) { return res.send({
status: 1,
message: err.message
}) }
// 用户名被占用
if (results.length > 0)
{ return res.send({
status: 1,
message: '用户名被占用,请更换其他用户名!'
}) }
// TODO: 用户名可用,继续后续流程... })
如果用户可用,先对用户输入的密码进行一个加密的操作,使用bcryptjs对用户的密码进行加密
优点:
加密之后的密码,无法被逆向破解
同一明文密码多次加密,得到的加密结果各不相同,保证了安全性
3.3 导入bcryptjs
npm i bcryptjs
3.3.1在用户路由的文件,导入bcryptjs
const bcrypt = require('bcryptjs')
3.3.2 在注册用户的处理函数中,确认用户名可用之后,调用 bcrypt.hashSync(明文密码, 随机盐的长度) 方法,对用户的密码进行加密处理:
// 对用户的密码,进行 bcrype 加密,返回值是加密之后的密码字符串
userinfo.password = bcrypt.hashSync(userinfo.password, 10)
4 插入新用户
4.1 定义SQL语句
const sql = 'insert into ev_users set ?'
4.2 执行SQL语句,插入新用户
db.query(sql,{username:userinfo.username,password:userinfo.password},(err,results) => {
// 判断sql语句是否执行成功
// if(err) return res.send({ status:1, message: err.message})
if(err) return res.cc(err)
// 判断影响函数是否为一
// if(results.affectedRows !==1 ) return res.send({status:1, message:'注册用户失败'})
if(results.affectedRows !==1 ) return res.cc('注册用户失败')
// 注册用户成功
// res.send({status:0, message:'注册用户成功'})
res.cc('注册成功')
})
注册成功的效果:
序号9就是刚刚注册成功的用户
- 在处理函数中,需要多次调用 res.send() 向客户端响应 处理失败 的结果,为了简化代码,可以手动封装一个 res.cc() 函数
5.1 在app.js 中在所有路由之前声明一个全局中间件,对res对象挂载一个res.cc()函数
// 封装一个res.cc函数,用于每次如果有错误都会执行一次,便于复用
app.use((req,res,next) => {
// status默认值为1,
res.cc = function(err,status = 1){
res.send({
status,
message:err instanceof Error ? err.message:err
})
}
next()
})
6.优化表单的数据认证
单纯的使用 if...else... 的形式对数据合法性进行验证,效率低下、出错率高、维护性差。因此,推荐使用第三方数据验证模块,来降低出错率、提高验证的效率与可维护性
6.1可以使用安装 joi 包,为表单中携带的每个数据项,定义验证规则
安装 @escook/express-joi中间件,来实现自动对表单数据进行验证的功能
npm install @hapi/joi@17.1.0,joi
6.2创建验证规则的文件user.js
// 导入验证规则的包
const joi = require('joi')
// 定义用户名和密码的验证规则
// string必须是字符串,alphanum只包含a-z,A_Z,0——9; min最小长度,max最大长度,required必填项,pattern(正则表达式)
const username = joi.string().alphanum().min(1).max(10).required()
const password = joi.string().pattern(/^[\S]{6,12}$/).required()
// 定义验证规则和登录表单数据的规则对象
exports.reg_login_schema = {
body:{
username,
password
}
}
7.注册新用户的代码优化如下:
// 1.导入验证表单的中间件
const expressjoi = require('@escook/express-joi')
// 2.导入需要验证规则对象
const {reg_login_schema} = require('../schemn/user')
// 注册用户 并且调用验证规则的对象;处理函数
router.post('/reguser',expressjoi(reg_login_schema),use_handle.regUser)
7.在app.js 的全局错误级别中间件中,捕获验证失败的错误,并把验证失败的结果响应给客户端:
// 定义错误的中间件
app.use((err,req,res,next) => {
if(err instanceof joi.ValidationError) return res.cc(err)
})
8,实现登录功能
8.1.编写登录接口,并且验证表单的数据是否合法
router.post('/login',expressjoi(reg_login_schema),use_handle.login)
8.2.接收表单数据
const userinfo = req.body
8.3.定义SQL语句,根据用户名查询数据
const sql = `select * from ev_users where username=?`
8.4.执行SQL语句
// 执行SQL语句
db.query(sql,userinfo.username,(err,results) => {
// 执行SQL失败
if(err) return res.cc(err)
// 执行SQL语句成功,但是获取的数据条数不等于1
if(results.length !==1) return res.cc('登录失败')
//TODO 成功之后的操作
})
8.5.成功执行之后,需要验证客户端传过来的密码是否正确
核心实现思路:调用 bcrypt.compareSync(用户提交的密码, 数据库中的密码) 方法比较密码是否一致,
返回值是布尔值(true 一致、false 不一致)
const compareResult = bcrypt.compareSync(userinfo.password,results[0].password)
if(!compareResult) return res.cc("登录失败,密码错误")
// TODO:登录成功,生成 Token 字符串
8.6.创建 config.js 文件,并向外共享加密和还原 Token 的 jwtSecretKey 字符串
module.exports = {
// 加密和解密token的密钥
jwtSecretKey:'bee like qianduan #',
// token的有效期
expiresIn:'10h'
}
8.7生成token字段,核心注意点:在生成 Token 字符串的时候,一定要剔除密码和头像的,密码过于重要不建议放在token里面,而头像的字段过于太大,也不适合放入token字段
// 生成Token字符串 剔除密码和头像
// 1.获取用户信息,剔除重要信息
const user = {...results[0],password:'',user_pic:''}
// 2.对用户信息进行加密,生成Token字段
const tokenStr = jwt.sign(user,config.jwtSecretKey,{expiresIn:config.expiresIn})
// 3.调用res.send 将token响应给客户端
res.send({
status:0,
message:'登录成功',
token: 'Bearer ' + tokenStr
})
实现的效果图:

登录成功之后,返回了一段token字段,注意设置的token有效期为10小时,用于登录有权限的接口
9.配置解析token的中间件
9.1安装解析token的中间件
npm i express-jwt
9.2需要在路由前,设置一个解析token的中间件
// 在路由之前配置解析的token中间件
const expressJWT = require('express-jwt')
const config = require('./config')
// 注册成全局中间件 设置除了api以外的接口都需要token认证
app.use(expressJWT({secret:config.jwtSecretKey}).unless({path:[/^\/api/]}))
9.3在app.js 中的错误级别中间件里面,捕获并处理 Token 认证失败后的错误:
// 捕获身份认证失败的错误
if (err.name === 'UnauthorizedError') return res.cc('身份认证失败!')
接入有权限接口的时候效果图:
本文介绍了如何使用Node.js创建后台管理项目,包括初始化项目、配置跨域、解析表单数据、用户路由模块的创建,以及实现登录和注册功能。在注册过程中,详细讲解了如何使用bcryptjs加密密码,同时提到了数据验证的重要性,并引入了joi进行表单验证。登录功能的实现包括了JWT Token的生成与验证,确保了接口的安全性。

8428

被折叠的 条评论
为什么被折叠?



