Express框架开发接口之登录与注册API

news2024/11/17 0:50:52

我们利用node+express+mysql开发接口,对数据库数据进行简单增、删、查改等操作。

接口是什么?

接口是 前后端通信的桥梁

简单理解:一个接口就是 服务中的一个路由规则 ,根据请求响应结果 接口的英文单词是 API (Application Program Interface),所以有时也称之为 API 接口

这里的接口指的是『数据接口』, 与编程语言(Java,Go 等)中的接口语法不同

下面做一个书城商店api案例(大概有个人信息模块,书产品分类模块,购物车模块,信息资讯),利用宝塔工具将api部署到服务器,利用cdn访问图片,富文本编辑器使用等。(vue3)一边做uniapp开发小程序一边写接口,可能会写后台管理系统(看时间)。为了节约时间不用ts😅😅😅,后面有时间再用ts优化ᥬ🌚᭄。

工具

数据库可视化

377ee077acfddce7329f78fe733766bd.png

接口测试工具Postman

6559a752499dd3f0d43283f099e68937.png

1、初始化

我们这里利用node+express+mysql开发一个简单的书城商店API。后面会使用result API规范接口

1、创建项目

新建文件夹server 安装依赖

npm init -y

6e049a1919b7e33f54c7b03f48c60020.png

安装依赖express

npm i express

新建app.js作为入口文件创建服务器实例

// 导入 express 模块

const express = require('express')

// 创建 express 的服务器实例

const app = express()

// write your code here...
// 调用 app.listen 方法,指定端口号并启动web服务器

app.listen(8000, function () {
  console.log('server running at http://127.0.0.1:8000')
})

a3095b9a103c0f3a633d466e98933760.png

安装nodemon

npm i nodemon

2、配置跨域依赖

npm i cors

在app.js中导入并配置cors中间件:

// 导入 cors 中间件

const cors = require('cors')

// 将 cors 注册为全局中间件

app.use(cors())

3、配置解析表单数据的中间件

通过如下的代码,配置解析application/x-www-form-urlencoded格式的表单数据的中间 件:

app.use(express.urlencoded({ extended: false }))

4、初始化路由

  1. 在项目根目录中,新建 router 文件夹,用来存放所有的 路由 模块

路由模块中,只存放客户端的请求与处理函数之间的映射关系

  1. 在项目根目录中,新建 router_handler 文件夹,用来存放所有的 路由处理函数模块

路由处理函数模块中,专门负责存放每个路由对应的处理函数

47861b750dfa003fcd7e9e1fa394d613.png

例如:新建用户user路由模块

42f21ed7b1abb7f9b4f06961fc107953.png

在入口文件中注册路由

4c8796c565d1a8dbb902ef718f37deab.png

访问接口(确保服务在运行),没有运行访问不了服务

ee382e04e57f2fcaefa136de696a495f.png

f0f2ced84a6f1637735d8eb625ca7c70.png

2.项目初始化

我们需要在数据库进行存储数据,对数据进行操作,这里使用Mysql,后面改进会使用一个更好用的MongoDB

1.配置MySQL

npm i mysql

2.创建数据库book,并创建一个用户users表格

新建数据库

c527afdb523107a7e3227e4e8ab6c02f.png

4db5b662431e36cc1b0236df70e87a34.png

c26a7863de4746667f9cf818e70ea164.png

601ca0d9e7f9c6425c67a092508d2621.png

创建users表格

cc34d3de00b01e49a74a1df60bfb2324.png

d4fbf17a0a36167b341488cf12b757a0.png

2.实现连接数据库功能

新建db文件夹在其创建index.js连接数据库

6754eed062c257f9dd2f5577c282a407.png

// 导入 mysql 模块

const mysql = require('mysql')

// 创建数据库连接对象

const db = mysql.createPool({
    host: '127.0.0.1',
    user: 'root',
    password: 'root',
    database: 'book',
})

// 向外共享 db 数据库连接对象

module.exports = db

3.实现注册功能

1.实现步骤

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

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

3. 对密码进行加密处理

4. 插入新用户

2.检测表单数据是否合法

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

// 接收表单数据

const userinfo = req.body

// 判断数据是否合法

if (!userinfo.username || !userinfo.password) {
  return res.send({ status: 1, message: '用户名或密码不能为空!' })
}

2 检测用户名是否被占用

1. 导入数据库操作模块:

const db=require('../db/index')
2. 定义 SQL 语句:

