• 首页 首页 icon
  • 工具库 工具库 icon
    • IP查询 IP查询 icon
  • 内容库 内容库 icon
    • 快讯库 快讯库 icon
    • 精品库 精品库 icon
    • 问答库 问答库 icon
  • 更多 更多 icon
    • 服务条款 服务条款 icon

NodeJs+Express+MySQL 实现实现登录注册接口+token生成和验证+跨域-CORS

武飞扬头像
另一个自己IT
帮助1

目录

一、express是什么?

二、安装 express

三、安装Mysql

四、安装 nodemon 实现项目热更新

五、这里先了解下express的post get delete接口

post接口说明:

get接口说明 :

 DELETE 接口

六、注册功能

1、流程分析

校验表单数据是否合法

检测用户名是否占用

密码加密处理

插入新用户

2、完整注册接口

regUser(req, res) 注册函数

七、封装错误处理函数(即:注册功能使用的res.cc)

八、登录功能

1、流程分析

1.判断前端提交的后端的数据是否合法。

2.查询登录的用户是否存在。

3.判断当前用户的密码是否正确。

2、生成token字符

 1.安装jsonwebtoken用于生成token

2.导入jsonwebtoken

 3.全局配置文件(里面有token的密钥)

3.使用jwt.sign对用户的信息进行加密,生成 token 字符串 

4.登录成功后将生成的token返回给客户端

3、登录接口的完整代码

1.login()登录函数代码

九、最后附上users路由模块和登录注册函数.js的完整代码

users.js

login.js登录注册函数

接口文档:

十、解析token中间件

 1.安装解析中间件

2.在App.js 中引入

3.注册全局中间件并解析token

4.注册全局错误中间件 当token失效时 返回信息

十一、CORS跨域中间件

 1. 使用 cors 中间件解决跨域问题

完整代码:(jsonp就不做过多解释了)

1、CORS 响应头部 - Access-Control-Allow-Origin

2、CORS 响应头部 - Access-Control-Allow-Headers

3、CORS 响应头部 - Access-Control-Allow-Methods

4、 CORS请求的分类

5、 简单请求

6、预检请求


一、express是什么?

  1. Express 是一个简洁而灵活的 node.js Web应用框架, 提供了一系列强大特性帮助你创建各种 Web 应用,和丰富的 HTTP 工具。
  2. 使用 Express 可以快速地搭建一个完整功能的网站。
  3. Express 框架核心特性:
  • 可以设置中间件来响应 HTTP 请求。

  • 定义了路由表用于执行不同的 HTTP 请求动作。

  • 可以通过向模板传递参数来动态渲染 HTML 页面。

二、安装 express

搭建Express项目有两种方式:

  • 方式一:从零搭建自己的express应用结构
  • 方式二:安装express-generator脚手架 一键生成express项目

在这里我们使用方式二快速构建一个express项目:

        express-generator 是 Express 应用程序生成器工具,我们可以使用它来快速创建应用程序框架。

  1. 在项目文件夹下打开node终端
  2. 安装express-generator 脚手架

    npm install -g express-generator
  3. 创建项目
    express expressFrame (expressFrame 是项目名)

         执行完后项目目录下的结构

学新通

 注:bin/www 是启动入口文件,在里面可以设置端口号等

3. 安装依赖

npm install

4. 启动项目
 

npm start

学新通

此时在浏览器打开 http://localhost:3000/

学新通

 出现以上页面,那么恭喜你express服务器已创建完成

三、安装Mysql

npm i mysql

1、新建db 数据库文件夹,文件夹下新建index.js 用来配置数据库信息,index.js内容如下

  1.  
    // 导入 mysql 模块
  2.  
    const mysql = require('mysql')
  3.  
    // 建立与 MySQL 数据库的连接
  4.  
    const db = mysql.createPool({
  5.  
    host: '127.0.0.1', // 数据库的IP地址
  6.  
    port: 3306, //数据库端口号
  7.  
    user: 'web2245321733', // 登录数据库的账号
  8.  
    password: 'web2245321733', // 登录数据库的密码
  9.  
    database: 'web2245321733' // 指定要操作哪个数据库
  10.  
    })
  11.  
     
  12.  
    // 检测数据库是否连接成功
  13.  
    db.query("select 1", (err, results) => {
  14.  
    if (err) return console.log(err);
  15.  
    console.log(results, '数据库链接成功');
  16.  
    });
  17.  
     
  18.  
    module.exports = db

此时重启项目,终端看到打印出数据库链接成功,学新通

四、安装 nodemon 实现项目热更新

在刚才的添加数据库当中,我们发现每次修改代码都需要重启项目,非常麻烦

1、安装 nodemon 来监控 node.js 源代码的任何变化和自动重启你的服务器

npm install -g nodemon

2、在package.json中修改启动命令

学新通

 然后重启项目,就可以了。

五、这里先了解下express的post get delete接口

