express.js
初始express
官方给出的概念:Express是基于node.js平台,快速、开放、极简的web开发框架 通俗的理解: Express的作用和node.js内置的http模块类似,专门用来创建web服务器的 本质: 就是一个npm上的第三方包,提供了快速创建web服务器的便捷方法
Express能做什么
对于前端程序员来说,最常用的两种服务器,分别是:
- web网站服务器:专门对外提供web网页资源的服务器
- API接口服务器:专门对外提供API接口的服务器
使用Express,我们可以方便,快速的创建web网站服务器或API接口的服务器
express基本使用
创建基本的web服务器
//导入express模块
const express = require('express');
//创建web服务器
const app = express()
//调用app.listen(端口号,启动成功后的回调函数),启动服务器
app.listen(80,()=>{
...
})
监听get请求
语法格式
//参数1:客户端请求的url地址
//参数2:请求对应的处理函数
// req:请求对象(包含了与请求相关的属性和方法)
// res:响应对象(包含了与响应相关的属性和方法)
app.get('请求url',function(req,res){/*处理函数*/})
监听post请求
语法格式
//参数1:客户端请求的url地址
//参数2:请求对应的处理函数
// req:请求对象(包含了与请求相关的属性和方法)
// res:响应对象(包含了与响应相关的属性和方法)
app.post('请求url',function(req,res){/*处理函数*/})
把内容响应给客户端
通过res.send()方法:
app.get('/user',(req,res)=>{
//向客户端发送json对象
res.send({name:'zs',age:20,gender:'男'})
})
app.get('/user',(req,res)=>{
//向客户端发送文本内容
res.send('请求成功')
})
获取url中携带的查询参数
通过req.query对象,可以访问到客户端通过查询字符串的形式,发送到服务器的参数:
app.get('/',(req,res)=>{
//req.query默认是一个空对象
//客户端使用?name=zs&age=20这种查询字符串形式,发送到服务器的参数
//可以通过req.query对象访问到,例如:
//req.query.name req.query.age
console.log(req.query);
})
获取url中的动态参数
通过req.params对象,可以访问到url中,通过:匹配到的动态参数:
//url地址中,可以通过:参数名的形式,匹配动态参数值
app.get('/user/:id/:username',(req.res)=>{
//req.params默认是一个空对象
//里面存放着通过:动态匹配到的参数值
console.log(req.params);
})
托管静态资源
express.static()
express提供了一个非常好用的函数,叫做express.static(),通过它,我们可以非常方便地创建一个静态资源服务器
例如:通过如下代码就可以将public目录下的图片,css文件,js文件对外开发访问了:
app.use(express.static('public'))
注意:express在指定的静态目录中查找文件,并对外提供资源的访问路径。因此,存放静态文件的目录名不会出现在url中
托管多个静态资源目录
如果托管多个静态资源目录,请多次调用express.static()函数:
app.use(express.static('public'))
app.use(express.static('files'))
注意:访问静态资源文件时,express.static()函数会根据目录的添加顺序查找所需的文件
挂载路径前缀
如果希望在托管的静态资源访问路径之前,挂载路径前缀,则可以使用如下的方式:
app.use('/public',express.static('public'));
使用nodemon工具
在编写调试Node.js项目的时候,如果修改了项目的代码,则需要频繁的手动close掉,然后再重新启动
非常繁琐。
现在,可以使用nodermon这个工具,它能够监听项目文件的变动,当代码被修改后,nodemon 会自动帮我们重启项目,极大方便了开发和调试。
当基于Node.js编写了一个网站应用的时候,传统的方式,是运行node app.js 命令,来启动项目。这样做的坏处是:代码被修改之后,需要手动重启项目。
现在,我们可以将node 命令替换为nodemon命令,使用nodemon app.js 来启动项目。这样做的好处是:代码被修改之后,会被nodemon监听到,从而实现自动重启项目的效果。
心
- 安装
npm i -g nodemon - 命令
nodemon app.js
express路由
在express中,路由指的是客户端的请求与服务器处理函数之间的映射关系
express中的路由分三个部分组成,分别是请求的类型、请求的url地址、处理函数,格式如下:
app.METHOD(PATH,HANDLER);
路由匹配的过程
每当一个请求到达服务器之后,需要先经过路由的匹配,只有匹配成功之后,才会调用对应的处理函数。 在匹配时,会按照路由的顺序进行匹配,如果请求类型和请求的URL同时匹配成功,则Express 会将这次请求,转交给对应的function函数进行处理。
注意:
- 按照定义的先后顺序进行匹配
- 请求类型和请求的url同时匹配成功,才会调用对应的处理函数
模块化路由
为了方便对路由进行模块化的管理,express不建议将路由直接挂载到app上,而是推荐将路由抽离为单独的模块。步骤如下:
- 创建路由模块对应的.js文件
- 调用
express.Router()函数创建路由对象 - 向路由对象上挂载具体的路由
- 使用
module.exports向外共享路由对象 - 使用
app.use()函数注册路由模块
创建路由模块
var express = require('express');
var router = express.Router();
router.get('/user/list',function(req,res){
res.send('GET')
})
router.post('/user/add',function(req,res){
res.send('POST')
})
module.export=router;
注册路由模块
//导入路由模块
const userRouter = require('./router/user.js')
//使用app.use()注册路由模块
app.use(userRouter)
为路由模块添加前缀
类似于托管静态资源时,为静态资源同意挂载访问前缀一样,路由模块添加前缀的方式也非常简单:
//导入路由模块
const userRouter = require('./router/user.js')
//使用app.use()注册路由模块,并添加统一的访问前缀/api
app.use('/api',userRouter);
中间件
express中间件调用流程
当一个请求到达express的服务器之后,可以连续调用多个中间件,从而对这次请求进行预处理。
基本格式
express的中间件,本质上就是一个function处理函数,express中间件的格式如下:
注意:中间件函数的形参列表中,必须包含next参数。而路由处理函数中只包含req和res
next函数
next函数是实现多个中间件连续调用的关键,它表示把流转关系转交给下一个中间件或路由
中间件初体验
定义中间件函数
格式:
//常量mw所指向的,就是一个中间件函数
const mw = function(req,res,next){
//注意:当前中间件的业务处理完毕后,必须调用next()函数
//表示把流转关系转交给下一个中间件或路由
next()
}
全局生效的中间件
客户端发起的任何请求,到达服务器之后,都会触发中间件,叫做全局生效的中间件。 通过调用app.use(中间件函数),即可定义一个全局生效的中间件。示例如下:
const mw = function(req,res,next){
...
next()
}
//全局生效的中间件
app.use(mw)
简化形式:
app.use(function(req,res,next){
...
next()
})
中间件的作用
多个中间件之间,共享一份req和res。基于这样的特性,我们可以在上游中间件中,统一为req或res对象添加自定义的属性或方法,供下游的中间件或路由进行使用
定义多个全局中间件
可以使用app.use()连续定义多个全局中间件。客户端请求到达服务器之后,会按照中间件定义的先后顺序依次进行调用,示例如下:
app.use(function(req,res,next){//第一个全局中间件
...
next()
})
app.use(function(req,res,next){//第二个全局中间件
...
next()
})
app.get('/user',(req,res)=>{//请求这个路由,会依次触发上述两个全局中间件
res.send('Home page')
})
局部生效的中间件
不使用app.use()定义的中间件,叫做局部生效的中间件,示例如下:
//定义中间件函数mw1
const mw1 = function(req,res,next){
...
next()
}
//mw1这个中间件只在”当前路由中生效“,这种用法属于”局部生效的中间件“
app.get('/',mw1,function(req,res){
res.send('User')
})
//mw1这个收纳柜简介不会影响下面这个路由
app.get('/user',function(req,res){
res.send('User')
})
定义多个局部生效的中间件
可以在路由中通过如下两个等价的方式,使用多个局部中间件
app.get('/',mw1,mw2,function(req,res){
res.send('User')
})
app.get('/',[mw1,mw2],function(req,res){
res.send('User')
})
了解使用中间件的5个注意事项
- 一定要在路由之前注册中间件
- 客户端发送过来的请求,可以连续调用多个中间件进行处理
- 执行完中间件的业务代码之后,不要忘记调用
next()函数 - 为了防止代码逻辑混乱,调用next()函数后不要再写额外的代码
- 连续调用多个中间件时,多个中间件之间,共享req和res对象
中间件分类
- 应用级别的中间件
- 路由级别的中间件
- 错误级别的中间件
- express内置的中间件
- 第三方的中间件
应用级别的中间件
通过app.use()或app.get()或app.post(),绑定到app实例上的中间件,叫做应用级别的中间件,示例如下:
app.use(function(req,res,next){//第一个全局中间件
...
next()
})
app.get('/',mw1,function(req,res){
res.send('User')
})
路由级别的中间件
绑定到express.Router()实例上的中间件,叫做路由级别的中间件。它的用法和应用级别中间件没有任何区别。只不过,应用级别中间件时绑定到app实例上,路由级别中间件绑定到router实例上,代码示例如下:
var app = express();
var router = express.Router()
router.use(function(req,res,next){
...
next()
})
app.use('/',router)
错误级别的中间件
错误级别的中间件的作用:专门用来捕获整个项目发生的异常错误,从而防止项目异常崩溃的问题
格式:错误级别中间件的function处理函数中,必须有4个形参,形参顺序从前到后,分别是(err,req,res,next)
app.get('/',function(req,res){
throw new Error('服务器内部发生了错误!')//抛出一个自定义错误
res.send('Home Page');
})
app.use(function(err,req,res,next){
console.log('发生错误'+err.message)//在服务器打印错误信息
res.send('Error'+err.message)//向客户端响应错误相关的内容
})
注意:错误级别中间件,必须注册在所有路由之后
express内置中间件
自express 4.16.0版本开始,express内置了三个常用的中间件,极大的提高了express项目的开发效率和体验
express.static快速托管静态资源的内置中间件,例如:HTML,图片,css样式等(无兼容性问题)express.json解析json格式的请求体数据(有兼容性问题,仅在4.16.0+版本可用)express.urlencoded解析URL-encoded格式的请求体数据(有兼容性问题,仅在4.16.0+版本可用)
//配置解析application/json格式数据的内置中间件
app.use(express.json)
//配置解析application/x-www-form-urlencoded格式数据的内置中间件
app.use(express.urlencoded({extended: false}))
express.json()
const app = express();
app.use(express.json());
app.post('/user',(req,res)=>{
//在服务器。可以使用req.body这个属性,来接收客户端发送过来的请求体数据
//默认情况下,如果不配置解析表单数据的中间件,则req.body默认等于undefined
console.log(req.body)
res.send('OK')
})
express.urlencoded()
const app = express();
app.use(express.urlencoded({extended: false}));
app.post('/user',(req,res)=>{
console.log(req.body)
res.send('OK')
})
第三方中间件
非express官方内置的,而是由第三方开发出来的中间件,叫做第三方中间件。在项目中,大家可以按需下载并配置第三方中间件,从而提高开发效率 例如:在express@4.16.0之前的版本中,经常使用body-parser这个第三方中间件,来解析请求体数据,步骤如下:
- 运行
npm i body-parser安装中间件 - 使用
require导入中间件 - 调用
app.use注册并使用中间件
注意express内置的express.urlencoded中间件,就是基于body-parser这个第三方中间件进一步封装出来的
const parser = require('body-parser');
app.use(parser.urlencoded({extended: false}))
app.post('/user',(req,res)=>{
console.log(req.body);
res.send('ok');
})
自定义中间件
自己手动模拟一个类似于express.urlencoded这样的中间件,来解析POST提交到服务器的表单数据 实现步骤:
- 定义中间件
- 监听req的data事件
在中间件中,需要监听req对象的data事件,来获取客户端发送到服务器的数据 如果数据量比较大,无法一次性发送完毕,则客户端会把数据切割后,分批发送到服务器。所以data事件可能会触发多次,每次触发data事件时,获取到数据只是完整数据的一部分,需要手动对接收到的数据进行拼接
- 监听req的end事件
- 使用querystring模块解析请求体数据
- 将解析出来的数据对象挂载为req.body
- 将自定义中间件封装成模块
text.js
const express = require("express")
const app = express();
//导入自己封装的中间件模块
const customBodyParser = require('./custom-body-parser')
//解析表单数据的中间件
app.use(customBodyParser)
app.post('/user',(req,res)=>{
console.log(req.body);
res.send(req.body);
})
app.listen(80,()=>{
console.log(109);
})
custom-body-parser.js
const qs = require("querystring");
export default function bodyParser(req,res,next){
//定义逻辑代码
let str = '';
//监听req对象的data事件(客户端发送过来的新的请求体数据)
req.on('data',(chunk)=>{
//拼接请求体数据,隐式转换为字符串
str+=chunk
})
//监听req对象的end事件(请求体发送完毕后自动触发)
req.on("end",()=>{
// console.log(str);
//调用qs.parse(),把字符串格式的请求体数据解析成对象模式
const body = qs.parse(str);
req.body = body;
console.log(body);
next();
})
}
使用Express编写接口
创建基本的服务器
// 引入express模块
const express = require('express');
// 创建express的服务器实例
const app = express();
// 调用app.listen方法,指定端口号并启动web服务器
app.listen(8000, () => {
console.log("已启动");
})
创建API路由模块
apiRouter.js
const express = require('express')
const apiRouter = express.Router();
//挂载对应路由
module.exports = apiRouter
web服务器文件
//app.js导入并注册路由模块
const apiRouter = require("./apiRouter.js");
app.use('/api',apiRouter)
编写GET接口
apiRouter.js
//挂载对应路由
apiRouter.get('/get',(req,res)=>{
//获取到客户端通过查询字符串,发送到服务器的数据
const query = req.query
//调用res.send()方法,把数据响应给客户端
res.send({
status: 0,//状态,0表示成功,1表示失败
msg: 'GET请求成功',//状态描述
data: query//需要响应给客户端的具体数据
})
})
编写POST接口
apiRouter.post('/post',(req,res)=>{
//通过req.body获取请求体中包含的url-encoded格式的数据
const body = req.body
//调用res.send()方法,向客户端响应结果
res.send({
status: 0,
msg: 'POST请求成功!',
data: body
})
})
解决跨域问题
CORS跨域资源共享
在使用之前先下载npm i cors
const cors = require("cors");
app.use(cors()); //使用cors中间件
CORS响应头部
Access-Control-Allow-Origin
响应头部可以携带一个Access-Control-Allow-Origin字段,其语法如下:
Access-Control-Allow-Origin:<origin>|*
其中origin参数的值指定了允许访问该资源的外域URL
例如,下面的字段值只允许来自 itcast.cn 的请求
res.setHeader('Access-Control-Allow-Origin','http://itcast.cn')
如果指定了Access-Control-Allow-Origin字段的值为通配符*,表示允许来自任何域的请求,示例代码如下:
res.setHeader('Access-Control-Allow-Origin','*')
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对额外的请求头进行声明,否则这次请求会失败
//允许客户端额外向服务器发送Content-Type请求头和X-Custom-Header请求头
//注意:多个请求头之间使用英文的逗号进行分割
res.setHeader('Access-Control-Allow-Headers','Content-Type','X-Custom-Header');
Access-Control-Allow-Methods
默认情况下,CORS仅支持客户端发起GET、POST、HEAD请求
如果客户端希望通过PUT、DELETE等方式请求服务器的资源,则需要在服务器端,通过Access-Control-Allow-Methods来指明实际请求所允许使用的HTTP方法
//只允许POST、GET、DELETE、HEAD请求方法
res.setHeader('Access-Control-Allow-Methods','POST,GET,DELETE,HEAD')
//允许所有的HTTP请求方法
res.setHeader('Access-Control-Allow-Methods','*')
CORS请求分类
客户端在请求CORS接口时,根据请求方式和请求头的不同,可以将CORS的请求分为两个大类,分别是 简单请求,预检请求
简单请求 同时满足以下两个条件的请求,就属于简单请求:
请求方式:GET、POST、HEAD三者之一HTTP头部信息不超过以下几种字段:无自定义头部字段、Accept、Accept-Language、Content-Language、DPR、Downlink、Save-Data、Viewport-Width、Width、Content-Type(值仅限于text/plain、 multipart/form-data、application/x-www-form-urlencoded三者之一)
预检请求 只要符合以下任何一个条件的请求,都需要进行预检请求:
- 请求方式为
GET、POST、HEAD之外的请求Method类型 - 请求头
包含自定义头部字段 - 向服务器发送了
application/json格式的数据
在浏览器与服务器正式通信之前,浏览器会先发送Option请求进行预检,以获知服务器是否允许该实际请求,所以这一次的option请求称为“预检请求”。服务器成功响应预检请求后,才会发送真正的请求,并且携带真实数据
简单请求和预检请求的区别
- 简单请求的特点:客户端与服务器之间
只会发生一次请求 - 预检请求的特点:客户端与服务器之间会发生两次请求,
OPTION预检请求成功之后,才会发起真正的请求
编写JSONP接口
概念:浏览器端通过
<script>标签的src属性,请求服务器上的数据,同时,服务器返回一个函数的调用。这种的方式叫做JSONP
特点:
- JSONP不属于真正的Ajax请求,因为它没有使用XMLHttpRequest这个对象
- JSONP仅支持GET请求,不支持POST、PUT、DELETE等请求
创建JSONP接口的注意事项
如果项目中已经配置了CORS跨域资源共享,为了防止冲突,必须在配置CORS中间件之前声明JSONP的接口。否则JSONP接口会被处理成开启了CORS的接口。
//优先创建JSONP接口【这个接口不会被处理成CORS接口】
app.get('/api/jsonp'.(req,res)=>{
...
})
//再配置CORS中间件【后续的所有的接口,都会被处理成CORS接口】
app.use(cors())
//这样一个开启了CORS的接口
app.get('/api/get',(req,res)=>{
...
})
实现JSONP接口的步骤
- 获取客户端发送过来的回调函数的名字
- 得到要通过JSONP形式发送给客户端的数据
- 根据前两步得到的数据,拼接出一个函数调用的字符串
- 把上一步拼接得到的字符串,响应给客户端
<script>标签进行解析执行后端代码
app.get('/api/jsonp',(req,res)=>{
//1. 获取客户端发送过来的回调函数的名字
const funcName = req.query.callback;
//2. 得到要通过JSONP形式发送给客户端的数据
const data = {name: 'zs',age :22}
//3. 根据前两步得到的数据,拼接出一个函数调用的字符串
const scriptStr = `${funcName}(${JSON.stringify(data)})`
//4. 把上一步拼接得到的字符串,响应给客户端`<script>`标签进行解析执行
res.send(scriptStr)
})
前端jquery代码
$('#jsonp').on('click', function () {
$.ajax({
type: 'get',
url: 'http://127.0.0.1/api/jsonp',
dataType: 'jsonp',//表示发起jsonp请求
success: function (res) {
console.log(res);
}
})
})
在express中操作MySOL
- 安装操作MySQL数据库的第三方模块:
npm i mysql - 通过MySQL模块连接到MySQL数据库
- 通过MySQL模块执行SQL语句
配置mysql 在使用MySQL模块操作MySQL数据库之前,必须先对mysql模块进行必要的配置:
//导入mysql模块
const mysql = require('mysql')
//建立与mysql数据库的连接
const db = mysql.createPool({
host: '127.0.0.1',//数据库的IP地址
user: 'root',//登录数据库的账号
password: 'ling',//登录数据库的密码
database: ''//指定要操作哪个数据库
})
错误
Client does not support authentication protocol requested by server; consider upgrading MySQL client
解决博客
查询语句
db.query('select * from tags',(err,results)=>{
if(err) return console.log(err.message);
console.log(results);
})
插入语句
const user = {username: 'tom',password: '123'};
const sqlStr = 'INSERT INTO users (username,password) VALUES (?,?)'
db.query(sqlStr,[user.username,user.password],(err.results)=>{
if(err) return console.log(err.message);
//如果执行的是insert into 插入语句,则results是一个对象
//可以通过affectedRows的属性,来判断是否插入成功
if(results.affectedRows === 1){
console.log('插入成功')
}
})
便捷插入
const user = {username: 'tom',password: '123'};
const sqlStr = 'INSERT INTO users SET ?'
db.query(sqlStr,user,(err.results)=>{
if(err) return console.log(err.message);
//如果执行的是insert into 插入语句,则results是一个对象
//可以通过affectedRows的属性,来判断是否插入成功
if(results.affectedRows === 1){
console.log('插入成功')
}
})
更新语句
const user = {id: 6,username: 'tom',password: '123'};
const sqlStr = 'update users set username=?,password=? where id=?'
db.query(sqlStr,[user.username,user.password,user.id],(err.results)=>{
if(err) return console.log(err.message);
//如果执行的是insert into 插入语句,则results是一个对象
//可以通过affectedRows的属性,来判断是否插入成功
if(results.affectedRows === 1){
console.log('更新成功')
}
})
便捷更新
const user = {id: 6,username: 'tom',password: '123'};
const sqlStr = 'update users set ? where id=?'
db.query(sqlStr,[user,user.id],(err.results)=>{
if(err) return console.log(err.message);
//如果执行的是insert into 插入语句,则results是一个对象
//可以通过affectedRows的属性,来判断是否插入成功
if(results.affectedRows === 1){
console.log('更新成功')
}
})
删除语句
const sqlStr = 'delete from users where id=?'
//如果sql语句中有多个占位符,则必须使用数组为每个占位符指定具体的值
//如果sql语句中只有一个占位符,则可以省略数祖
db.query(sqlStr,8,(err.results)=>{
if(err) return console.log(err.message);
//如果执行的是insert into 插入语句,则results是一个对象
//可以通过affectedRows的属性,来判断是否插入成功
if(results.affectedRows === 1){
console.log('更新成功')
}
})
在express中使用session
web开发模式
- 基于
服务端渲染的传统web开发模式 - 基于
前后端分离的新型web开发模式
服务端渲染
服务器发送给客户端的HTML页面,是在服务器通过字符串的拼接,动态生成的。因此,客户端不需要使用Ajax这样的技术额外请求页面的数据
优点:
前端耗时少。因为服务器负责动态生成HTML内容,浏览器只需要直接渲染页面即可。尤其是移动端,更省电有利于SEO。因为服务器端响应的是完整的HTML页面内容,所以爬虫更容易获取信息,更有利于SEO
缺点:
占用服务器端资源。即服务器端完成HTML页面内容的拼接,如果请求较多,会对服务器造成一定的访问压力不利于前后端分离,开发效率低。使用服务器端渲染,则无法进行分工合作,尤其对于前端复杂度高的项目,不利于项目高效的开发
前后端分离
前后端分离的开发模式,依赖于Ajax技术的广泛应用。简而言之,前后端分离的web开发模式,就是后端只负责提供api接口,前端使用Ajax调用接口的开发模式
优点:
- 开发体验好。前端专注于UI页面的开发,后端专注于api的开发,且前端有更多的选择性
- 用户体验好。Ajax技术广泛应用,极大的提高了用户的体验,可以轻松实现页面的局部刷新
- 减轻了服务器端的渲染压力。因为页面最终是在每个用户的浏览器生成
缺点:
- 不利于SEO。因为完整的HTML页面需要在客户端动态拼接完成,所以爬虫对无法爬取页面的有效信息。(解决方案,利用Vue,React等前端框架的SSR技术能够很好的解决SEO问题)
不同开发模式下的身份认证
对于服务端渲染和前后端分离这两种开发模式:
- 服务端渲染推荐使用
Session认证机制 - 前后端分离推荐使用
JWT认证机制
Session认证机制
什么是Cookie
Cookie是存储在用户浏览器中一段不超过4kB的字符串。它由一个key和一value和其他几个用于控制Cookie有效期、安全性、使用范围的可选属性组成 不同域名下的cookie各自独立,每当客户端发起请求时,会自动把当前域名下所有未过期的cookie一同发送到服务器
cookie的几大特性:
- 自动发送
- 域名独立
- 过期限制
- 4kB限制
session的工作原理
在express中使用express-session
- 安装express-session中间件:
npm i express-session - 配置express-session中间件
//导入session中间件
var session = require('express-session');
//配置Session中间件
app.use(session({
secret: 'xxx',//secret属性的值可以为任意字符串
resave: false,//固定写法
saveUninitialized: true//固定写法
}))
- session中存数据,首次登录
app.get('/api/login',(req,res)=>{
if(!req.body.username!=='admin'||req.body.password!=='00000'){
return res.send({status: 1,msg: '登录失败'})
}
req.session.user = req.body;
req.session.islogin= true;
res.send({status: 0,msg: 'success'})
})
- session中取数据
app.get('/api/username',(req,res)=>{
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)=>{
req.session.destroy();
res.send({
status: 0,
msg: '退出登录成功'
})
})
JWT认证机制
session认证的局限性
session认证机制需要配合Cookie才能实现,由于Cookie默认不支持跨域访问。所以当涉及到前端跨域请求后端接口的时候,需要做许多额外的配置,才能实现跨域Session认证
JWT工作原理
JWT的组成部分
Header头部、Payload有效荷载、Signature签名。三者之间使用英文’.‘分隔,示例如下:
- Payload部分才是
真正的用户信息,它是用户信息经过加密之后生成的字符串 - Header和Signature是
安全性相关的部分,只是为了保证Token的安全性
JWT的使用方式
客户端到服务器返回的JWT之后,通常会将它存储到localStorage或sessionStorage 此后,客户端每次与服务器通信,都要带上这个JWT的字符串,从而进行身份认证,推荐的做法是把JWT放在HTTP请求头Authorization字段中
在express中使用
- 安装
npm i jsonwebtoken express-jwtjsonwebtoken: 用于生成JWT字符串 express-jwt: 用于将jwt字符串解析还原成json对象 - 定义secret密钥
- 登录成功后生成JWT字符串 调用jsonwebtoken包提供的sign()方法,将用户的信息加密成JWT字符串,响应给客户端:
app.post('/api/login',(req,res)=>{
res.send({
status: 200,
msg: '登陆成功',
token: jwt.sign({username: userinfo.username},secretKey,{expiresIn:'30s'})
})
})