NodeJS

Nodejs

通过终端打开nodejs

e:
cd vn\html\js
node nidi.js

fs读写模块

导入模块

const fs = require('fs')

读取指定文件中的内容fs.readFile()

fs.eadFile(path[,options],callback)//用中括号包起来说明是可选参数
//path	必选参数,字符串,表示文件路径
//options	可选参数,表示以说明编码格式来读取文件
//callback	必选参数,文件读取完成后,通过后调函数拿到读取的结果

//以utf8的编码格式,读取指定文件的内容,并打印err和dataStr的值
//以utf8的编码格式,读取指定文件的内容,并打印err和dataStr的值
//1.导入fs模块,来操作文件
const fs = require('fs')

//2.调用fs.readFile()方法读取文件
//  参数1:读取文件路径
//  参数2:读取文件时采用的编码格式,一般默认utf8
//  参数3:回调函数,拿到读取失败和成功的结果 err dataStr
fs.readFile('1.txt','utf8',function(err,dataStr) {
    //2.1打印失败的结果
    //如果读取成功,则err的值为 null
    //如果读取失败,则err的值为 错误对象,dataStr 的值为 underfined
    console.log(err)
    console.log('------')
    //2.2打印成功的结果
    console.log(dataStr)
})
//判断读取文件是否成功
const fs = require('fs')

fs.readFile('1.txt','utf8',function(err,dataStr) {
    if (err) {
        return console.log('读取文件失败!' + err.message)
    }

    console.log('读取文件成功!内容是:' + dataStr)
})

向指定的文件中写入内容fs.writeFile()

fs.writeFile(file,data[,options],callback)
//file	必选参数,需要指定一个文件路径的字符串,表示文件的存放路径
//data	必选参数,表示写入的内容
//options	可选参数,表示以什么格式写入文件内容,默认值是utf8
//callback	必选参数,文件写入完成后的回调函数
//1.导入fs模块,来操作文件
const fs = require('fs')

//2.调用fs.writeFile()方法,写入文件内容
//	参数1:表示文件的存放路径
//	参数2:表示要写入的内容
//	参数3:回调函数
fs.writeFile('2.txt','wo si ni di',function(err) {
    //2.1如果文件写入成功,则 err 的值等于 null
    //如果文件写入失败,则 err 的值为错误对象
	console.log(err)
})
//判断文件是否写入成功
const fs = require('fs')

fs.writeFile('2.txt','wo si ni di',function(err) {
    if(err) {
		return console.log('文件输入失败!' + err.message)
    }
    console.log('文件写入成功')
})

path路径模块

const fs = require('path')

路径拼接path.join()

将多个路径片段拼接为完整的路径字符串

path.jon([...paths])
//...paths<string>	路径片段的序列
//放回值<string>

涉及路径拼接的操作,都要使用path.join()进行处理

const patStr = path.join(__dirname,'1.txt')//__dirname显示当前文件所在目录
cnosole.log(pathStr)//输出 当前文件所在目录\1.txt

获得路径中的文件名path.basename()

path.basename(path[,ext])
//path<string> 必选参数,表示一个路径的字符串
//ext<string>	可选参数,表示文件拓展名
//放回值<string>	表示路径中的最后一部分
const path = require('path')

//定义文件的存放路径
const fpath = '/a/b/c/index.html'

const fullName = path.basename(fpath)
console.log(fullName)//输出index.html

const nameWithoutExt = path.basename(fpath)
console.log(nameWithoutExt)//输出index

获取路径中的拓展名path,extname()

const fpath = '/a/b/c/index.html'

const fext = path.extname(fpath)
console.log(fext)//输出 .html

http模块

创建web服务器的模块

//1.导入http模块
const http = require('http')
//2.创建web服务器实例
const server = http.createServer()
//3.用server.on为服务器实例绑定request事件,监听客户端请求
server.on('request',function(req,res) {
    console.log('someone visit our web server')
})
//4.启动服务器
//server.listen(端口号,回调函数)
server.listen(80,function() {
    console.log('server running at http://127.0.0.1:80')
})
req请求对象

