zoukankan      html  css  js  c++  java
  • Node教程——封装一个token验证器

    蓝图blueprint,git地址:https://github.com/BM-laoli/Node-token-forApi-blueprint

    重要说明

    这个轮子是 使用 express@5.0 + MongoDB构建起来的一个 node后台通用的验证器,里面主要讲的就是使用jwt,token进行验证,当然你想使用session也没问题,但是这个蓝图工程只包含了token字段内容

    首先是初始化我们的项目,

    主要是 安装一些东西

    1. 项目的初始化

    如下是我们的项目文件夹结构

    ![img](file:///C:UsersADMINI~1AppDataLocalTemp1589190039(1).jpg)

    1. 项目的包管理

    首先我们需要使用express@next框架,因为只用next才能在里面使用async es7的一些东西,
    我们还需要mongoose来操作数据库
    我们还需要bcrypt对数据库里面的密码进行加密
    我们还需要jsonwebtoken快捷的生成token

    npm install express@next
    npm install mongoose
    npm install bcrypt
    npm install jsonwebtoken
    

    好了以上就是我们需要做的

    设计路由逻辑

    首先我们需要在这里设计几个接口,他们是,并且完成post请求的配置解析json

    • 测试接口
    • 注册接口
    • 登录接口
    • 获取所有用户信息接口
    • 等录之后的权限校验接口
    1. 构建入口,并且完成json的解析
      /app.js
    
    const express = require('express');
    
    const app = express();
    
    
    //解析一遍post参数
    app.use(express.urlencoded({ extended: true }))
    app.use(express.json())
    
    
    //路由分发器
    app.get('/test', async(req, res) => {
        res.send('测试打通!没问题')
    })
    
    
    app.use('/api', require('./route/index'))
    
    
    app.listen(3001, async() => {
        console.log('http://localhost:3001');
    })
    
    1. 构建分路由

    注意这里只是简单的做一些功能测试,后续我们会把这个东西都删掉,把验证丢给router去验证,目的就是模块化处理业务,请求主页不需要token请求管理的页的接口就需要验证

    /router/index.js

    const express = require('express');
    
    const indexApi = express.Router()
    
    
    indexApi.post('/register', async(req, res) => {
        console.log(req.body);
        res.send('register!!ok')
    })
    
    
    indexApi.post('/login', async(req, res) => {
        console.log(req.body);
        res.send('login!!ok')
    })
    
    
    indexApi.get('/users', async(req, res) => {
        res.send('users!!ok')
    })
    
    
    indexApi.get('/profile', async(req, res) => {
        res.send('profile!!ok')
    })
    
    
    
    
    
    1. 完成对应的接口测试
      /.http
    
    @uri = http://localhost:3001/api
    
    
    ### 测试
    GET {{uri}}
    
    ### 展示出所有的用户
    GET  {{uri}}/users
    
    
    
    ###  注册
    POST {{uri}}/register
    Content-Type: application/json
    
    {
        "username":"user2",
        "password":"123456"
    }
    
    ### 登录
    POST {{uri}}/login
    Content-Type: application/json
    
    {
        "username":"user2",
        "password":"123456"
    }
    
    
    ### 获取个人信息,传递的是当前保持状态了的用户
    GET {{uri}}/profile
    Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6IjVlYjhhNDIzOTM4YzdhNmQ3NDg5ZDJlMyIsImlhdCI6MTU4OTE2MDY0Nn0.UeSGbDgUrQaThemD18iIAGW6t-lc8R_R5tDvFamrgDw
    
    
    

    设计实现数据库Model

    在这里我们需要完成的工作有:

    • 使用mongoose连上数据库
    • 创建shcema规则
    • 使用规则创建集合
    • 倒出集合操作对象,创建集合交给理由或者中间件去做,这里功能比较简单我们可以直接丢给路由去做,但是为了保持编程的风格一致,我打算丢到中间件里面去

    我们把model都写在一个文件里有点不太妥当,当然这样做是完全没有问题的,如果项目有良好的架构我们可以后期考虑把他们分类的去构建model,比如与用户相关的molde都放在一个文件里,等等

    /model/model.js

    const mongoose = require('mongoose');
    const bcrypt = require('bcrypt');
    
    mongoose.connect('mongodb://localhost:27017/express-auth', { useUnifiedTopology: true, useNewUrlParser: true, useCreateIndex: true });
    
    
    const UserSchma = new mongoose.Schema({
    
        username: {
            type: String,
            unique: true //只需要usernam为唯一值
        },
    
        password: {
            type: String,
        }
    
    })
    
    const User = mongoose.model('User', UserSchma) //虽然这个表的名字是User但是实际上数据库创建的时候会给你变成users
    
    // 倒出数据操作对象
    module.exports = { User }
    

    注意啊,我们只是定义的集合还有数据库的表(集合)操作对象,还米有拿去业务中使用,接下里的章节我们会拿到具体的业务里去使用
    好了我们阶段性的回顾一下我们现在的文件夹里面都有哪些东西吧

    实现新增(实际上就是注册)用户接口

    这里我们定义就能实现我们的业务功能了,首先要说明的是,我们这里依然使用标准化的项目开放方式,把功能写在中间件里,

    这里的定义的中间件处理一个专门的业务就是User相关的业务,这是我工作中编写nodejs的全栈项目的一个习惯
    /middleware/users.js

    const { User } = require('../model/model')
    
    module.exports = {
    
        register: async(req, res, next) => {
            // console.log(req.body);
            let { username, password } = req.body
    
            const user = await User.create({
                username: username,
                password: password
            })
            req.user = user
    
            next()
        }
    }
    
    

    在路由里面你只需要弄这个就好了。实际上中间件就是一个对象

    const users = require('../middleware/users')
    +++
    indexApi.post('/register', users.register, (req, res) => {
        res.send(req.user)
    })
    
    +++
    
    

    实现展示用户功能

    前面我们实现了用户的注册新增功能,那么我们就来实现一些查看所有用户功能。
    用了前面的架构模式,我们的这个业务就可以全部写在middelwear里了,如果有设计赋值的操作。我们还可以创建一个工具middlewear,来帮助我们实现复杂的功能,这就是模块化开发

    /middleware/users.js

    +++
     //查看所有用户
        showUser: async(req, res, next) => {
    
            //为什么是find就可以了,因为mongoose给你封装了
            const user = await User.find();
    
            req.user = user;
            next();
    
        }
    +++
    
    
    

    /router/index.js

    const users = require('../middleware/users')
    +++
    indexApi.get('/users', users.showUser, async(req, res) => {
        res.send(req.user)
    })
    +++
    
    

    实现bcrypt加密密码

    实际上实现bcrypt的加密非常的简单,只需要调用方法就好了

    +++
      password: {
            type: String,
            set(val) { //val是自定义的保存前的加密,返回的值就是加密之后的密码
                return require('bcrypt').hashSync(val, 10) //进行散列之后的密码,10就是加密强度
            }
        }
    +++
    

    加密之后的密码验证怎么做?实际上这里就是登录功能

    实际上也非常的简单,先验证用户名是否正确 如果正确就去根据用户名查用户数据,然后拿到加密之后的密码,然后使用bcrypt自己的验证方式去验证就好了

    /middleware/users.js

    +++
       //登录器
        login: async(req, res, next) => {
    
            let { username, password } = req.body
    
            const user = await User.findOne({
                username: username
            })
    
            //验证用户名
            if (!user) {
                return res.status(422).send({ message: '用户名不存在' })
            }
            const isPasswordValid = bcrypt.compareSync(password,
                user.password
            )
    
            //验证密码
            if (!isPasswordValid) {
                return res.status(422).send({ message: '密码无效' })
            }
            req.user = user
            next()
        }
    +++
    
    

    /router/index.js

    indexApi.post('/login', users.login, async(req, res) => {
        res.send(req.user)
    })
    
    
    
    

    实现token的下发

    实际上这个也非常的简单,我们只需要在登录成功的时候给用户加上一个token就好了

    这里的业务逻辑核心,其实就是这个token该如何加

    • 修改一下我们的登录功能,使得用户登录的时候加上一个用以验证用户登录状态的token
      /middleware/users.js
    +++
     const jwt = require('jsonwebtoken')
    +++
    
    +++
     //登录器
        login: async(req, res, next) => {
        +++
    
            //生成token,jwtToken   
            //生成签名,我们给id丢进去据好了
        const token = jwt.sign({
                //加密的签名
                id: String(user._id),
                //密钥
            }, 'asdasdasdasdasdasdasdasdasdasdasd') //这个东西实际上是一串秘钥,用来对应每一个的tonken验证器,它应该被写一个单独的文件里
        res.user = {
            user,
            token
    
        }
        +++
    }
    
    • 我们看一些测试的结果
    {
      "user": {
        "_id": "5eb933c9cf3c3f33fcadb560",
        "username": "user2",
        "password": "$2b$10$n2OHQzuSuUtwWpg.YuiDO.FPM4Q9nrBdqANLB3Wkh67P.MonpIyYi",
        "__v": 0
      },
      "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6IjVlYjkzM2M5Y2YzYzNmMzNmY2FkYjU2MCIsImlhdCI6MTU4OTE5Nzc4MX0.a4vrQwTeGsuI320m1OYsjSB8abdxxm8TReKYg6UKbVQ"
    }
    

    实现用户的token校验.

    这里我们把auth做成一个中间件,这个中间件可以加载需要验证的路由的前面,如果通过验证就放行,要不然就放行next,这就是验证器auth的实现原理,非常的简单
    /middleware/auth.js

    
    const jwt = require('jsonwebtoken')
    const { User } = require('../model/model')
    module.exports = {
        auth: async(req, res, next) => {
            //注意啊这个字段是我们前端需要实现的,因为这是后台要求的
            let raw = String(req.headers.authorization).split(' ').pop() //我为啥要用空格分隔,因为我发起请求的时候多加了一个字段,
    
            const tokenData = jwt.verify(raw, 'asdasdasdasdasdasdasdasdasdasdasd')
            let { id } = tokenData
    
            
            //加到req上以便以给下一个中间件使用
            req.user = await User.findById(id)
            
            next()
    
        }
    }
    
    

    假设我们现在需要把这个auth用于我们的 profile接口做验证,那么我们可以这样来使用

    //核心token验证器
    indexApi.get('/profile', auth.auth, async(req, res) => {
        res.send(req.user)
    })
    

    注意,以上的所有都只是一个小小的demo。正式的打包再我这里

    整理好所有的目录,打包构建成蓝图并且发布上git

    • 我为什么要整理成蓝图?因为我希望我的token验证其能复用到很多地方去,假设我以后的项目需要用这个那么我就直接下载蓝图,这样我的token就不用我再去啰嗦的写了,这也实际上已经是一个初步的node框架的雏形了。

    我把它写在了blueprint_for_token_v3中。你可以直接git clone去使用它构建你的node项目

    优化项目结构打包,我做了那些事?(主要就是以下爱的事情)

    1. 优化目录结构
    2. 整理接口 去掉了没有用的接口,只保留了一些基础的接口
    3. 使用说明:你只需要npm install 就能实现token的验证了,需要验证之后的接口请在admin之后的路由书写,当然你可以自定义路由,拿着我的auth去做验证就可以了
    4. 建议你使用非对称加密的密钥对,进行token的加密,你可以通过引入文件去配置你的加密信息
  • 相关阅读:
    该伙伴事务管理器已经禁止了它对远程/网络事务的支持
    HDU 4883 TIANKENG’s restaurant (贪心)
    Android:创建可穿戴应用
    debian支持ll命令
    mongodb进阶一之高级查询
    Hadoop之——又一次格式化hdfs系统的方法
    J2EE的13个规范之(二) JDBC 及其使用
    2015欧冠决赛--脑力劳动结硕果
    运行计划之误区,为什么COST非常小,SQL却跑得非常慢?
    QVariant与自定义数据类型转换的方法
  • 原文地址:https://www.cnblogs.com/BM-laoli/p/12871945.html
Copyright © 2011-2022 走看看