post接口说明:

  1.  
    // 定义 POST 接口
  2.  
    router.post('/post', (req, res) => {
  3.  
    // 通过 req.body 获取请求体中包含的 url-encoded 格式的数据
  4.  
    const body = req.body
  5.  
    // 调用 res.send() 方法,向客户端响应结果
  6.  
    res.send({
  7.  
    status: 0, // 0 表示处理成功,1 表示处理失败
  8.  
    msg: 'POST 请求成功!', // 状态的描述
  9.  
    data: body, // 需要响应给客户端的数据
  10.  
    })
  11.  
    })

get接口说明 :

  1.  
    // 在这里挂载对应的路由
  2.  
    router.get('/get', (req, res) => {
  3.  
    // 通过 req.query 获取客户端通过查询字符串,发送到服务器的数据
  4.  
    const query = req.query
  5.  
    // 调用 res.send() 方法,向客户端响应处理的结果
  6.  
    res.send({
  7.  
    status: 0, // 0 表示处理成功,1 表示处理失败
  8.  
    msg: 'GET 请求成功!', // 状态的描述
  9.  
    data: query, // 需要响应给客户端的数据
  10.  
    })
  11.  
    })

 DELETE 接口

  1.  
    // 定义 DELETE 接口
  2.  
    router.delete('/delete', (req, res) => {
  3.  
    res.send({
  4.  
    status: 0,
  5.  
    msg: 'DELETE请求成功',
  6.  
    })
  7.  
    })

参数说明:

  • router.post 用于创建post接口  它有两个参数 参数1:路由匹配规则    参数2:请求的回调函数
  • 回调函数的req参数:客户端请求信息,包括 请求头 请求参数等,req.bodey或req.query获取请求参数
  • 回调函数的res参数:用于提交服务端的响应给客户端
  • 调用 res.send() 方法,向客户端响应处理的结果

六、注册功能

1、流程分析

注册的一般流程 1.校验表单数据是否合法 2.检测用户名是否占用 3.密码加密处理 4.插入新用户

校验表单数据是否合法

  1.  
    // 通过 req.body 获取请求体中包含的 url-encoded 格式的数据
  2.  
    const userInfo = req.body
  3.  
    //【步骤一】对客户端的数据进行校验
  4.  
    if (userInfo.username == '' || userInfo.password == '') {
  5.  
    return res.send({
  6.  
    status: 1,
  7.  
    msg: '用户名和密码不能为空'
  8.  
    })
  9.  
    }

检测用户名是否占用

  1.  
    // 定义sql语句,查询用户名是否被占用
  2.  
    let sql = 'select * from ev_users where username=?'
  3.  
    db.query(sql, [userInfo.username], (error, result) => {
  4.  
    if (error) {
  5.  
    return res.cc(error)
  6.  
    }
  7.  
    if (result.length > 0) {
  8.  
    return res.cc('用户名已被占用!')
  9.  
    }
  10.  
    })

密码加密处理

安装bcryptjs 加密包 用于密码加密

npm i bcryptjs

 引入加密包

  1.  
    // 导入 bcryptjs 加密包
  2.  
    const bcrypt = require('bcryptjs')

调用 bcrypt.hashSync() 对密码加密

  1.  
    // 调用 bcrypt.hashSync() 对密码加密
  2.  
    userInfo.password = bcrypt.hashSync(userInfo.password, 10)

 说明:bcrypt.hashSync() 的参数1:要加密的密码  参数2: 加密等级 填10即可

插入新用户

前面我们已经连接过数据库了,这里我们直接引入数据库操作模块

  1.  
    // 导入数据库操作模块
  2.  
    const db = require('../../db/index')

 定义插入新用户的 SQL 语句

  1.  
    // 定义插入新用户的 SQL 语句
  2.  
    let sql1 = 'insert into ev_users set ?'

 调用 db.query() 执行 sql 语句 插入新用户 并给客户端返回结果

  1.  
    // 调用 db.query() 执行 sql 语句
  2.  
    db.query(sql1, {
  3.  
    username: userInfo.username,
  4.  
    password: userInfo.password
  5.  
    }, (error, result) => {
  6.  
    if (error) return res.cc(error)
  7.  
    // 判断影响行数是否为 1
  8.  
    if (result.affectedRows !== 1) return res.cc('注册用户失败!')
  9.  
    return res.cc('注册用户成功', 0, {
  10.  
    username: userInfo.username
  11.  
    })
  12.  
    })

2、完整注册接口

  • 打开routes文件夹下的users.js 路由模块,添加以下内容
  1.  
    /**
  2.  
    * POST 用户注册
  3.  
    * @param username 用户名
  4.  
    * @param password 用户密码
  5.  
    */
  6.  
    router.post('/add', (req, res, next) => {
  7.  
    // 通过 req.body 获取请求体中包含的 url-encoded 格式的数据
  8.  
    console.log(req.body)
  9.  
    const userInfo = req.body
  10.  
    //【步骤一】对客户端的数据进行校验
  11.  
    if (userInfo.username == '' || userInfo.password == '') {
  12.  
    return res.send({
  13.  
    status: 1,
  14.  
    msg: '用户名和密码不能为空'
  15.  
    })
  16.  
    }
  17.  
    // 【步骤二】执行定义好的注册函数
  18.  
    regUser(req, res)
  19.  
    });