只要服务器接收到客户端请求,就会调用通过server.on()为服务器绑定的request事件处理函数

server.on('request',(req)=> {
    //req.url是客户端请求的URL地址
    const url = req.url
    //req.method是客户端请求的method类型
    const method = req.method
    //`${url}`占位符 定义多行字符串,还可以加入变量和表达式
    const str = `your request rul is ${url},and request method is ${method}`
    console.log(str)
})
res响应对象

res.end()将内容响应给客户端

server.on('request',(req,res)=> {
    const str = `your request rul is ${req.url},and request method is ${req.method}`    
    //调用res.end()
    //向客户端发送指定内容,并结束这次请求的处理过程
    res.end(str)
})

解决中文乱码的方法:调用res.setHeard()自定义响应头

res.setHeader('Content-Type','text/html; charset=utf-8')
根据不同的url响应不同的html内容

1.获取请求的url地址
2.设置默认的响应内容为 404 Not found
3.判断用户请求的是否为/或/index.htm首页
4.判断用户请求的是否为/about.html关于页面
5.设置Content-Type响应头,防止中文乱码
6.使用res.end()把内容响应给客户端

server.on('request', (req, res) => {
    //1.获取 请求的url地址
    const url = req.url
    //2.设置 默认的响应内容 为 404 Not found
    let content = '404 Not found'
    //3.判断用户请求的是否为 /或/index.html首页
    //4.判断用户请求的是否为 /about.html关于页面
    if (url === '/' || url === '/index.html'){
        content = '<h1>首页</h1> '
    }else if (url === '/about.html') {
        content = '<h1>关于页面</h1> '
    }
    //5.设置 Content - Type响应头 防止中文乱码
    res.setHeader('Content-Type', 'text/html; charset=utf-8')
    //6.使用 res.end() 把内容响应给客户端
    res.end(content)
})

模块化

module.exports对象

自定义模块中,将模块内的成员共享,供外界使用
外界用require()导入自定义模块时,得到的是module.exports所指的对象(默认为空)

使用require()导入模块时,导入的结果永远以module.exports指向的对象为准

//A
const nidi = require('./自定义模块')
console.log(nidi)
//使用require()导入模块时,导入的结果永远以module.exports指向的对象为准
//打印 {uname:'iam67',sayHi()}
//不打印 {username='luxiaotao',sayHello()}

//B
//向module.exports 对象上挂载 username 属性
module.exports.username = 'luxiaotao'
//向module.exports 对象上挂着sayHello 方法
module.exports.sayHello = function() {
    console.log('hello')
}
//让moduele.exports指向一个全新的对象
module.exports = {
	uname:'iam67'
    sayHi() {
        console.log('Hi')
    }
}

exports对象 默认情况下和module.exports指向同一个对象

包(第三方模块)

在npmjs网站上搜索包

//在项目中安装包
npm i 包的完整名称	//在终端中运行

安装指定版本的包

//通过@指定具体版本
eg. npm i moment@2.22.2

Express

创建基本web服务器

//1.导入express
const express = require('express')

//2.创建web服务器
const app = express()

//3.启动web服务器
app.listen(80, () => {
    console.log('express server running at http://127.0.0.1')
})
监听GET/POST请求
//参数1:客户端请求的URL地址
//参数2:请求对应的处理函数
//		req:请求对象(包含了与请求有关的属性与方法)
//		res:响应对象(包含了与响应有关的属性与方法)
app.get('URL', (req, res) => {/*处理函数*/})
//POST语法格式同GET
//监听客户端的GET和POST请求,并向客户端响应具体的内容
app.get('/user', (req, res) => {
    //调用express提供的 res.send() ,向客户端响应一个JSON对象
    res.send({name:'luxiaotao'})
})

app.post('/user',(req, res) => {
    //调用express提供的 res.send() ,向客户端响应一个文本字符串
    res.send('请求成功')
})
处理参数

获取URL中携带的查询参数

