Node.js: express + MySQL para lograr el inicio de sesión de registro, autenticación de identidad

        Parte del contenido de este artículo, como la verificación de campos y la estructura del proyecto, está escrito en otro artículo: Node.js: El uso de express + MySQL

1. Realizar el registro

        1. El registro debe escribir el número de cuenta y la contraseña del nuevo usuario en la base de datos. El número de cuenta se puede escribir directamente en la base de datos, pero la contraseña generalmente no se almacena directamente en la base de datos. La contraseña se cifrará y se almacenará en la base de datos, lo que puede mejorar la seguridad de la cuenta.

        2. Al iniciar sesión, cifre la contraseña de la misma manera y compárela con la contraseña almacenada en la base de datos. Si son iguales, el inicio de sesión es exitoso.

        3. Date cuenta

        (1), primero cree una tabla en la base de datos para almacenar información, el nombre de la tabla que creé es user_login

         (2), el cifrado de contraseñas utiliza un paquete   bcryptjs   , este paquete puede cifrar contraseñas e instalarlo en el proyecto con npm.

         (3) En la estructura del directorio, cree un nuevo archivo en el módulo de enrutamiento, el módulo de función de procesamiento y el módulo de verificación de campo para completar esta parte del contenido.

 aplicación.js

// 引入express
const express = require("express");
// 创建服务器的实例对象
const app = express();
// 引入校验规则的包,在定义错误级别的中间件时会用到
const joi = require("joi");

// 配置解析表单数据的中间件  内置中间件,只能解析application/x-www-form-urlencoded格式的数据
app.use(express.urlencoded({ extended: false }));

// 封装res.send() ,必须在路由之前封装
/**
 * 不管是输出正确的数据还是错误的信息,每次都需要写这么多东西
 * res.send({ state: 0, message: "查询成功", data: result })
 * 封装之后不需要写这么多
 */
app.use((req, res, next) => {
  // 定义一个输出的函数
  res.output = function (err, status = 1, data) {
    res.send({
      status,
      message: err instanceof Error ? err.message : err,
      data,
    });
  };
  next();
});

// 导入并使用登录,注册的路由模块
const loginRouter = require("./router/login");
app.use(loginRouter);
// 导入并使用路由模块
const inforRouter = require("./router/userInfor");
app.use(inforRouter);

// 在所有路由下面调用错误级别的中间件
app.use((err, req, res, next) => {
  // 验证失败导致的错误
  if (err instanceof joi.ValidationError) return res.output(err);
  // // 身份认证失败后的错误
  // if(err.name === 'UnauthorizedError') return res.cc('身份认证失败!')

  // 未知的错误
  res.output(err);
  next();
});

// 启动服务器
app.listen(3007, () => {
  console.log("Server running at http://127.0.0.1:3007");
});

        Debido a que app.js es el archivo de entrada de todo el proyecto, todas las rutas deben importarse y usarse en este archivo.

El archivo login.js en la carpeta del enrutador

const express = require("express");
const router = express.Router();

// 导入校验规则的中间价
const expressJoi = require("@escook/express-joi");
// 引入规则
const { login_rules } = require("../schema/login");

// 引入处理函数
const login_handler = require("../router_handler/login");

// 注册
router.post("/register", expressJoi(login_rules), login_handler.userRegister);

// 将路由导出
module.exports = router;

        Igual que en el artículo anterior, registre rutas en este archivo, importe funciones de procesamiento desde router_handler, importe middleware y reglas para reglas de verificación y, finalmente, exporte rutas para usar en archivos app.js.

El archivo login.js en la carpeta router_handler (función de manejo)

// 导入数据库模块
const db = require("../db/index");
// 引入对密码进行加密的包
const bcryptjs = require("bcryptjs");