regUser(req, res) 注册函数

在这里我单独新建了个login.js文件用于写注册和登录的处理的函数放

学新通

 引入

  1.  
    // 导入写好的注册/登录函数
  2.  
    const {
  3.  
    regUser,
  4.  
    login
  5.  
    } = require('../public/javascripts/login')

regUser(req, res) 注册函数内容如下:

  1.  
    // 导入数据库操作模块
  2.  
    const db = require('../../db/index')
  3.  
    // 导入 bcryptjs 加密包
  4.  
    const bcrypt = require('bcryptjs')
  5.  
     
  6.  
    /**
  7.  
    * POST 用户注册
  8.  
    * @param username 用户名
  9.  
    * @param password 用户密码
  10.  
    */
  11.  
    exports.regUser = (req, res) => {
  12.  
    // 获取客户端提交到服务器的用户信息
  13.  
    const userInfo = req.body
  14.  
    // 定义sql语句,查询用户名是否被占用
  15.  
    let sql = 'select * from ev_users where username=?'
  16.  
    db.query(sql, [userInfo.username], (error, result) => {
  17.  
    if (error) {
  18.  
    return res.cc(error)
  19.  
    }
  20.  
    if (result.length > 0) {
  21.  
    return res.cc('用户名已被占用!')
  22.  
    }
  23.  
    // 调用 bcrypt.hashSync() 对密码加密
  24.  
    userInfo.password = bcrypt.hashSync(userInfo.password, 10)
  25.  
    // 定义插入新用户的 SQL 语句
  26.  
    let sql1 = 'insert into ev_users set ?'
  27.  
    // 调用 db.query() 执行 sql 语句
  28.  
    db.query(sql1, {
  29.  
    username: userInfo.username,
  30.  
    password: userInfo.password
  31.  
    }, (error, result) => {
  32.  
    if (error) return res.cc(error)
  33.  
    // 判断影响行数是否为 1
  34.  
    if (result.affectedRows !== 1) return res.cc('注册用户失败!')
  35.  
    return res.cc('注册用户成功', 0, {
  36.  
    username: userInfo.username
  37.  
    })
  38.  
    })
  39.  
    })
  40.  
    }
  41.  
     
  42.  
    /**
  43.  
    * POST 登录的回调函数
  44.  
    * @param username 用户名
  45.  
    * @param password 用户密码
  46.  
    */
  47.  
    exports.login = (req, res) => {
  48.  
     
  49.  
    }

七、封装错误处理函数(即:注册功能使用的res.cc)

在注册功能里我对res.send向客户端响应函数做了处理,

在app.js中,放在路由前面

  1.  
    //封装错误处理函数
  2.  
    app.use((req, res, next) => {
  3.  
    res.cc = function(err, status = 1, data = {}) {
  4.  
    res.send({
  5.  
    status,
  6.  
    data,
  7.  
    message: err instanceof Error ? err.message : err
  8.  
    })
  9.  
    }
  10.  
    next()
  11.  
    })

八、登录功能

1、流程分析

登录的一般流程 1.判断前端提交的后端的数据是否合法。 2.查询登录的用户是否存在。 3.判断当前用户的密码是否正确。

1.判断前端提交的后端的数据是否合法。

  1.  
    // 通过 req.body 获取请求体中包含的 url-encoded 格式的数据
  2.  
    console.log(req.body)
  3.  
    const userInfo = req.body
  4.  
    //【步骤一】对客户端的数据进行校验
  5.  
    if (userInfo.username == '' || userInfo.password == '') {
  6.  
    return res.send({
  7.  
    status: 1,
  8.  
    msg: '用户名和密码不能为空'
  9.  
    })
  10.  
    }

2.查询登录的用户是否存在。

  1.  
    // 定义 SQL 语句
  2.  
    const sql = 'select * from ev_users where username=?'
  3.  
    // 执行 SQL 语句,根据用户名查询用户的信息
  4.  
    db.query(sql, userInfo.username, (err, result) => {
  5.  
    // 执行 SQL 语句失败
  6.  
    if (err) return res.cc(err)
  7.  
    // 执行 SQL 语句成功,但是获取的数据条数不为1 也是失败的
  8.  
    if (result.length !== 1) return res.cc('登录失败!')
  9.  
    // 经过上方俩条判断条件,则证明执行 SQL 成功
  10.  
    })

3.判断当前用户的密码是否正确。

使用 加密包的bcrypt.compareSync方法对比用户提交的密码和数据库中的密码是否一致,如果一直即:登录成功

  1.  
    // TODO :判断密码是否正确
  2.  
    const comRes = bcrypt.compareSync(userInfo.password, result[0].password)
  3.  
    if (!comRes) return res.cc('登陆失败')

2、生成token字符

密码正确的话,登录成功,根据用户信息生成唯一的token返回给客户端

 1.安装jsonwebtoken用于生成token

npm i jsonwebtoken