app.get('/', (req, res) => {
    //客户端使用 ?name=luxiaotao&age=20 这种查询字符串形式,发送到服务器的参数
    //通过 req.query 可以获取得客户端发送过来的查询参数
    //eg. req.query.name
    //注意:默认情况下,req.query 是一个空对象
    console.log(req.query)
    res.send(req.query)
})

获取RUL中的动态参数

//URL地址中,可以通过 :参数名 的形式,匹配动态参数值
app.get('/user:id', (req, res) => { //这里的:id是一个动态参数
    //req.papams 是动态匹配到的URL参数,默认是一个空对象
    //里面存放着通过:动态匹配到的参数值
    console.log(req.params)
})
托管静态资源
创建静态资源服务器express.static()

将public目录下的图片,CSS文件,JavaScipt文件对外访问

如果要托管多个静态资源目录,要多次调用express.static()

app.use(express.static('./public'))//public是文件名
挂载路径前缀
app.use('/wosinidi', express.static('./public'))
//通过带有/wosinidi的前缀地址来访问public目录中的文件
//http://127.0.0.1/wosinidi/index.html

路由

客户端的请求与服务器处理函数之间的映射关系

按照定义的先后顺序进行匹配
请求类型请求的URL同时匹配成功才会调用对应的处理函数

//参数METHOD	请求的类型
//参数PATH	请求的URL地址
//参数HANDLER	处理函数
app.METHOD(PATH, HANDLER)
//挂载路由最简单的方法(不常用)
app.get('/', (req,res) => {res.send('get request')})
app.post('/', (req,res) => {res.send('post request')})

为了方便对路由进行模块化的管理,Express不建议将路由直接挂载到app上,而是推荐将路由抽离为单独的模块

//A	创建路由模块
//1.导入express
const express = require('express')

//2.调用 express.Router() 函数创建路由对象
const router = express.Router()

//3.向路由对象上挂载具体的路由
router.get('/user/list', (req,res) => {
	res.send('get user list')
})
router.post('/user/list', (req,res) => {
	res.send('add new user')
})
//4.使用 module.exports 向外共享路由对象
module.exports = router
//B	5.使用 app.use() 函数注册路由模块
const express = require('express')
const app = express()

//导入路由模块
const router = require('./A')
//注册路由模块
app.use('/wosinidi', router)//同一添加访问前缀
//app.use()函数的作用,用来注册中间件

app.listen(80, ()=> {
    
})

中间件

1.一定要在路由之间注册中间件
2.客户端发送过来的请求,可以连续使用多个中间件进行处理
3.执行完中间件的业务代码之后,不要忘记调用next()函数
4.为了防止代码逻辑混乱,调用next()函数后不要再写额外的代码
5.连续调用多个中间件,多个中间件之间,共享req和res对象

有next的就是中间件

//定义最简单的中间件函数
const nw = function(req,res,next) {
    console.log('这是一个最简单的中间件函数')
    //把流转关系,转交给下一个中间件或路由
    next()
}
全局生效的中间件

客户端发起的任何请求,到达服务器之后,都会触发的中间件

//通过调用app.use(中间件函数),即可定义全局生效的中间件
const nw = function(req,res,next) {
    console.log('这是一个最简单的中间件函数')
    //把流转关系,转交给下一个中间件或路由
    next()
}
//将nw注册为全局生效中间件
app.use(nw)

app.get('/', (req,res) => {
    res.send('hello world')
})

-----------------------------------
//定义全局中间件的简化形式
app.use((req,res,next) => {
    console.log('这是一个简单的中间件')
    next()
})
局部生效的中间件

不使用app.use()定义的中间件

//1.定义中间件函数
const mw1 = (req,res,next) => {
    console.log('调用了局部生效的中间件')
    next()
}

//2.创建路由
//局部生效的中间件函数	只会在访问url为'/'时生效,不会影响其他路由
app.get('/',mw1, new2 ,(req,res) => {
    res.send('home page')
})
app.get('/', (req,res) => {
    res.send('user page')
})
中间件在实际使用的作用