// 注册处理函数并导出
exports.userRegister = (req, res) => {
  // 先取到从客户端传来的值
  let { username, password } = req.body;
  /**
   *  注册的时候用户名是唯一的,不能与其他人的用户名一样
   *  在将信息写入数据库之前,要先检查用户名是否被占用
   */
  // 查询有无相同的用户名
  const sql = "select username from user_login where username=?";
  // 执行查找sql语句
  db.query(sql, username, (err, results) => {
    // sql语句执行失败
    if (err) return res.output(err);
    // 执行成功,如果有数据,则说明有相同的用户名
    if (results.length === 1) return res.output("用户名被占用");
    // 执行成功,用户名没被占用

    console.log('加密之前', password);
    // 对密码进行加密,第二个参数可以提高密码的安全性,几也行
    password = bcryptjs.hashSync(password, 10);
    console.log('加密过后', password);

    // 定义新语句,增加用户
    const sqlStr = "insert into user_login set ?";
    // 执行增加sql语句
    db.query(sqlStr, { username, password }, (err, results) => {
      // 执行sql语句失败
      if (err) return res.output(err);
      // 执行sql语句成功 但影响行数不为1
      if (results.affectedRows !== 1) return res.output("用户注册失败!");
      // 执行成功,影响行数为1
      res.output("注册成功!");
    });
  });
};

        (1), primero obtenga el nombre de usuario y la contraseña que se registrarán del cliente y use let al deconstruir el nombre de usuario y la contraseña, porque la contraseña debe cifrarse en las siguientes operaciones y las variables se modifican. Con const, puedes redefinir un valor.

        (2) El proceso de registro se divide en tres pasos. El primer paso es verificar si el nombre de usuario tiene el mismo valor en la base de datos. El nombre de usuario no puede ser el mismo. Si hay el mismo valor, se devuelve el nombre de usuario. .

        (3) Si no tiene el mismo nombre de usuario y contraseña, puede usarlos para cifrar la contraseña establecida por el usuario y usar el paquete bcryptjs para cifrar. Usando el método hashSync(), el primer parámetro es la contraseña original y el segundo parámetro puede mejorar la seguridad de la contraseña, que es un número.

        (4) Después del cifrado, puede ver la contraseña cifrada en la salida del terminal, almacenar los datos en la base de datos y usar la instrucción de inserción. Si la instrucción de la base de datos se ejecuta correctamente, pero el número de filas afectadas no es 1, es también un fracaso.

El archivo login.js en la carpeta de esquema (reglas de validación, utilizadas en el archivo login.js en el directorio del enrutador)

// 导入校验规则的包
const joi = require("joi");
// 确定规则
const username = joi.string().alphanum().min(1).max(16).required();
const password = joi.string().pattern(/^[\S]{6,12}$/).required();

// 导出规则
exports.login_rules = {
  body: {
    username,
    password,
  },
};

Después de que todo esté escrito, use cartero para probarlo. Primero, pruebe si se pueden usar las reglas de verificación. Si ingresa una contraseña incorrecta, la contraseña de error no se ajusta a las reglas.

 Luego reemplace la contraseña por una válida:

 

 

El registro fue exitoso, verifique la contraseña antes y después del cifrado en el terminal y habrá un nuevo dato en la base de datos.

Luego haga clic en enviar nuevamente, esta vez ya hay datos en la base de datos al registrarse y el nombre de usuario está ocupado.

 Error, se toma el nombre de usuario.

En segundo lugar, realice el inicio de sesión.

        Para lograr iniciar sesión, se utilizará un paquete jsonwebtoken, que puede generar un token.

estructura de archivos:

        Esta vez hay un archivo de configuración global adicional, config.js, este archivo establece la clave de cifrado al generar el token, y la duración del token, la clave y la hora también se pueden escribir directamente en el archivo, pero el cifrado debe ser usado, también es necesario usar Decryption, para la conveniencia de sacarlo. La clave se puede configurar a voluntad.

archivo config.js 

// 全局的配置文件
module.exports = {
    /**
     * 设置token加密和解密用到的密钥
     */
    jwtSecretKey: 'c^_^h',
    /**
     * 设置token的有效期
     */
    expiresIn: '10h',
}

El archivo login.js en la carpeta del enrutador (configure el enrutamiento en este archivo)

const express = require("express");
const router = express.Router();

// 导入校验规则的中间价
const expressJoi = require("@escook/express-joi");
// 引入规则
const { login_rules } = require("../schema/login");

// 引入处理函数
const login_handler = require("../router_handler/login");