2.定义SQL语句

const sql=`select * from users where username=?`

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: 用户名可用,继续后续流程...

})

4.对密码进行加密处理

为了保证密码的安全性,不建议在数据库以明文的形式保存用户密码,推荐对密码进行加密 存储

在当前项目中,使用bcryptjs对用户密码进行加密,优点:

加密之后的密码,无法被逆向破解

同一明文密码多次加密,得到的加密结果各不相同,保证了安全性

npm i bcryptjs

在/router_handler/user.js中,导入bcryptjs:

const bcrypt=require('bcryptjs')

在注册用户的处理函数中,确认用户名可用之后,调用bcrypt.hashSync(明文密码, 随机盐的 长度)方法,对用户的密码进行加密处理:

// 对用户的密码,进行 bcrype 加密,返回值是加密之后的密码字符串 
userinfo.password=bcrypt.hashSync(userinfo.password, 10)

5.插入新用户

  1. 定义插入用户的 SQL 语句:
const sql='insert into users set ?'

调用db.query()执行 SQL 语句,插入新用户:

db.query(sql, { username: userinfo.username, password: userinfo.password }, function 
(err, results) {
  // 执行 SQL 语句失败

  if (err) return res.send({ status: 1, message: err.message })
  // SQL 语句执行成功,但影响行数不为 1

  if (results.affectedRows !== 1) {
    return res.send({ status: 1, message: '注册用户失败,请稍后再试!' })
 }
  // 注册成功

  res.send({ status: 0, message: '注册成功!' })
})

全部代码

/**
 * 在这里定义和用户相关的路由处理函数,供 /router/user.js 模块进行调用

 */
const db = require('../db/index')
const bcrypt = require('bcryptjs')

// 注册用户的处理函数

exports.regUser = (req, res) => {
    // 接收表单数据

    const userinfo = req.body

    // 判断数据是否合法

    if (!userinfo.username || !userinfo.password) {
        return res.send({ status: 1, message: '用户名或密码不能为空!' })
    }
    const sql = `select * from users where username=?`
    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: '用户名被占用,请更换其他用户名!' })
        }
        // 对用户的密码,进行 bcrype 加密,返回值是加密之后的密码字符串

        userinfo.password = bcrypt.hashSync(userinfo.password, 10)
        const sql = 'insert into users set ?'
        db.query(sql, { username: userinfo.username, password: userinfo.password }, function
            (err, results) {
            // 执行 SQL 语句失败

            if (err) return res.send({ status: 1, message: err.message })
            // SQL 语句执行成功,但影响行数不为 1

            if (results.affectedRows !== 1) {
                return res.send({ status: 1, message: '注册用户失败,请稍后再试!' })
            }
            // 注册成功

            res.send({ status: 0, message: '注册成功!' })
        })



    })

}

// 登录的处理函数

exports.login = (req, res) => {
    res.send('login OK')
}

为了方便测试接口我们允许其他字段为空

04cecc94cba46d5c4eefb5cc2ede5cc2.png

967b62c1c79fef2d377a63e266480ee4.png

一开始表里没有数据

17b6f96eea614a5458b453d26fec0b9b.png

我们去测试注册功能(一定要配置解析表单数据的中间件)

2fd884b8470cc529d18db8e01f94301d.png

9250b17c3b60a7b3ff63a5bfe91a1913.png

7c8f2160658805a65c5221bb87bc439f.png

注册功能完成

4.实现登录功能

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

返回值是布尔值(true 一致、false 不一致)

 const userinfo = req.body
    const sql = `select * from users where username=?`
    db.query(sql, userinfo.username, function (err, results) {
        // 执行 SQL 语句失败

        if (err) return res.cc(err)
        // 执行 SQL 语句成功,但是查询到数据条数不等于 1

        if (results.length !== 1) return res.send('登录失败!')
        // TODO:判断用户输入的登录密码是否和数据库中的密码一致
        // 拿着用户输入的密码,和数据库中存储的密码进行对比

        const compareResult = bcrypt.compareSync(userinfo.password, results[0].password)

        // 如果对比的结果等于 false, 则证明用户输入的密码错误

        if (!compareResult) {
            return res.send('登录失败!')
        }
        return res.send('登录成功!')



    })

efa2b7df29334d402d4ff2406e59bccf.png

基础登录接口实现

5、认识JWT

首先我们需要认识token

5.1token 是什么

token 是服务端生成并返回给 HTTP 客户端的一串加密字符串, token 中保存着 用户信息