2.导入jsonwebtoken

  1.  
    // 导入生成Token的包
  2.  
    const jwt = require('jsonwebtoken')

 3.全局配置文件(里面有token的密钥)

config.js放在根目录

  1.  
    // 全局配置文件 config.js
  2.  
    module.exports = {
  3.  
    // 加密和解密 token 的密钥
  4.  
    jwtSecretKey: 'itheima No1. ^_^',
  5.  
    // token 有效期
  6.  
    expiresIn: '10h'
  7.  
    }

导入

  1.  
    // 导入全局配置文件(里面有token的密钥)
  2.  
    const config = require('../../config')

3.使用jwt.sign对用户的信息进行加密,生成 token 字符串 

jwt.sign 有三个参数依次是 生成token的数据,加密的形式,token有效期

 加密前先处理用户信息,将用户的敏感信息置空(如:密码等)

  1.  
    // 在服务器端生成 Token 字符串
  2.  
    const user = {
  3.  
    ...result[0], // 解构用户信息
  4.  
    password: '', //密码等敏感信息置空
  5.  
    }
  6.  
    // 对用户的信息进行加密,生成 token 字符串
  7.  
    const tokenStr = jwt.sign(user, config.jwtSecretKey, {
  8.  
    expiresIn: config.expiresIn //tonken 有效期
  9.  
    })

4.登录成功后将生成的token返回给客户端

  1.  
    // 调用 res.send 将Token响应给客户端
  2.  
    res.send({
  3.  
    status: 0,
  4.  
    data: {
  5.  
    user: user,
  6.  
    token: 'Bearer ' tokenStr,
  7.  
    },
  8.  
    message: '登录成功!!!',
  9.  
    })

3、登录接口的完整代码

  1.  
    /**
  2.  
    * POST 用户登录
  3.  
    * @param username 用户名
  4.  
    * @param password 用户密码
  5.  
    */
  6.  
    router.post('/login', (req, res, next) => {
  7.  
    // 通过 req.body 获取请求体中包含的 url-encoded 格式的数据
  8.  
    // console.log(req.body)
  9.  
    const userInfo = req.body
  10.  
    //对客户端的数据进行校验
  11.  
    if (userInfo.username == '' || userInfo.password == '') {
  12.  
    return res.send({
  13.  
    status: 1,
  14.  
    msg: '用户名和密码不能为空'
  15.  
    })
  16.  
    }
  17.  
    // 执行定义好的登录函数
  18.  
    login(req, res)
  19.  
    });

1.login()登录函数代码

  1.  
    /**
  2.  
    * POST 登录的回调函数
  3.  
    * @param username 用户名
  4.  
    * @param password 用户密码
  5.  
    */
  6.  
    exports.login = (req, res) => {
  7.  
    console.log('user', req.user);
  8.  
    // 接收表单的数据
  9.  
    const userInfo = req.body
  10.  
    // 定义 SQL 语句
  11.  
    const sql = 'select * from ev_users where username=?'
  12.  
    // 执行 SQL 语句,根据用户名查询用户的信息
  13.  
    db.query(sql, userInfo.username, (err, result) => {
  14.  
    // 执行 SQL 语句失败
  15.  
    if (err) return res.cc(err)
  16.  
    // 执行 SQL 语句成功,但是获取的数据条数不为1 也是失败的
  17.  
    if (result.length !== 1) return res.cc('登录失败!')
  18.  
    // 经过上方俩条判断条件,则证明执行 SQL 成功
  19.  
     
  20.  
    // TODO :判断密码是否正确
  21.  
    const comRes = bcrypt.compareSync(userInfo.password, result[0].password)
  22.  
    if (!comRes) return res.cc('登陆失败')
  23.  
    // 在服务器端生成 Token 字符串
  24.  
    const user = {
  25.  
    ...result[0], // 解构用户信息
  26.  
    password: '', //密码等敏感信息置空
  27.  
    }
  28.  
    // 对用户的信息进行加密,生成 token 字符串
  29.  
    const tokenStr = jwt.sign(user, config.jwtSecretKey, {
  30.  
    expiresIn: config.expiresIn //tonken 有效期
  31.  
    })
  32.  
    // 调用 res.send 将Token响应给客户端
  33.  
    res.send({
  34.  
    status: 0,
  35.  
    data: {
  36.  
    user: user,
  37.  
    token: 'Bearer ' tokenStr,
  38.  
    },
  39.  
    message: '登录成功!!!',
  40.  
    })
  41.  
    })
  42.  
    }

九、最后附上users路由模块和登录注册函数.js的完整代码