// 注册
router.post("/register", expressJoi(login_rules), login_handler.userRegister);
// 登录,登录的时候字段也是相同的规则
router.post("/login", expressJoi(login_rules), login_handler.userLogin);

// 将路由导出
module.exports = router;

        Tanto el registro como el inicio de sesión utilizan un nombre de usuario y una contraseña, y las reglas para ambos son las mismas.

El archivo login.js en la carpeta router_handler (función de procesamiento de inicio de sesión)

// 导入数据库模块
const db = require("../db/index");
// 引入对密码进行加密的包
const bcryptjs = require("bcryptjs");
// 导入生成token的包
const jwt = require("jsonwebtoken");
// 导入全局的配置文件,密文
const config = require("../config");

// 注册处理函数
exports.userRegister = (req, res) => {
  // 先取到从客户端传来的值
  let { username, password } = req.body;
  /**
   *  注册的时候用户名是唯一的,不能与其他人的用户名一样
   *  在将信息写入数据库之前,要先检查用户名是否被占用
   */
  // 查询有无相同的用户名
  const sql = "select username from user_login where username=?";
  // 执行查找sql语句
  db.query(sql, username, (err, results) => {
    // sql语句执行失败
    if (err) return res.output(err);
    // 执行成功,如果有数据,则说明有相同的用户名
    if (results.length === 1) return res.output("用户名被占用");
    // 执行成功,用户名没被占用
    // 定义新语句,增加用户
    const sqlStr = "insert into user_login set ?";

    console.log("加密之前", password);
    // 对密码进行加密,第二个参数可以提高密码的安全性,几也行
    password = bcryptjs.hashSync(password, 10);
    console.log("加密过后", password);

    // 执行增加sql语句
    db.query(sqlStr, { username, password }, (err, results) => {
      // 执行sql语句失败
      if (err) return res.output(err);
      // 执行sql语句成功 但影响行数不为1
      if (results.affectedRows !== 1) return res.output("用户注册失败!");
      // 执行成功,影响行数为1
      res.output("注册成功!");
    });
  });
};

// 登录处理函数
exports.userLogin = (req, res) => {
  // 接收表单数据
  const { username, password } = req.body;
  // 先查找用户名是否在数据库中,定义sql语句
  const sql = "select * from user_login where username=?";
  // 执行语句
  db.query(sql, username, (err, results) => {
    if (err) return res.output(err);
    // 语句执行成功,但没有相应的username
    if (results.length !== 1) return res.output("登录失败");
    // 语句执行成功,也有相应的username
    // 进行密码的比较
    // 前面是客户端的密码,后面是数据库中存储经过加密的密码
    const compareResult = bcryptjs.compareSync(password, results[0].password);
    // 会返回true或false
    if (!compareResult) {
      return res.output("密码错误,登录失败!");
    }
    // 密码比对正确,在服务端生成token字段
    // 获取到用户的信息,剔除掉密码,生成token
    const user = { ...results[0], password: "" };
    // 对用户的信息进行加密,生成token字符串,参数2和参数3可以直接写,也可以抽出去
    const tokenStr = jwt.sign(user, config.jwtSecretKey, {
      expiresIn: config.expiresIn,
    });
    // 调用res.send将token响应给客户端
    res.output("登录成功", 0, "Bearer " + tokenStr);
  });
};

        (1), primero reciba el nombre de usuario y la contraseña del cliente y luego busque el nombre de usuario correspondiente en la base de datos; de lo contrario, el nombre de usuario es incorrecto y el inicio de sesión falla.

        (2) Si hay un nombre de usuario correspondiente, es necesario comparar si la contraseña es la misma. El paquete bcryptjs todavía se usa para comparar la contraseña y se usa el método compareSync (). Este método tiene dos parámetros, el El primero se pasa desde el cliente La contraseña por venir, el segundo parámetro es almacenar la contraseña cifrada en la base de datos, devolverá verdadero o falso según el resultado, si no es el mismo, el inicio de sesión fallará.

        (3) Si la contraseña es la misma y el inicio de sesión es exitoso, para generar un token y devolverlo al cliente, use el paquete jsonwebtoken, el método sign (), que genera un token basado en la información del usuario, y el usuario La información generalmente elimina la contraseña. El segundo parámetro es la clave, el tercer parámetro es el tiempo, cuánto dura el período de validez y, finalmente, el token se devuelve al cliente.

        (4), el cliente token no se puede usar directamente, debe agregar 'Portador' al frente, agregarlo al regresar y la interfaz se puede usar directamente.