5.2 token 的作用

实现会话控制,可以识别用户的身份,主要用于移动端 APP

5.3 token 的工作流程

5.4 token 的特点

服务端压力更小 数据存储在客户端 相对更安全 数据加密 可以避免 CSRF(跨站请求伪造) 扩展性更强 服务间可以共享 增加服务节点更简单

5.5生成 JWT 的 Token 字符串

JWT(JSON Web Token )是目前最流行的跨域认证解决方案,可用于基于token的身份验证 JWT 使 token 的生成与校验更规范

我们可以使用jsonwebtoken 包来操作 token

安装生成 Token 字符串的包:

npm i jsonwebtoken

在/router_handler/user.js模块的头部区域,导入jsonwebtoken包:

// 用这个包来生成 Token 字符串 
constjwt=require('jsonwebtoken')

创建config.js文件,并向外共享加密和还原 Token 的jwtSecretKey字符串:

module.exports= {  jwtSecretKey: 'itheima No1. ^_^', } 

将用户信息对象加密成 Token 字符串:

// 导入配置文件

const config = require('../config')

// 生成 Token 字符串

const tokenStr = jwt.sign(user, config.jwtSecretKey, {
  expiresIn: '10h', // token 有效期为 10 个小时

})

将生成的 Token 字符串响应给客户端:

res.send({
  status: 0,
  message: '登录成功!',
  // 为了方便客户端使用 Token,在服务器端直接拼接上 Bearer 的前缀

  token: 'Bearer ' + tokenStr,
})

5.6配置解析 Token 的中间件

安装解析 Token 的中间件

npm i express-jwt

在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 认证失败后的错误:

// 错误中间件

app.use(function (err, req, res, next) {
  // 省略其它代码...

  // 捕获身份认证失败的错误

  if (err.name === 'UnauthorizedError') return res.cc('身份认证失败!')
  // 未知错误...

})

分析:

是Express版本和express-jwt的版本不兼容的可能比较大,因为这两个版本都是安装最新版本的,很可能会出现这个问题,通过尝试修改版本并解决了报错问题。
下面附上修改后的版本:

"express": "^4.17.1",
 "express-jwt": "^5.3.3",
 "jsonwebtoken": "^8.5.1",

重新下载依赖

npm i

认识

6、实现数据库命令增删查改的封装

为什么封装?

这样我们一直写数据库操作,代码重复太多,写项目眼花缭乱

封装目的:解决代码冗余

首先我们需要理解ORM (可以在我ORM介绍博客中招) 开发效率快

**先学一下ORM

将db/index.js修改为如下代码

const mysql = require('mysql');
// 数据库连接设置
let db = {
    host: 'localhost',//数据库地址
    port: '3306',
    user: 'root',//用户名,没有可不填
    password: 'root',//密码,没有可不填
    database: 'book'//数据库名称
}

let options = {};
let tableSQL = '';
let isConnect = false;

function Model(name, option) {
    this.name = name;
    this.option = option;
};

/**
* @description: 查询数据
* @param {} options:可选参数
* @param {Function} callback :(req,results)=>{}
*/
Model.prototype.find = function (options, callback) {
    if (!isConnect) {
        console.log(options.constructor);
        this.connect(err => {
            isConnect = true;
            var str = '';
            if (!callback) {
                str = `select * from ${this.name}`;
                callback = options;
            } else if (options.constructor == Array) {
                str = `select ${options.join()} from ${this.name}`;
            } else if (options.constructor == Object) {
                str = `select ${options.arr.join()} from ${this.name} where ${options.where}`;
            } else {
                str = `select * from ${this.name} where ${options}`;
            };
            //console.log(str);
            connection.query(str, (error, results, fields) => {
                callback(error, results, fields);
            });
            return this;
        })
    } else {

        var str = '';
        if (!callback) {
            str = `select * from ${this.name}`;
            callback = options;
        } else if (options.constructor == Array) {
            str = `select ${options.join()} from ${this.name}`;
        } else {
            str = `select * from ${this.name} where ${options}`;
        };
        //console.log(str);
        connection.query(str, (error, results, fields) => {
            callback(error, results, fields);
        });
        return this;
    }

};