users.js

  1.  
    var express = require('express');
  2.  
    var router = express.Router();
  3.  
    // 导入写好的注册/登录函数
  4.  
    const {
  5.  
    regUser,
  6.  
    login
  7.  
    } = require('../public/javascripts/login')
  8.  
     
  9.  
    /* GET users listing. */
  10.  
    router.get('/', function(req, res, next) {
  11.  
    // 获取客户端提交到服务器的用户信息
  12.  
    const userInfo = req.body
  13.  
    if(req.user){
  14.  
    return res.cc('获取成功', 0, req.user)
  15.  
    }
  16.  
    // 获取到中间件的时间
  17.  
    res.send('GET 请求成功');
  18.  
    });
  19.  
     
  20.  
    /**
  21.  
    * POST 用户注册
  22.  
    * @param username 用户名
  23.  
    * @param password 用户密码
  24.  
    */
  25.  
    router.post('/add', (req, res, next) => {
  26.  
    // 通过 req.body 获取请求体中包含的 url-encoded 格式的数据
  27.  
    console.log(req.body)
  28.  
    const userInfo = req.body
  29.  
    //【步骤一】对客户端的数据进行校验
  30.  
    if (userInfo.username == '' || userInfo.password == '') {
  31.  
    return res.send({
  32.  
    status: 1,
  33.  
    msg: '用户名和密码不能为空'
  34.  
    })
  35.  
    }
  36.  
    // 【步骤二】执行定义好的注册函数
  37.  
    regUser(req, res)
  38.  
    });
  39.  
     
  40.  
    /**
  41.  
    * POST 用户登录
  42.  
    * @param username 用户名
  43.  
    * @param password 用户密码
  44.  
    */
  45.  
    router.post('/login', (req, res, next) => {
  46.  
    // 通过 req.body 获取请求体中包含的 url-encoded 格式的数据
  47.  
    // console.log(req.body)
  48.  
    const userInfo = req.body
  49.  
    //对客户端的数据进行校验
  50.  
    if (userInfo.username == '' || userInfo.password == '') {
  51.  
    return res.send({
  52.  
    status: 1,
  53.  
    msg: '用户名和密码不能为空'
  54.  
    })
  55.  
    }
  56.  
    // 执行定义好的登录函数
  57.  
    login(req, res)
  58.  
    });
  59.  
     
  60.  
     
  61.  
     
  62.  
     
  63.  
     
  64.  
    /* 模板 */
  65.  
    // // 在这里挂载对应的路由
  66.  
    // router.get('/get', (req, res) => {
  67.  
    // // 通过 req.query 获取客户端通过查询字符串,发送到服务器的数据
  68.  
    // const query = req.query
  69.  
    // // 调用 res.send() 方法,向客户端响应处理的结果
  70.  
    // res.send({
  71.  
    // status: 0, // 0 表示处理成功,1 表示处理失败
  72.  
    // msg: 'GET 请求成功!', // 状态的描述
  73.  
    // data: query, // 需要响应给客户端的数据
  74.  
    // })
  75.  
    // })
  76.  
     
  77.  
    // // 定义 POST 接口
  78.  
    // router.post('/post', (req, res) => {
  79.  
    // // 通过 req.body 获取请求体中包含的 url-encoded 格式的数据
  80.  
    // const body = req.body
  81.  
    // // 调用 res.send() 方法,向客户端响应结果
  82.  
    // res.send({
  83.  
    // status: 0,
  84.  
    // msg: 'POST 请求成功!',
  85.  
    // data: body,
  86.  
    // })
  87.  
    // })
  88.  
     
  89.  
    // // 定义 DELETE 接口
  90.  
    // router.delete('/delete', (req, res) => {
  91.  
    // res.send({
  92.  
    // status: 0,
  93.  
    // msg: 'DELETE请求成功',
  94.  
    // })
  95.  
    // })
  96.  
     
  97.  
    module.exports = router;