Inicio de sesión correcto.

Tres, dentro y fuera del inicio de sesión.

        Las funciones del proyecto se dividen en funciones dentro y fuera del inicio de sesión, como un sistema de blog, puede ver los artículos que contiene cuando no ha iniciado sesión, pero puede publicar artículos solo después de iniciar sesión.

        La interfaz en el inicio de sesión debe enviar el token en el encabezado de la solicitud al solicitar la autenticación de identidad. Solo hay cambios en app.js y otros archivos permanecen sin cambios.

aplicación.js

// 引入express
const express = require("express");
// 创建服务器的实例对象
const app = express();
// 引入校验规则的包,在定义错误级别的中间件时会用到
const joi = require("joi");

// 配置解析表单数据的中间件  内置中间件,只能解析application/x-www-form-urlencoded格式的数据
app.use(express.urlencoded({ extended: false }));

// 封装res.send() ,必须在路由之前封装
/**
 * 不管是输出正确的数据还是错误的信息,每次都需要写这么多东西
 * res.send({ state: 0, message: "查询成功", data: result })
 * 封装之后不需要写这么多
 */
app.use((req, res, next) => {
  // 定义一个输出的函数
  res.output = function (err, status = 1, data) {
    res.send({
      status,
      message: err instanceof Error ? err.message : err,
      data,
    });
  };
  next();
});

// 在路由之前配置解析token的中间件
const { expressjwt: jwt } = require("express-jwt");
// 解析token需要token的密钥
const config = require("./config");
// 定义中间件,需要哪个密钥解析,.unless指定哪些接口不需要进行token身份认证
app.use(
  jwt({ secret: config.jwtSecretKey, algorithms: ["HS256"] }).unless({
    path: [/^\/api/],
  })
);

// 导入并使用登录,注册的路由模块
const loginRouter = require("./router/login");
app.use("/api", loginRouter);
// 导入并使用路由模块
const inforRouter = require("./router/userInfor");
app.use(inforRouter);

// 在所有路由下面调用错误级别的中间件
app.use((err, req, res, next) => {
  // 验证失败导致的错误
  if (err instanceof joi.ValidationError) return res.output(err);

  // 未知的错误
  res.output(err);
  next();
});

// 启动服务器
app.listen(3007, () => {
  console.log("Server running at http://127.0.0.1:3007");
});

        (1) Se incluye un token en el encabezado de la solicitud y el token debe analizarse cuando la interfaz lo solicita. Para analizar el token, se requiere un paquete express-jwt y se requiere la misma clave que cuando se generó el token.

        (2) Cuando el middleware para analizar tokens no está definido, todas las rutas no necesitan tokens en este momento. Después de la definición, se requieren tokens para todas las rutas. less() puede especificar qué rutas no requieren tokens. El atributo de algoritmos establece el algoritmo de jwt. Para obtener más detalles, consulte la documentación de express-jwt.

        (3), app.use("/api", loginRouter); cuando utilice enrutamiento, agregue '/api' antes de la solicitud y también debe agregar '/api' antes de la ruta de la solicitud, y no es necesario enviarla. al iniciar sesión y registrar el token.

El token no se envió y se informó un error.

 Envíe el token y la consulta se realizará correctamente.

Finalmente, clasifique los paquetes utilizados en todo el proceso:

expresar: marco

mysql: base de datos

@escook/express-joi: Validar automáticamente los datos del formulario

joi: reglas de campo

bcryptjs: cifrar contraseñas

jsonwebtoken: token generado

express-jwt: analiza el token después de enviarlo en el encabezado de la solicitud

        Puede ir al siguiente enlace para obtener el código del artículo.

        Enlace: https://pan.baidu.com/s/1t7bX0Nv3kggyf7IFzEffcA Código de extracción: 0000

Supongo que te gusta

Origin blog.csdn.net/h360583690/article/details/125583639
Recomendado
Clasificación