/**
* @description: 分页查询
* @param {Object} options :   { where:查询条件, number: 当前页数 , count : 每页数量 }
* @return: 
*/
Model.prototype.limit = function (options, callback) {
    var str = '';
    if (!options.where) {
        str = `select * from ${this.name} limit ${(options.number - 1) * options.count},${options.count}`;
    } else {
        str = str = `select * from ${this.name} where ${options.where} limit ${(options.number - 1) * options.count},${options.count}`;
    };
    console.log(str);
    connection.query(str, (error, results, fields) => {
        callback(error, results, fields);
    });
    return this;
};

/**
* @description: 插入数据
* @param {Object} obj:对象或者数组
* @param {Function} callback :(req,results)=>{}
*/
Model.prototype.insert = function (obj, callback) {
    if (!isConnect) {
        this.connect(err => {
            if (err) {
                throw err;
            } else {
                connection.query(tableSQL, (error, results, fields) => {
                    if (Array.isArray(obj)) {
                        for (var i = 0; i < obj.length; i++) {
                            this.insertObj(obj[i], callback)
                        }
                    } else {
                        this.insertObj(obj, callback)
                    }
                });

            }
        });
    } else {
        if (Array.isArray(obj)) {
            for (var i = 0; i < obj.length; i++) {
                this.insertObj(obj[i], callback)
            }
        } else {
            this.insertObj(obj, callback)
        }
    }

};

Model.prototype.insertObj = function (obj, callback) {
    let keys = [];
    let values = '';
    for (var key in obj) {
        keys.push(key);
        values += `"${obj[key]}",`;
    };
    values = values.replace(/,$/, '');
    let str = `INSERT INTO ${this.name} (${keys.join()}) VALUES (${values})`;
    connection.query(str, (error, results, fields) => {
        callback(error, results);
    });
}

/**
* @description: 更新数据
* @param {Object} option:可选参数 更新条件
* @param {Object} obj: 修改后的数据 
* @param {Function} callback :(req,results)=>{}
*/
Model.prototype.update = function (option, obj, callback) {
    let str = '';
    if (arguments.length == 2) {
        callback = obj;
        obj = option;
        str = `UPDATE ${this.name} SET `;
        for (var key in obj) {
            str += `${key}='${obj[key]}', `;
        };
        str = str.replace(/(, )$/, '');
    } else {
        str = `UPDATE ${this.name} SET `;
        for (var key in obj) {
            str += `${key}='${obj[key]}', `;
        };
        str = str.replace(/(, )$/, '');
        str += ` where ${option}`;
    };

    console.log(str);
    connection.query(str, (error, results, fields) => {
        callback(error, results, fields);
    });
    return this;

};

/**
* @description: 删除数据
* @param {Object} option:可选参数 删除条件
* @param {Function} callback :(req,results)=>{}
*/
Model.prototype.delete = function (option, callback) {
    var str = '';
    if (!callback) {
        str = `delete from ${this.name}`;
        callback = option;
    } else {
        str = `delete from ${this.name} where ${option}`;
    };
    console.log(str);
    connection.query(str, (error, results, fields) => {
        callback(error, results, fields);
    });
    return this;
};

/**
* @description: 执行sql语句
* @param {String} str : sql语句
* @param {Function} callback :(req,results)=>{}
*/
Model.prototype.sql = function (str, callback) {
    connection.query(str, (error, results, fields) => {
        callback(error, results, fields);
    });
    return this;
};

/**
* @description: 删除model表格 (慎用!)
* @param {type} 
* @return: 
*/
Model.prototype.drop = function (callback) {
    connection.query(`DROP TABLE ${this.name}`, (error, results, fields) => {
        callback(error, results, fields);
    });
    return this;
};

//连接检测
Model.prototype.connect = function (callback) {
    let p1 = new Promise((resolve, reject) => {
        connection.connect((err) => {
            if (err) {
                //console.log(err.stack);
                //console.log(err);//42000 数据库不存在  28000账号错误
                //console.log(err.sqlState);//42000 数据库不存在  28000账号错误
                reject(err);
            } else {
                resolve();
            }
        });
    });

    p1.then(() => {
        callback(null);
    }, err => {
        if (err.sqlState == 42000) {
            createDatabase(callback);
        } else if (err.sqlState == 28000) {
            callback('数据库账号或密码错误');
        } else {
            callback(err);
        }
    });
};

