Node教程——封装一个token验证器
蓝图blueprint,git地址:https://github.com/BM-laoli/Node-token-forApi-blueprint
重要说明
这个轮子是 使用 express@5.0 + MongoDB构建起来的一个 node后台通用的验证器,里面主要讲的就是使用jwt,token进行验证,当然你想使用session也没问题,但是这个蓝图工程只包含了token字段内容
首先是初始化我们的项目,
主要是 安装一些东西
- 项目的初始化
如下是我们的项目文件夹结构
.jpg)
- 项目的包管理
首先我们需要使用express@next框架,因为只用next才能在里面使用async es7的一些东西,
我们还需要mongoose来操作数据库
我们还需要bcrypt对数据库里面的密码进行加密
我们还需要jsonwebtoken快捷的生成token
npm install express@next
npm install mongoose
npm install bcrypt
npm install jsonwebtoken
好了以上就是我们需要做的
设计路由逻辑
首先我们需要在这里设计几个接口,他们是,并且完成post请求的配置解析json
- 测试接口
- 注册接口
- 登录接口
- 获取所有用户信息接口
- 等录之后的权限校验接口
- 构建入口,并且完成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');
})
- 构建分路由
注意这里只是简单的做一些功能测试,后续我们会把这个东西都删掉,把验证丢给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')
})
- 完成对应的接口测试
/.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项目
优化项目结构打包,我做了那些事?(主要就是以下爱的事情)
- 优化目录结构
- 整理接口 去掉了没有用的接口,只保留了一些基础的接口
- 使用说明:你只需要npm install 就能实现token的验证了,需要验证之后的接口请在admin之后的路由书写,当然你可以自定义路由,拿着我的auth去做验证就可以了
- 建议你使用非对称加密的密钥对,进行token的加密,你可以通过引入文件去配置你的加密信息
Node教程——封装一个token验证器的更多相关文章
- 【Android Developers Training】 93. 创建一个空验证器
注:本文翻译自Google官方的Android Developers Training文档,译者技术一般,由于喜爱安卓而产生了翻译的念头,纯属个人兴趣爱好. 原文链接:http://developer ...
- 手把手从零开始---封装一个vue视频播放器组件
现在,在网页上播放视频已经越来越流行,但是网上的资料鱼龙混杂,很难找到自己想要的,今天小编就自己的亲身开发体验,手把手从零开始---封装一个vue视频播放器组件. 作为一个老道的前端搬砖师,怎么可能会 ...
- 用Unity写一个12306验证器的恶搞图生成软件
前言 前一阵子是买火车票的高峰期,然后12306的验证码就遭到各种吐槽.其实大部分验证码没有那么难,大家只是因为买不到票 发泄一下不满的情绪.于是各种恶搞的图就出现了,比如找二次元里人物的矮子,找好男 ...
- 实现一个可配置的java web 参数验证器
当使用java web的servlet来做接口的时候,如果严格一点,经常会对请求参数做一些验证并返回错误码.我发现通常参数验证的代码就在servlet里边,如果参数不正确就返回相应的错误码.如果接口数 ...
- 谷歌验证器的原理及JS实现
阅读本篇文章你可以了解到谷歌验证器的实现原理,并且可以自己使用node.js实现支持谷歌验证器的两步验证. 这两年发现身边的很多应用和网站纷纷支持两步验证,并且呼吁用户使用两步验证. 并且发现,除了A ...
- 9、 Struts2验证(声明式验证、自定义验证器)
1. 什么是Struts2 验证器 一个健壮的 web 应用程序必须确保用户输入是合法.有效的. Struts2 的输入验证 基于 XWork Validation Framework 的声明式验证: ...
- flask 自定义验证器(行内验证器、全局验证器)
自定义验证器 在WTForms中,验证器是指在定义字段时传入validators参数列表的可调用对象,下面来看下编写自定义验证器. 行内验证器 除了使用WTForms提供的验证器来验证表单字段,我们还 ...
- 雷林鹏分享:XML 验证器
XML 验证器 使用我们的 XML 验证器来对您的 XML 文件进行语法检查. XML 错误会终止您的程序 XML 文档中的错误会终止您的 XML 应用程序. W3C 的 XML 规范声明:如果 XM ...
- 自研后端HTTP请求参数验证器服务ParamertValidateService
好处:方便了后端对HTTP请求中参数进行核验,只需一次编写效验器,一行代码便可对所有参数的pojo进行参数核验!而且更改效验逻辑时只需要更改效验器类即可,实现了解耦合. 只需要程序员按照规范开发一个P ...
随机推荐
- stand up meeting for beta release plan 12/16/2015
今天我们开会讨论一下beta版需要的feature,其中待定的feature是可选做的,如果有时间.其他都是必须实现的. 因为做插件的计划失败了,所以我们现在是pdf阅读器和取词查词加入生词本这两部分 ...
- mysql数据库深入学习
mysql 数据库 一.数据库介绍 1.关系型数据库的特点 二维表 典型产品Oracle传统企业,MySQL是互联网企业 数据存取是通过SQL 最大特点,数据安全性方面强(ACID) 2.NoSQ ...
- 在vue中使用ztree树插件
插件资源及api:树官网 本事例是在vue3.0+中演示,事例是实际项目中正在用的组件所以部分打了马赛克. 1.插件准备(提前准备好插件文件) 可以直接在官网下载,搭建好脚手架后将准备好的文件放在li ...
- 如何防止CDN防护被绕过
当攻击者发现目标站点存在CDN防护的时候,会尝试通过查找站点的真实IP,从而绕过CDN防护.我们来看一个比较常见的基于公有云的高可用架构,如下:CDN(入口层)->WAF(应用层防护)-> ...
- Aria2任意文件写入漏洞
目录: 简介 漏洞描述 payload 漏洞复现 一.Aria2介绍 Aria2是一个命令行下运行,多协议,多来源下载工具(HTTP / HTTPS,FTP,BitTorrent,Metalink), ...
- API加密框架monkey-api-encrypt发布1.2版本
框架介绍 monkey-api-encrypt 是我之前写的一个API加密的框架,主要是将加密/解密的逻辑交给框架实现,等数据到达Controller后自动解密了,让开发人员不需要关注数据的加解密操作 ...
- Python 七步捉虫法
了解一些技巧助你减少代码查错时间. -- Maria Mckinley 在周五的下午三点钟(为什么是这个时间?因为事情总会在周五下午三点钟发生),你收到一条通知,客户发现你的软件出现一个错误.在有了初 ...
- Java类的使用
在一个Java文件中写两个类:一个基本的类,一个测试类.注意:文件名称和测试类名称一致. 如何使用呢?创建对象使用.如何创建对象呢?格式:类名 对象名 = new 类名(); Student s = ...
- java实现自定义哈希表
哈希表实现原理 哈希表底层是使用数组实现的,因为数组使用下标查找元素很快.所以实现哈希表的关键就是把某种数据类型通过计算变成数组的下标(这个计算就是hashCode()函数 比如,你怎么把一个字符串转 ...
- bootstrap4中使用fontawesome5.6.3
先下载fontawesome5.6.3,选择free for web,下载完解压,丢在资源目录下 <form action=""> <div class=&quo ...