login.js登录注册函数

  1.  
    // 导入数据库操作模块
  2.  
    const db = require('../../db/index')
  3.  
    // 导入 bcryptjs 加密包
  4.  
    const bcrypt = require('bcryptjs')
  5.  
    // 导入生成Token的包
  6.  
    const jwt = require('jsonwebtoken')
  7.  
    // 导入全局配置文件(里面有token的密钥)
  8.  
    const config = require('../../config')
  9.  
     
  10.  
    /**
  11.  
    * POST 用户注册
  12.  
    * @param username 用户名
  13.  
    * @param password 用户密码
  14.  
    */
  15.  
    exports.regUser = (req, res) => {
  16.  
    // 获取客户端提交到服务器的用户信息
  17.  
    const userInfo = req.body
  18.  
    // 定义sql语句,查询用户名是否被占用
  19.  
    let sql = 'select * from ev_users where username=?'
  20.  
    db.query(sql, [userInfo.username], (error, result) => {
  21.  
    if (error) {
  22.  
    return res.cc(error)
  23.  
    }
  24.  
    if (result.length > 0) {
  25.  
    return res.cc('用户名已被占用!')
  26.  
    }
  27.  
    // 调用 bcrypt.hashSync() 对密码加密
  28.  
    userInfo.password = bcrypt.hashSync(userInfo.password, 10)
  29.  
    // 定义插入新用户的 SQL 语句
  30.  
    let sql1 = 'insert into ev_users set ?'
  31.  
    // 调用 db.query() 执行 sql 语句
  32.  
    db.query(sql1, {
  33.  
    username: userInfo.username,
  34.  
    password: userInfo.password
  35.  
    }, (error, result) => {
  36.  
    if (error) return res.cc(error)
  37.  
    // 判断影响行数是否为 1
  38.  
    if (result.affectedRows !== 1) return res.cc('注册用户失败!')
  39.  
    return res.cc('注册用户成功', 0, {
  40.  
    username: userInfo.username
  41.  
    })
  42.  
    })
  43.  
    })
  44.  
    }
  45.  
     
  46.  
    /**
  47.  
    * POST 登录的回调函数
  48.  
    * @param username 用户名
  49.  
    * @param password 用户密码
  50.  
    */
  51.  
    exports.login = (req, res) => {
  52.  
    console.log('user', req.user);
  53.  
    // 接收表单的数据
  54.  
    const userInfo = req.body
  55.  
    // 定义 SQL 语句
  56.  
    const sql = 'select * from ev_users where username=?'
  57.  
    // 执行 SQL 语句,根据用户名查询用户的信息
  58.  
    db.query(sql, userInfo.username, (err, result) => {
  59.  
    // 执行 SQL 语句失败
  60.  
    if (err) return res.cc(err)
  61.  
    // 执行 SQL 语句成功,但是获取的数据条数不为1 也是失败的
  62.  
    if (result.length !== 1) return res.cc('登录失败!')
  63.  
    // 经过上方俩条判断条件,则证明执行 SQL 成功
  64.  
     
  65.  
    // TODO :判断密码是否正确
  66.  
    const comRes = bcrypt.compareSync(userInfo.password, result[0].password)
  67.  
    if (!comRes) return res.cc('登陆失败')
  68.  
    // 在服务器端生成 Token 字符串
  69.  
    const user = {
  70.  
    ...result[0], // 解构用户信息
  71.  
    password: '', //密码等敏感信息置空
  72.  
    }
  73.  
    // 对用户的信息进行加密,生成 token 字符串
  74.  
    const tokenStr = jwt.sign(user, config.jwtSecretKey, {
  75.  
    expiresIn: config.expiresIn //tonken 有效期
  76.  
    })
  77.  
    // 调用 res.send 将Token响应给客户端
  78.  
    res.send({
  79.  
    status: 0,
  80.  
    data: {
  81.  
    user: user,
  82.  
    token: 'Bearer ' tokenStr,
  83.  
    },
  84.  
    message: '登录成功!!!',
  85.  
    })
  86.  
    })
  87.  
    }

到此我们的登录和注册接口已经实现,

接口文档:

注册:post       http://127.0.0.1/users/add

           请求参数:username:用户名   

                             password:密码

登录:post       http://127.0.0.1/users/login

           请求参数:username:用户名   

                             password:密码

十、解析token中间件

在刚刚我没完成了token的生成,现在我们做一个中间件用来解析token,来对用户进行身份认证

 1.安装解析中间件

npm i express-jwt

2.在App.js 中引入

  1.  
    //token解析中间件 一定要在路由之前配置解析 Token 的中间件
  2.  
    const expressJWT = require('express-jwt')
  3.  
    //映入解密
  4.  
    const config = require('./config')

3.注册全局中间件并解析token

  1.  
    // 注册全局中间件 链式调用 unless 方法,接收一个配置对象,path 字段设置一个正则表达式,表示不需要 token 身份认证的路由前缀。
  2.  
    app.use(expressJWT({
  3.  
    // 加密时设置的密钥
  4.  
    secret: config.jwtSecretKey,
  5.  
    // 设置算法
  6.  
    algorithms: ['HS256'],
  7.  
    // 无token请求不进行解析,并且抛出异常
  8.  
    // credentialsRequired: false
  9.  
    }).unless({
  10.  
    path: [
  11.  
    '/users/add',
  12.  
    '/users/login',
  13.  
    {
  14.  
    url: /^\/public\/.*/,
  15.  
    methods: ['GET', 'POST']
  16.  
    }
  17.  
    ]
  18.  
    // path: ['/users/login','/users']
  19.  
    }))

4.注册全局错误中间件 当token失效时 返回信息

  1.  
    // 错误中间件 当token失效时 返回信息
  2.  
    app.use((err, req, res, next) => {
  3.  
    if (err.name === 'UnauthorizedError') {
  4.  
    res.status(401).send({
  5.  
    status: 1,
  6.  
    data: {},
  7.  
    message: '身份认证失败!'
  8.  
    });
  9.  
    }
  10.  
    });

十一、CORS跨域中间件

注册登录接口写好了,但是使用的时候会有一个很严重的问题:不支持跨域请求。

解决接口跨域问题的方案主要有两种:

① CORS(主流的解决方案,推荐使用)

② JSONP(有缺陷的解决方案:只支持 GET 请求)

 1. 使用 cors 中间件解决跨域问题

cors 是 Express 的一个第三方中间件。通过安装和配置 cors 中间件,可以很方便地解决跨域问题。

使用步骤分为如下 3 步:

① 运行 npm install cors 安装中间件

npm install cors

② 使用 const cors = require(‘cors’) 导入中间件

  1.  
    // 一定要在路由之前,配置 cors 这个中间件,从而解决接口跨域的问题
  2.  
    const cors = require('cors')