//创建数据库
let createDatabase = function (callback) {
    let p2 = new Promise((resolve, reject) => {
        connection = mysql.createConnection({
            host: options.host,//数据库地址
            port: options.port,//端口号
            user: options.user,//用户名,没有可不填
            password: options.password,//密码,没有可不填
        });
        connection.connect((err) => {
            //if (err) throw error;
            if (err) {
                reject(err);
            } else {
                resolve();
            }
        });
    });

    let p3 = new Promise((resolve, reject) => {
        connection.query(`CREATE DATABASE ${options.database}`, (err, results, fields) => {
            //if (error) throw error;
            if (err) {
                reject(err);
            } else {
                resolve();
            }

        });
    });

    let p4 = new Promise((resolve, reject) => {
        connection.query(`use ${options.database}`, (err, results, fields) => {
            if (err) {
                reject(err);
            } else {
                resolve();
            }
        });
    });

    let pAll = Promise.all([p2, p3, p4]);

    pAll.then(() => {
        callback(null);
    }).catch((err) => {
        callback(err);
    });
}



let orm = {
    /**
    * @description:连接数据库
    * @param {String} host: 主机名 默认localhost
    * @param {Number} port: 端口号 默认3306
    * @param {String} user: 用户名 
    * @param {String} password: 密码 
    * @param {String} database: 数据库名称 默认og
    * @return: 
    */
    connect: function ({ host = 'localhost', port = 3306, user = '', password = '', database = 'og' }) {
        databaseName = database;//全局存储当前数据库名称

        options = {
            host,//数据库地址
            port,//端口号
            user,//用户名,没有可不填
            password,//密码,没有可不填
            database//数据库名称
        };
        connection = mysql.createConnection(options);

    },
    /**
    * @description:创建model (表格模型对象)
    * @param {String} name:表格名称
    * @param {Object} options:表格数据结构
    * @return: Model对象:负责数据库增删改查
    */
    model: function (name, options) {
        let str = 'id int primary key auto_increment, ';
        for (var key in options) {
            if (options[key] == Number) {
                str += `${key} numeric,`;
            } else if (options[key] == Date) {
                str += `${key} timestamp,`;
            } else {
                str += `${key} varchar(255),`;
            }
        };
        str = str.replace(/,$/, '');
        //console.log(`CREATE TABLE ${name} (${str})`);
        //console.log(str);
        tableSQL = `CREATE TABLE ${name} (${str})`;
        return new Model(name, options);
    }
};

orm.connect(db);



module.exports = orm;

新建文件夹handleDB/index.js数据库操作进行封装

const db = require('../db/index')

function handleDB(res, tableName, methodName, errorMsg, n1, n2) {
    //数据库操作
    let Model = db.model(tableName);  // 表映射为模型, 参数是要操作的这个表的表名
    let results;  //results就收查询到的数据
    try {
        results = new Promise((resolve, reject) => {
            // Model.find("id>=15",(err,data)=>{   //直接调用不封装
            if (!n1) {
                Model[methodName]((err, data) => {    //封装的时候使用这种格式 
                    if (err) reject(err);   // 失败的时候调用reject()
                    resolve(data);    //成功的时候调用resolve() 
                });
                return
            }
            //能够给执行到这里说明n1已经传进来了!
            if (!n2) {
                Model[methodName](n1, (err, data) => {    //封装的时候使用这种格式 
                    if (err) reject(err);   // 失败的时候调用reject()
                    resolve(data);    //成功的时候调用resolve() 
                });
                return
            }
            //能够给执行到这里说明n1和n2已经传进来了!
            Model[methodName](n1, n2, (err, data) => {    //封装的时候使用这种格式 
                if (err) reject(err);   // 失败的时候调用reject()
                resolve(data);    //成功的时候调用resolve() 
            });

        })
    } catch (err) {
        console.log(err); // 给后台看到的
        res.send({ errMsg: errorMsg }); //给前端送过去的
        return
    }

    return results
}

module.exports = handleDB

完善获取数据库数据的写法

async+await版本

app.get("/",(req, res)=>{

    (async function(){
        let Student = db.model("students");  //获取学生表模型
        let results = await new Promise((resolve,reject)=>{
            Student.find('id>3',(err,data)=>{
                if(err)reject(err);
                resolve(data);
            });    
        })

        res.send(results);
    })();
})

带捕获异常的版本

(async function(){
    let Student = db.model("students");  //获取学生表模型
    let results 
    try{
        results = await new Promise((resolve,reject)=>{
            Student.find('id>3',(err,data)=>{
                if(err)reject(err);
                resolve(data);
            });    
        })
    }catch(err){
        console.log(err);
        res.send({errmsg:"数据库查询出错"})
        return
    }
    res.send(results);
})();

