在mySQL中创建users表

image-20220123140254013

安装配置mysql模块2.18.1版本

image-20220123141124018

注册

实现步骤

1、检测表单数据是否合法

2、检测用户名是否被占用

3、对密码进行加密处理

4、插入新用户

检测表单数据是否合法

1、判断用户名和密码是否为空

//获取客户端提交到服务器的用户信息
const userinfo = req.body
//对表单中的数据,进行合法性的检验
if(!userinfo.username || !userinfo.password){
    return res.send({
        status:1,
        message:'用户名或密码不合法',
    })
}

检测用户名是否被占用

1、导入数据库模块

2、定义sql语句

//定义SQL语句查询用户名是否被占用
const sqlStr = 'select * from users where username=?'
//select语句results返回的是数组,因此判断时只需要看数组长度是否大于0
db.query(sqlStr,userinfo.username,(err,results)=>{
    if(err){
        return res.send({
            status:1,
            message:err.message,
        })
    }
    //判断用户名是否被占用
    if(results.length >0){
        return res.send({
            status:1,
            message:'用户名被占用,请更换其他用户名'
        })
    }
    //TODO:用户名可以使用
})

对密码进行加密

使用bcryptjs包进行加密,优点:

  • 加密之后的密码,无法被逆向破解
  • 同一明文密码多次加密,得到的加密结果各不相同

安装 bcryptjs包,2.4.3版本

调用 bcrypt.hashSync(明文密码,随机的长度)方法,对密码进行加密处理

//调用bcrypt.hashSync 对密码进行加密
userinfo.password=bcrypt.hashSync(userinfo.password,10)

插入新用户

//定义插入新用户的sql语句
const sql = 'insert into users set ?'
db.query(sql,{username:userinfo.username,password:userinfo.password},(err,results)=>{
    if(err){
        return res.send({
            status:1,
            message:err.message,
        })
    }
    //判断影响行数是否为1
    if(results.affectedRows !==1){
        return res.send({
            status:1,
            message:'注册用户失败,请稍后再试'
        })
    }
    //注册用户成功
    res.send({
        status:0,
        message:'注册成功!'
    })
})

优化代码

1、对res.send()进行优化

//在路由之前,封装res.cc函数
app.use((req,res,next)=>{
    //status 默认值为1,表示失败的情况
    //err都值,可能是一个错误对象,也可能是一个错误的描述字符串
    res.cc = function (err,status = 1) {
        res.send({
            status,
            message:err instanceof Error ? err.message : err
        })
    }
    next()
})
// return res.send({
//     status:1,
//     message:err.message,
// })
//修改后
return res.cc(err)

2、优化表单数据验证

使用第三方数据验证模块,来降低出错率、提高验证的效率与可维护性

  • 安装 hapi/joi 包,为表单中携带的每个数据项,定义验证规则:
npm install @hapi/joi@17.1.0
  • 安装 escook/express-joi 用来实现自动对表单数据进行验证的功能:
npm install escook/express-joi

创建文件

image-20220123205253522

写入规则

//定义 用户信息验证规则模块
const joi = require('@hapi/joi')

/**
 * string() 值必须是字符串
 * alphanum() 值只能包含字母和数字的字符串
 * min(length) 最小长度
 * max(length) 最大长度
 * required() 的值是必须项,不能为undefined
 * pattern(正则表达式) 值必须符合正则表达式的规则
 * **/

//用户名验证规则
const username = joi.string().alphanum().min(1).max(10).required()
//密码的验证规则
const password = joi.string().pattern(/^[\S]{6,12}]$/).required()

//注册和登录表单的验证规则对象
exports.reg_login_schema = {
    //表示需要对req.body 中的数据进行验证
    body :{
        username,
        password,
    },

}

修改/router/user.js 中的代码

const userHandler = require('../router_handler/user')

//导入验证表单数据的中间件
const expressJoi = require('@escook/express-joi')
//导入验证表单数据的验证规则对象
const { reg_login_schema } = require('../schema/user')

//注册新用户
// router.post('/reguser',userHandler.regUser)
//修改后
router.post('/reguser',expressJoi(reg_login_schema),userHandler.regUser)
//登录
// router.post('/login',userHandler.login)
router.post('/login',userHandler.login)

在app.js 的全局错误界别中间件中,捕获验证失败的错误,并把验证失败的结果响应给客户端:

const joi = require('@hapi/joi')
//定义错误级别的中间件
app.use(function (err,req,res,next){
    //数据验证失败导致的错误
    if(err instanceof joi.ValidationError) return res.cc(err)
    //未知错误
    res.cc(err)
})

登录

实现步骤

  1. 检测表单数据是否合法
  2. 根据用户名查询用户的数据
  3. 判断用户输入的密码是否争取
  4. 生成JWT的Token字符串

检测登录表单的数据是否合法

将 /router/user.js 中登录的路由代码修改

//登录
// router.post('/login',userHandler.login)
router.post('/login',expressJoi(reg_login_schema),userHandler.login)

根据用户名查询用户的数据

  • 接收表单数据
const userinfo = req.body
  • 定义SQL语句
const sql = 'select * from users where username=?'
  • 执行SQL语句
//接收表单数据
const userinfo = req.body
//定义SQL语句
const sql = 'select * from users where username=?'
//执行SQL语句
db.query(sql,userinfo.username,(err,results)=>{
    //执行SQL语句失败
    if(err) return res.cc(err)
    //执行SQL语句成功,但是获取到的数据条数不等于1
    if(results.length !== 1) return res.cc('登录失败!')

})

判断用户输入的面是否正确

核心思路:调用bcrypt.compareSync(用户提交的棉麻,数据库中的密码) 方法比较密码是否一直

返回值(true 表示一致,false 表示不一致)

//使用用户输入的密码,和数据库中存储的棉麻进行比较
const compareResult = bcrypt.compareSync(userinfo.password,results[0].password)

//如果对比的结果等于false,密码错误
if(!compareResult){
    return res.cc('登录失败')
}

生成JWT的Token字符串

通过ES6的高级语法,快速剔除 密码头像 的值

const user = {...results[0],password:'',user_pic:''}

安装Token字符串包

npm i jsonwebtoken@8.5.1

在handler/user.js中修改

//导入生成Token 的包
const jwt=require('jsonwebtoken')
//导入全局的配置文件
const config = require('../config')

//在服务器端生成Token字符串
//通过ES6语法快速剔除 密码 头像 敏感信息
const user = {...results[0],password:'',user_pic:''}
//对用户的信息进行加密,生成Token字符串
const tokenStr = jwt.sign(user,config.jwtSecretKey,{expiresIn:config.expiresIn})
//调用res.send()将Token响应给客户端
res.send({
    status:0,
    message:'登录成功!',
    token:'Bearer '+tokenStr
})

创建config.js制定全局配置

//这是一个全局的配置文件

module.exports={
    //加密和解密Token的密钥
    jwtSecretKey:'lanlan ^v^',
    expiresIn:'10h'
}

配置解析Token 的中间件

安装包

npm install express-jwt@5.3.3

在app.js 中注册路由之前,配置解析Token的中间件

//导入配置文件
const config = require('./config')
//解析token的中间件
const expressJWT = require('express-jwt')
//使用 .unless({path:[/^\/api\//]})指定哪些接口不需要进行Token 的身份认证
app.use(expressJWT({secret:config.jwtSecretKey}).unless({path:[/^\/api\//]}))

在app.js中的错误级别中间件中,捕获并处理Token认证失败后的错误