③ 在路由之前调用 app.use(cors()) 配置中间件

app.use(cors())

完整代码:(jsonp就不做过多解释了)

  1.  
    // 【必须在配置 cors 中间件之前,配置 JSONP 的接口】
  2.  
    app.get('/api/jsonp', (req, res) => {
  3.  
    // TODO: 定义 JSONP 接口具体的实现过程
  4.  
    // 1. 得到函数的名称
  5.  
    const funcName = req.query.callback
  6.  
    // 2. 定义要发送到客户端的数据对象
  7.  
    const data = {
  8.  
    name: 'zs',
  9.  
    age: 22
  10.  
    }
  11.  
    // 3. 拼接出一个函数的调用
  12.  
    const scriptStr = `${funcName}(${JSON.stringify(data)})`
  13.  
    // 4. 把拼接的字符串,响应给客户端
  14.  
    res.send(scriptStr)
  15.  
    })
  16.  
     
  17.  
    // 一定要在路由之前,配置 cors 这个中间件,从而解决接口跨域的问题
  18.  
    const cors = require('cors')
  19.  
    app.use(cors())

注意事项:

①CORS 主要在服务器端进行配置。客户端浏览器无须做任何额外的配置,即可请求开启了CORS的接口。

②CORS在浏览器中有兼容。只有支持XMLHttpRequest Level2的浏览器,才能正常访问开启了CORS的服务端接口(例如:IE10 、Chrome4 、FireFox3.5 )。

1、CORS 响应头部 - Access-Control-Allow-Origin

如果指定了 Access-Control-Allow-Origin 字段的值为通配符 *,表示允许来自任何域的请求,示例代码如下:

res.setHeader('Access-Control-Allow-Origin','*')

2、CORS 响应头部 - Access-Control-Allow-Headers

默认情况下,CORS 仅支持客户端向服务器发送如下的 9 个请求头:
Accept、Accept-Language、Content-Language、DPR、Downlink、Save-Data、Viewport-Width、Width 、Content-Type (值仅限于 text/plain、multipart/form-data、application/x-www-form-urlencoded 三者之一)
如果客户端向服务器发送了额外的请求头信息,则需要在服务器端,通过 Access-Control-Allow-Headers 对额外的请求头进行声明,否则这次请求会失败!

 

  1.  
    // 允许客户端额外向服务器发送 Content-Type 请求头和 X-Custom-Header 请求头
  2.  
    // 注意: 多个请求头之间使用英文的逗号进行分割
  3.  
    res.setHeader('Access-Control-Allow-Headers ''Content-Type X-Custom-Header')

3、CORS 响应头部 - Access-Control-Allow-Methods

默认情况下,CORS 仅支持客户端发起 GET、POST、HEAD 请求。
如果客户端希望通过 PUT、DELETE 等方式请求服务器的资源,则需要在服务器端,通过 Access-Control-Alow-Methods来指明实际请求所允许使用的 HTTP 方法。示例代码如下:

  1.  
    // 只允许 POST、GET、DELETE、HEAD 请求方法
  2.  
    res,setHeader('Access-Control-A1low-Methods','POST,GET,DELETE,HEAD')
  3.  
    // 允许所有的 HTTP 请求方法
  4.  
    res,setHeader('Access-Control-A1low-Methods','*')

4、 CORS请求的分类

客户端在请求 CORS 接口时,根据 请求方式和请求头的不同,可以将 CORS 的请求分为两大类,分别是:
① 简单请求
② 预检请求

5、 简单请求

同时满足以下两大条件的请求,就属于简单请求:
① 请求方式:GET、POST、HEAD 三者之一
② HTTP 头部信息不超过以下几种字段:无自定义头部字段、Accept、Accept-Language、Content-Language、DPR、Downlink、Save-Data、Viewport-Width、Width 、Content-Type(只有三个值application/x-www-form-urlencoded、multipart/form-data、text/plain)

6、预检请求

只要符合以下任何一个条件的请求,都需要进行预检请求:
① 请求方式为 GET、POST、HEAD 之外的请求 Method 类型
② 请求头中包含自定义头部字段
③ 向服务器发送了 application/json 格式的数据

在浏览器与服务器正式通信之前,浏览器会先发送 OPTION 请求进行预检,以获知服务器是否允许该实际请求,所以这一次的 OPTION 请求称为“预检请求”。服务器成功响应预检请求后,才会发送真正的请求,并且携带真实数据。

7.、简单请求和预检请求的区别

简单请求的特点:客户端与服务器之间只会发生一次请求。
预检请求的特点:客户端与服务器之间会发生两次请求,OPTION 预检请求成功之后,才会发起真正的请求。