测试(注意先把登录和注册接口注释掉)

7、注册和登录功能的优化

对注册逻辑代码进行修改

修改代码为

// 注册用户的处理函数

exports.regUser = (req, res) => {
    (async function () {
        // 1.接收表单数据
        console.log(req);
        const userinfo = req.body
        console.log(userinfo);
        // 2.判断数据是否合法
        if (!userinfo.username || !userinfo.password) {
            return res.send({ status: 1, message: '用户名或密码不能为空!' })
        }
        // 3.查询数据库,看看是否存在这个用户名
        let result = await handleDB(res, "users", "find", "users数据库查询出错", `username="${userinfo.username}"`)
        //username="${userinfo.username}"这里注意加引号!!!
        // 4.如果已经存在,返回用户名已经被注册 return
        if (result.length > 0) {
            res.send({ status: 1, message: "用户名已经被注册,请更换其他用户名!" });
            return
        }
        // 5、不存在,则往数据库中新增加一条记录
        // 对用户的密码,进行 bcrype 加密,返回值是加密之后的密码字符串
        userinfo.password = bcrypt.hashSync(userinfo.password, 10)
        let result2 = await handleDB(res, "users", "insert", "users数据库新增出错", {
            username: userinfo.username,
            password: userinfo.password,
        })
        // SQL 语句执行成功,但影响行数不为 1
        if (result2.affectedRows !== 1) {
            return res.send({ status: 1, message: '注册用户失败,请稍后再试!' })
        }
        res.send({ status: 0, message: '注册成功!' })
    })();
}

对登录逻辑代码进行修改

原来代码

  db.query(sql, userinfo.username, function (err, results) {
        // 执行 SQL 语句失败
        if (err) return res.cc(err)
        // 执行 SQL 语句成功,但是查询到数据条数不等于 1
        if (results.length !== 1) return res.send('登录失败!')
        // TODO:判断用户输入的登录密码是否和数据库中的密码一致
        // 拿着用户输入的密码,和数据库中存储的密码进行对比
        const compareResult = bcrypt.compareSync(userinfo.password, results[0].password)
        // 生成 Token 字符串
        // 剔除完毕之后,user 中只保留了用户的 id, username, nick_name, email等 属性的值
        const user = { ...results[0], password: '', }
        const tokenStr = jwt.sign(user, config.jwtSecretKey, {
            expiresIn: '10h', // token 有效期为 10 个小时

        })
        // 如果对比的结果等于 false, 则证明用户输入的密码错误
        if (!compareResult) {
            return res.send('登录失败!')
        }
        return res.send({
            status: 0,
            message: '登录成功!',
            // 为了方便客户端使用 Token,在服务器端直接拼接上 Bearer 的前缀

            token: 'Bearer ' + tokenStr,
        })




    })

修改后代码

// 登录的处理函数

exports.login = (req, res) => {
    (async function () {
        // 1、获取参数,判空  (用户名、密码)
        let { username, password } = req.body
        if (!username || !password) {
            res.send({ status: 1, message: "缺少必传参数" });
            return
        }
        // 2、查询数据库,验证手机号码是不是已经注册了
        let result = await handleDB(res, "users", "find", "users数据库查询出错", `username="${username}"`);
        // 3、如果没有注册,则返回用户名未注册,登录失败 return
        if (result.length == 0) {
            res.json({ status: 1, message: "用户名未注册,无法登录" });
            return
        }
        // 4、校验密码是否正确,如果不正确 就return
        // 拿着用户输入的密码,和数据库中存储的密码进行对比
        const compareResult = bcrypt.compareSync(password, result[0].password)
        // 如果对比的结果等于 false, 则证明用户输入的密码错误
        if (!compareResult) {
            return res.send({ status: 1, message: '用户名或者密码错误,登录失败!' })
        }
        // 剔除完毕之后,user 中只保留了用户的 id, username, 等 属性的值
        const user = { ...result[0], password: '', }
        const tokenStr = jwt.sign(user, config.jwtSecretKey, {
            expiresIn: '10h', // token 有效期为 10 个小时

        })
        return res.send({
            status: 0,
            message: '登录成功!',
            // 为了方便客户端使用 Token,在服务器端直接拼接上 Bearer 的前缀
            token: 'Bearer ' + tokenStr,
        })

    })();


}

在配置cookie和session获取登录用户id,保存在会话之中差不多登录和注册接口功能基本完成了。