多个中间件之间,共享同一份req和res。基于这样的特性我们可以在上游的中间件,统一为req或res对象添加自定义的属性或方法,供下游的中间件或路由使用

app.use((req,res,next) => {
    //获取到请求到达服务器的事件
    const time = Date.now()
    //为req对象挂载自定义属性,从而把事件共享给后面所有路由
    req.startTime = time
    next()
})

app.get('/', (req,res) => {
    res.send('time is' + req.startTime)
})
错误级别中间件

捕获整个项目中发生的异常错误,从而防止项目异常崩溃的问题
格式:在function函数中,必须有4个形参,顺序为(err,req,res,next)

app.use((err, req, res, next) => {
    console.log('发生了错误' + err.message)
    res.send('Error:' + err.message)
}

必须放在所有路由之后

解析请求体数据

解析JSON格式的请求体数据

app.use(express.json())

解析URL -encoded格式的请求体数据

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

在服务器端,可以通过req.body
来获取JSON格式和URL -encoded格式的请求体数据

接口

express接口
//A
const express = requier('express')
const app = express()

//配置解析表单数据的中间件
app.use(express.urlencoded({ extended: false}))

//导入路由模块
const router = require('./B')
//把路由模块注册到app上
//当请求到达服务器,先要经过这个全局中间件的处理,进入后进行匹配,调用对应路由接口
app.use('/api', router)

app.listen(80, () => {
    console.log('server is running at http://127.0.0.1')
})

//B 接口
const express = requier('express')
const router = express.Router()

//挂载对应路由
//定义GET接口
router.get('/get', (req, res) => {
    //通过req.query获取客户端通过查询字符串,发送到服务器的数据
    const query = req.query
    //通过res.send(),向客户端响应处理的结果
    res.send({
        status: 0,  //0表示成功,1表示失败
        meg: 'GET请求成功', //状态的描述
        data: query //需要响应给客户端的数据
    })
})
//定义POST接口
router.post('/post', (req, res) => {
    //通过req.body获取请求体中包含的 url -encoded 格式的数据
    const body = req.body
    //通过res.send(),向客户端响应处理的结果
    res.send({
        status: 0,  //0表示成功,1表示失败
        meg: 'POST请求成功', //状态的描述
        data: body //需要响应给客户端的数据
    })
})
//向外界共享router
module.exports = router
解决跨域

编写的GET和POST接口不支持跨域请求
解决接口跨域的方案有两种

  1. CORS(主流)
  2. JSONP(有缺陷,只支持GET请求)

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

//安装
npm i cors
const cors = require('cors')	//导入中间件
app.use(cors())	//配置成全局中间件

什么是CORS?
CORS是一系列HTTP响应头组成,这些 HTTP响应头绝对浏览器是否阻止前端JS代码跨域获取资源

设置CORS响应头解决跨域

//允许所有的HTTP请求方法
res.setHeader('Access-Control-Allow-Origin','*')

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

什么是JSONP
浏览器端通过<script标签的src属性,请求服务器上的数据,同时,服务器返回一个函数的调用。这种请求数据的方式叫做JSONP

JSONP接口(只支持get)

如果已经配置了CORS跨域资源共享,为了防止冲突必须在配置CORS中间件之前声明JSONP的接口,否则JSONP接口会被处理成开启CORS的接口

//A	编写JSON接口
app.get('/api/jsonp', (req,res) => {
    //1.得到函数的名称
    const funcName = req.query.callback
    //2.定义要发送给客户端的数据对象
    const data = { name:'luxiaotao', age:22 }
    //3.拼接出一个函数的调用				将data转义成JSON
    const scriptStr = `${funcName}(${JSON.stringify(data)})`
    //4.把拼接的字符串,响应给客户端
    res.send(scriptStr)
})
//B 在网页中使用jQuery发起JSONP请求
//调用$.ajax()函数,提供JSONP的配置选项,从而发起JSONP请求
<script>
    $.ajax({
	type:'GET'	//指定请求方式
    url:'http://127.0.0.1/api/jsonp'	//请求的URL地址
    dataType:'jsonp'	//告诉服务器发起的是JSONP请求
    success:function (res) {
		console.log(res)	//执行回调函数,打印res
	}
})

数据库

组织存储管理数据的仓库

在项目中操作mysql
1.安装mysql模块 npm i mysql
2.配置mysql模块

//导入mysql模块
const mysql = require('mysql')
//配置对象,建立于mysql数据库的连接
const db = mysql.createPool({
    host:'127.0.0.1',		 //数据库的IP地址
    user:'root',			 //登录数据库的账号
    password:'qqqqqqq7',	 //登录数据库的密码
    database:'my_db_01'		 //指定操作哪个数据库
})

检测MySQL模块是否能正常工作

db.query('select 1', (err,results) => {
    //mysql模块工作期间报错了
    if (err) return console.log(err.message)
    //能够成功的执行SQL语句
    console.log(results)
})
查询数据select
//查询users表中所有的数据
db.query('select * from users', (err, results) => {
    //查询失败
    if (err) return console.log(err, nessage)
    //查询成功
    //如果执行的是 select 查询语句,则执行的结果是数组
    console.log(results)
})
新增数据insert into
//向users表中新增一条数据,齐总username的值为luxiaotao,password的值为123456
const user = {username: 'luxiao', password:'123456'}
//定义待执行的sql语句   ?是占位符
const sqlStr = 'insert into users (username, password) values (?, ?)'
//执行sql语句
db.query(sqlStr, [user.username, user.password], (err, results) => {
    //执行sql语句失败了
    if(err) return console.log(err.message)
    //成功了    
    //如果执行的是inser into 语句,则result是一个对象
    //可以通过affectedRows属性,来判断是否插入数据成功
    if (results.affectedRows === 1) {
        console.log('插入数据成功')
    } 
})
-----------------------------------------------------------
//如果数据对象的每个属性和数据表的字段一一对应,则有插入数据的便捷方式
const user = { username: 'luxiaotao', password: '123456' }
//定义待执行的sql语句
const sqlStr = 'insert into users set ?'
//执行sql语句
db.query(sqlStr, user, (err,results) => {
    if (err) return console.log(err.message)
    if (results.affectedRows === 1){
        console.log('插入数据成功')
    }
})
更新数据update
//演示如何更新用户的信息
const user = { id:'45', username: 'nidi', password: '654321' }
//定义sql语句
const sqlStr = 'update users set username=?, password=? where id=?'
//执行sql语句
db.query(sqlStr, [user.username, user.password, user.id], (err, results) => {
    if(err) return console.log(err.message)
//执行update语句之后,执行的结果也是一个对象,可以通过affectedRows判断是否更新成功
    if(results.affectedRows === 1) {
        console.log('更新成功')
    }
}) 
--------------------------------------------------------------
//演示更新数据的快捷方式
const user = { id: '45', username: 'luxiao', password: '123123' }
//定义sql语句
const sqlStr = 'update users set ? where id=?'
//执行sql语句
db.query(sqlStr, [user, user.id], (err, results) => {
    if (err) return console.log(err.message)
    if (results.affectedRows === 1) {
        console.log('更新数据成功')
    }
})
删除数据delete

不安全,不能恢复数据

//删除id为48的用户
const sqlStr = 'delete from users where id=?'
//如果sql语句中有多个占位符?,则必须使用数组为每个占位符指定的值
//如果sql语句只有一个占位符?,则可以省略数组
db.query(sqlStr, 48, (err, results) => {
    if(err) return console.log(err.message)
    //执行delete语句之后,结果也是一个对象,也会包含affectedRows属性
    if(results.affectedRows === 1){
        console.log('删除数据成功')
    }
})
标记删除status

使用delete语句,会真正的把数据从表中删除,不安全,使用标记删除的形式,来模拟删除的动作
标记删除:在表中设置类似status这样的状态字段,来标记当前这条数据是否删除
当执行了删除的动作时,并没有执行delete将数据删除,而是执行update,将这条数据对应的status标记为删除即可

//标记删除
const sqlStr = 'update users set status=? where id=?'
db.query(sqlStr, [1, 39], (err, results) => {
    if(err) return console.log(err.message)
    if(results.affectedRows === 1) {
        console.log('标记删除成功')
    }
})
前后端的身份验证

Web开发模式
1.服务器渲染的Web开发模式(不需要太多交互的使用)
服务器渲染:服务器发送给客户端的HTML页面,是在服务器通过字符串的拼接动态生成的,因此,客户端不需要Ajax额外请求页面的数据

优点:
1.前端耗时少
2.有利于SEO(搜索引擎优化)
缺点:
1.占用服务器端资源
2.不利于前后端分离,开发效率低

app.get('/index.heml', (req, res) => {
    //1.要渲染的数据
    const user = { name:'zs', age:'20'}
    //2.服务器端通过字符串的拼接,动态生成HTML内容
    const html = `<h1>姓名:${user.name},年龄:{user.age}</h1>`
    //3.把生成好的页面内容响应给客户端,因此,客户端拿到的是带有真实数据的HTML页面
    res.send(html)
})

2.前后端分离的Web开发模式(需要太多交互的使用)
依赖于Ajax技术,后端只负责提供API接口,前端使用Ajax调用接口

优点:
1.开发体验好
2.用户体验好,可以实现局部刷新
3.减轻了服务器端的渲染压力
缺点:
1.不利于SEO

身份认证
服务器渲染:Session认证机制
前后端分离:JWT认证机制

Session认证机制

HTTP协议的无状态性
指的是客户端的每次HTTP请求都是独立的,连续多个请求之间没有直接的关系,服务器不会主动保留每次HTTP请求的状态

如何突破HTTP无状态的限制
会员卡身份认证方式:Cookie

什么是Cookie
Cookie是存储在用户浏览器中的一段不超过4KB的字符串,由名称、值、和其他几个用于控制Cookie有效期、安全性、使用范围的可选属性组成
不同域名下的Cookie各自独立,每当客户端发起请求时,会自动当前域名下所有没过期的Cookie一同发送到服务器
1.自动发送 2.域名独立 3.过期时限 4.4KB限制

Cookie在身份认证中的作用
客户端第一次请求服务器的时候,服务器通过响应头的形式,向客户端发送一个身份认证的Cookie,客户端会自动的将Cookie保存在浏览器中
当客户端浏览器每次请求服务器的时候,浏览器会自动将身份认证的Cookie,通过请求头的形式发送给服务器,服务器即可验明客户端的身份

Cookie不具有安全性
Cookie存储在浏览器中,而且浏览器提供了读写Cookie的API,因此Cookie很容易被伪造,不具有安全性。因此不建议服务器将重要的隐私数据,通过Cookie的形式发送给浏览器

提高身份认证的安全性
会员卡加刷卡认证,不仅要具有cookie,还要对cookie进行认证

在Express中使用Session认证

Session认证机制需要配合Cookie才能实现,由于Cookie默认不支持跨域访问,使用当设计前端跨域请求后端接口的时候,需要做很多额外的配置
1.当前端请求后端接口不存在跨域问题,使用Session身份认证机制
2.当前端跨域请求后端接口时,使用JWT认证机制

npm i express-session

配置express-session中间件

//导入session中间件
const session = require('express-session')

//配置session中间件
app.use(session({
    secret: 'nidi',             //任意字符串
    resave: false,              //固定写法
    saveUninitialized: true     //固定写法
}))

向session中存/取/清空数据
当中间件配置成功,用req.session来访问和使用session对象,从而存储/调用/清空数据

//存储数据
//登录的API接口
app.post('/api/login', (req,res) =>{
    //判断用户提交的登录信息是否正确
    if(req.body.username !== 'admin' || req.body.password !== '000000') {
        return res.send({ status: 1, msg: '登陆失败'})
    }
    
    //将登录成功后的用户信息保存到Session中
    //只有配置了express-session 中间件后,才能通过req点出来session属性
    req.session.user = req.body     //用户的信息
    req.session.islogin = true      //用户的登录状态

    res.send({ status: 0, msg: '登陆成功'})
})
-----------------------------------------------------
//取数据
//获取用户姓名的接口
app.get('/api/username', (req,res) => {
    //从session中获取用户的名称,响应给客户端
    if(!req.session.islogin) {	//判断里面是否存有值
        return res.send({ status: 1, msg: 'fail'})
    }	
    res.send({
        status: 0,
        msg: 'success',
        username: req.session.user.username,
    })
})
--------------------------------------------------
//清空session信息
//退出登录的接口
app.post('/api/logout', (req,res) => {
    res.session.destroy()
    res.send({
        status: 0,
        msg: '退出登录成功'
    })
})
JWT认证机制

JWT由HeaderPayloadSignature组成,由.分隔开
Payload是真正的用户信息,Header和Signature是安全信息相关内容

JWT使用方式
客户端收到服务器返回的JWT后,通常储存在localStorage和sessionStorage中
此后客户端每次与服务器通讯,都要带上JWT字符串从而进行身份验证,把JWT放在HTTP请求头的Authorization字段中

Authorization: Bearer <token>
在 Express中使用JWT
npm i jsonwebtoken express-jwt

导入JWT的包

//导入用于生成JWT字符串的包
const jwt = require('jsonwebtoken')
//导入用于将客户端发送过来的JWT字符串,解析还原成JSON对象的包
const expressJWT = require('express-jwt')

定义secret密钥
为了保证JWT字符串的安全性,防止JWT字符串在网络传输过程中被别人破解,专门定义一个用于加密解密的secret密钥:
1.生成JWT字符串的时候,需要使用secret密钥对用户的信息进行加密,最终得到加密好的JWT字符串
2.把JWT字符串解析还原成JSON对象时,需要使用secret密钥进行解密

//secret密钥的本质:就是一个字符串
const secreKey = 'wosinidi'

在登录成功后生成JWT字符串

//登录接口
app.post('/api/login', function(req, res) => {
	//...省略登录失败情况下的代码
    //用户登录成功之后,生成JWT字符串,通过token属性响应给客户端
    res.send({
		status: 200,
         message: '登陆成功',
//调用jwt.sign() 生成JWT字符串,三个参数分别是
         //用户信息对象,加密密钥,配置对象(可以配置当前token有效期)
         token:jwt.sign({username: userinfo.username}, secreKety, { expiresIn:'30s'})
    })
})

将JWT字符串还原为JSON对象

//使用app.use()来注册中间件
//expressJWT({ secret: secretKey }) 用来解析Token中间件
// .unless({ path: [/^\/api\//]}) 用来指定哪些接口不需要访问权限
//凡是以/api开头的接口都不需要访问权限
app.use(expressJWT({ secret: secretKey }).unless({ path: [/^\/api\//]}))

使用req.user获取用户信息
使用req.user对象来访问从JWT字符串中解析出来的用户信息
(7x版本改为了req.auth)

//这是一个有权限的API接口
app.get('/admin/getinfo', (req, res) => {
	//使用req.user获取用户信息,使用data属性将用户信息发送到客户端
    console.log(req.user)
    res.send({
		status: 200,
        message: '获取用户信息成功',
        data: req.user,	//要发送到客户端的信息
    })
})

捕获解析JWT失败后产生的错误
当使用express-jwt解析Token字符串时,如果客户端发送过来的Token字符串过期或不合法,会产生一个解析失败的错误,影响项目的正常运行,通过Express的错误中间件,捕获这个错误并进行相关的处理

//放在所有路由的后面
//使用全局错误处理中间件,捕获解析JWT失败后产生的错误
app.use((err, req, res, next) => {
    //这次错误是由token解析失败导致的
    if (err.name === 'UnauthorizedError') {
        return res.send({
			status: 401,
            message: '无效的token',
        })
    }
    res.send({
        status: 500,
        message: '未知的错误'
    })
})

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值