2、最后附上app.js完整代码 

  1.  
    // 导入 express
  2.  
    var express = require('express');
  3.  
    var path = require('path');
  4.  
    var cookieParser = require('cookie-parser');
  5.  
    var logger = require('morgan');
  6.  
     
  7.  
    // 引入路由模块
  8.  
    var indexRouter = require('./routes/index');
  9.  
    var usersRouter = require('./routes/users');
  10.  
     
  11.  
    // 创建服务器实例
  12.  
    var app = express();
  13.  
     
  14.  
    app.use(logger('dev'));
  15.  
     
  16.  
    // 处理 application/json
  17.  
    app.use(express.json());
  18.  
     
  19.  
    // 配置解析表单数据的中间件 处理 x-www-form-urlencoded
  20.  
    app.use(express.urlencoded({
  21.  
    extended: false
  22.  
    }));
  23.  
    app.use(cookieParser());
  24.  
    app.use(express.static(path.join(__dirname, 'public')));
  25.  
     
  26.  
    //token解析中间件 一定要在路由之前配置解析 Token 的中间件
  27.  
    const expressJWT = require('express-jwt')
  28.  
    //映入解密
  29.  
    const config = require('./config')
  30.  
    // 注册全局中间件 链式调用 unless 方法,接收一个配置对象,path 字段设置一个正则表达式,表示不需要 token 身份认证的路由前缀。
  31.  
    app.use(expressJWT({
  32.  
    // 加密时设置的密钥
  33.  
    secret: config.jwtSecretKey,
  34.  
    // 设置算法
  35.  
    algorithms: ['HS256'],
  36.  
    // 无token请求不进行解析,并且抛出异常
  37.  
    // credentialsRequired: false
  38.  
    }).unless({
  39.  
    path: [
  40.  
    '/users/add',
  41.  
    '/users/login',
  42.  
    {
  43.  
    url: /^\/public\/.*/,
  44.  
    methods: ['GET', 'POST']
  45.  
    }
  46.  
    ]
  47.  
    // path: ['/users/login','/users']
  48.  
    }))
  49.  
     
  50.  
    // 【必须在配置 cors 中间件之前,配置 JSONP 的接口】
  51.  
    app.get('/api/jsonp', (req, res) => {
  52.  
    // TODO: 定义 JSONP 接口具体的实现过程
  53.  
    // 1. 得到函数的名称
  54.  
    const funcName = req.query.callback
  55.  
    // 2. 定义要发送到客户端的数据对象
  56.  
    const data = {
  57.  
    name: 'zs',
  58.  
    age: 22
  59.  
    }
  60.  
    // 3. 拼接出一个函数的调用
  61.  
    const scriptStr = `${funcName}(${JSON.stringify(data)})`
  62.  
    // 4. 把拼接的字符串,响应给客户端
  63.  
    res.send(scriptStr)
  64.  
    })
  65.  
     
  66.  
    // 一定要在路由之前,配置 cors 这个中间件,从而解决接口跨域的问题
  67.  
    const cors = require('cors')
  68.  
    app.use(cors())
  69.  
     
  70.  
    //定义第一个全局中间件
  71.  
    app.use((req, res, next) => { //res用于返回客户端 req客户端的请求参数 next() 提交给下一个中间件
  72.  
    // 只允许 请求方法
  73.  
    res.setHeader('Access-Control-Allow-Methods', 'POST, GET, DELETE,HEAD')
  74.  
    // 响应头 允许所有的 HTTP 请求方法
  75.  
    res.setHeader('Access-Control-Allow-Methods', '*')
  76.  
    // 如果指定了 Access-Control-Allow-Origin 字段的值为通配符 *,表示允许来自任何域的请求
  77.  
    res.setHeader('Access-Control-Allow-Origin', '*')
  78.  
    // res.setHeader("Access-Control-Allow-Headers", "content-type,Authorization");
  79.  
    next();
  80.  
    })
  81.  
     
  82.  
     
  83.  
     
  84.  
    //封装错误处理函数
  85.  
    app.use((req, res, next) => {
  86.  
    res.cc = function(err, status = 1, data = {}) {
  87.  
    res.send({
  88.  
    status,
  89.  
    data,
  90.  
    message: err instanceof Error ? err.message : err
  91.  
    })
  92.  
    }
  93.  
    next()
  94.  
    })
  95.  
     
  96.  
    // 错误中间件 当token失效时 返回信息
  97.  
    app.use((err, req, res, next) => {
  98.  
    if (err.name === 'UnauthorizedError') {
  99.  
    res.status(401).send({
  100.  
    status: 1,
  101.  
    data: {},
  102.  
    message: '身份认证失败!'
  103.  
    });
  104.  
    }
  105.  
    });
  106.  
     
  107.  
    // 挂载路由
  108.  
    app.use('/', indexRouter);
  109.  
     
  110.  
    /* 用户路由 */
  111.  
    app.use('/users', usersRouter);
  112.  
     
  113.  
    module.exports = app;

 跨域这部分和jsonp参考了以下文章 

这篇好文章是转载于:学新通技术网

  • 版权申明: 本站部分内容来自互联网,仅供学习及演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,请提供相关证据及您的身份证明,我们将在收到邮件后48小时内删除。
  • 本站站名: 学新通技术网
  • 本文地址: /boutique/detail/tanhggggeg
系列文章
更多 icon
同类精品
更多 icon
继续加载