8.添加cookie和session的配置

(详细了解看会话控制篇章)

先安装cookie-parser和cookie-session :

npm add cookie-parser
npm add cookie-session

app.js中添加设置:

const cookieParser = require('cookie-parser');
const cookieSession = require('cookie-session');

// cookie的注册
app.use(cookieParser());  
// session的注册
app.use(cookieSession({
    name:"my_session",
    keys:["%$#^&^%&TSFR#$TRGDRG$%GFDG%^$#%#^GFDGRDHG$#@^Y%"],
    maxAge:1000*60*60*24*2    //过期时间设置为2天
}));

// 后面就可以通过 req.session[属性名] = 值来 设置session了

测试

设置session用户id

在测试接口中

获取session中的用户id

这样写前端访问其它接口时访问不到已经在session设置的用户ID,我忘了怎么解决了,后面写项目遇到再解决吧。

修改登录功能如下:

因为我们不可能将密码暴露,所以我们需要将密码设为空 password:' '

最后我们再完善一下数据库

登录注册功能实现完毕,接下里写个人信息模块

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1142698.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

分支预测机制

在Linux中&#xff0c;__predict_false和__predict_true是用于优化分支预测的宏。这些宏通过向编译器提供关于条件分支可能结果的提示&#xff0c;来帮助生成更有效的机器代码。 __predict_false宏扩展为一个属性&#xff0c;该属性指示编译器预测给定分支可能为假。这意味着&…

jdk20 升级, gson 的坑

项目部署在服务器中请求报下面这个错 java.lang.reflect.InaccessibleObjectException: Unable to make private java.util.Collections$EmptyList() accessible: module java.base does not "opens java.util" to unnamed module 2c35e847at java.base/java.lang.r…

制作一个简单的C语言词法分析程序

1.分析组成 C语言的程序中&#xff0c;有很单词多符号和保留字。一些单词符号还有对应的左线性文法。所以我们需要先做出一个单词字符表&#xff0c;给出对应的识别码&#xff0c;然后跟据对应的表格来写出程序 2.程序设计 程序主要有循环判断构成。不需推理即可产生的符号我…

【需水预测】区域需水量预测方法-定额法

区域需水量预测方法-定额法 1 生活用水需水量预测1.1 人口指标预测1.2 居民生活综合用水定额分析 2 农业需水量预测2.1 农田灌溉需水量2.2 林牧渔业需水量另&#xff1a;灌溉面积定义 3 工业需水量预测3.1 工业增加值发展指标预测 4 生态需水量预测4.1 生态环境面积发展指标预测…

APP自动化测试 ---- Appium介绍及运行原理

在面试APP自动化时&#xff0c;有的面试官可能会问Appium的运行原理&#xff0c;以下介绍Appium运行原理。 一、Appium介绍 1.Appium概念 Appium是一个开源测试自动化框架&#xff0c;可用于原生&#xff0c;混合和移动Web应用程序测试。它使用WebDriver协议驱动IOS&#xf…

工具分享:腾讯云价格计算器使用方法_服务器报价不求人

腾讯云服务器价格计算器可以一键计算出云服务器的精准报价&#xff0c;包括CVM实例规格价格、CPU内存费用、公网带宽收费、存储系统盘和数据盘详细费用&#xff0c;腾讯云百科txybk.com分享腾讯云价格计算器链接入口、使用方法说明&#xff1a; 腾讯云服务器价格计算器 打开腾…

项目经验分享|openGauss 陈贤文:受益于开源,回馈于开源

开源之夏 项目经验分享 2023 #08 # 关于 openGauss 社区 openGauss是一款开源关系型数据库管理系统&#xff0c;采用木兰宽松许可证v2发行。openGauss内核深度融合华为在数据库领域多年的经验&#xff0c;结合企业级场景需求&#xff0c;持续构建竞争力特性。同时openGauss也是…

树莓派基金会近日发布了新版基于 Debian 的树莓派操作系统

导读树莓派基金会&#xff08;Raspberry Pi Foundation&#xff09;近日发布了新版基于 Debian 的树莓派操作系统&#xff08;Raspberry Pi OS&#xff09;&#xff0c;为树莓派单板电脑带来了新的书虫基础和一些重大变化。 新版 Raspberry Pi OS 的最大变化是它现在基于最新的…

为了让女儿学打字,开发了个免费的打字网站

以后计算机也算是门手艺活了&#xff0c;想要增加沟通效率还是从入门打字开始。 之前小时候也玩过各种各样的打字游戏&#xff0c;警察抓小偷、打蜜蜂之类的&#xff0c;小的时候也没有各种教程不方便&#xff0c;一指禅玩这种游戏真的是累死&#xff0c;也没有任何提示。急得直…

如何分离一个要素的shp矢量文件:利用ArcGIS分割工具

下面介绍如何用ArcGIS对含有多个分离区域的一整个面要素进行分割 如下图&#xff0c;现在想要将下方的长形shp提取出来&#xff0c;首先打开shp文件&#xff1a; 右击空白处查看该矢量文件的投影信息&#xff1a; 在文件夹中新建shp文件&#xff0c;设置一样的投影&#xff1a…

docker - win10 家庭版 开始虚拟化

文章目录 前言docker - win10 家庭版 开始虚拟化 前言 如果您觉得有用的话&#xff0c;记得给博主点个赞&#xff0c;评论&#xff0c;收藏一键三连啊&#xff0c;写作不易啊^ _ ^。   而且听说点赞的人每天的运气都不会太差&#xff0c;实在白嫖的话&#xff0c;那欢迎常来啊…

ETCD备份与恢复

文章目录 主要内容一.备份1.先安装etcd客户端代码如下&#xff08;示例&#xff09;: 2.备份成文件并查看代码如下&#xff08;示例&#xff09;: 3.解释 二.恢复1.先停止服务代码如下&#xff08;示例&#xff09;: 2.删除现有ETCD&#xff0c;并恢复数据代码如下&#xff08;…

SpringBoot拉取高德天气预报数据

SpringBoot拉取高德天气预报数据 一、账号申请 1.整体流程 天气文档&#xff1a;https://lbs.amap.com/api/webservice/guide/api/weatherinfo 整体流程可参考&#xff1a;https://lbs.amap.com/api/webservice/guide/create-project/get-key 2.注册账号 注册地址&#x…

cmake 使用include 分层加载

include命令 说到cmake&#xff0c;可能最先想到的就是CmakeLists.txt文件&#xff0c;但是在很多情况下&#xff0c;也会看到.cmake文件。 cmake文件是干什么的&#xff0c;甚至会想.cmake文件是不是cmake的正统文件&#xff0c;而CmakeLists.txt并不是。但其实&#xff0c;Cm…

IP 地址查询,快速查询自己的 IP 地址

文章目录 在线结果 在线 http://myip.top/ 结果

c++ queue 的使用

目录 1. 默认构造函数 2. void push(const T& x) 3. void pop() 4. T& front() 5. T& back() 6. bool empty() 7. size_t size() 下面是 queue 的简介&#xff0c;来自 queue - C Reference (cplusplus.com) 的中文翻译&#xff0c;看看就行了&#xff…

使用Node.js软件包管理器(npm)安装TypeScript

安装node.js node.js的安装很简单&#xff0c;这里不再赘述&#xff0c;如果大家有需要&#xff0c;可以看一下这个&#xff1a;https://blog.csdn.net/David_house/article/details/123218488 检验电脑上node.js是否安装成功&#xff0c;或者是否已经安装node.js&#xff0c…

22款奔驰GLE450升级香氛负离子 清新淡雅

香氛负离子系统是由香氛系统和负离子发生器组成的一套配置&#xff0c;也可以单独加装香氛系统或者是负离子发生器&#xff0c;香氛的主要作用就是通过香氛外壳吸收原厂的香水再通过空调管输送到内饰中&#xff0c;而负离子的作用就是安装在空气管中通过释放电离子来打击空气中…

11月14号|Move生态Meetup相约浪漫土耳其

Move是基于Rust编程语言&#xff0c;由Mysten Labs联合创始人兼CTO Sam Blackshear在Meta的Libra项目中开发而来&#xff0c;旨在为开发者提供比现有区块链语言更通用的开发语言。Sam的目标是创建Web3的JavaScript&#xff0c;即一种跨平台语言&#xff0c;使开发人员能够在多个…

Java NIO 开发

Java NIO 新篇介绍加示例代码 Java NIO&#xff08;New IO&#xff09;是 JDK 1.4 引入的一组新的 I/O API&#xff0c;用于支持非阻塞式 I/O 操作。相比传统的 Java IO API&#xff0c;NIO 提供了更快、更灵活的 I/O 操作方式&#xff0c;可以用于构建高性能网络应用程序。 Ja…