身份验证解决了http请求无状态的问题。
身份验证有两种解决方案
- Session+Cookie
- JWT
一、Session+Cookie
1、登录时,后端发布一个加密字符串(包含用户相关信息)给前端,自动放在了cookie中;
2、前端在调用其他接口时,将这个加密字符串作为一个参数传递给服务器(cookie自动传递);
3、服务器拿到这个字符串解密,根据权限进行验证。
例如:在没有登录的情况下,是不能获取到数据库中的一些数据的。
1、使用session需要先安装两个插件
npm install cookie-parser
npm install express-session
2、在服务端入口文件中引入
const cookieParser=require('cookie-parser')
const session=require('express-session')
3、session配置
最简单版的session总体配置
app.use(session({
secret: 'hubwizApp', //为了安全性的考虑设置secret属性,相当于私钥(具体的加密算法内部已经实现了)
cookie: {maxAge: 60 * 1000 * 60 }, //设置过期时间
resave: true, // 即使 session 没有被修改,也保存 session 值,默认为 true
saveUninitialized: false, //无论有没有session cookie,每次请求都设置个session cookie ,默认给个标示为 connect.sid
}));
4、在登录的接口中,根据登录结果设置session
router.post("/login",(req,res)=>{
let {us,ps}=req.body;
if(!us||!ps){
return res.send({err:-1,msg:"登录参数不全"});
}
// 用户输入用户名和密码在数据库能查到,说明登录成功
User.find({us,ps}).then((data)=>{
if(data.length!==0){
// 登录成功之后,把用户的相关信息存到session中去
req.session.login=true;
req.session.name=us;
res.send({err:0,msg:"登录成功"})
}else{
res.send({err:-2,msg:"用户名或密码不正确"})
}
},(err)=>{
return res.send({err:-1,msg:"内部错误"});
})
})
5、在调用接口时验证session
使用session的验证逻辑
在调用接口时,会去执行中间件,执行next()才会继续往下
req.session可获取到session。
app.use("/food",(req,res,next)=>{
console.log(req.body)
console.log(req.session)
if(req.session.login){//如果session中的login为true,说明已经登录了(登录成功才会设置)
next()
}else{
res.send({err:-999,msg:"请先登录"})
}
}, foodRouter)
6、退出时注销session
router.get('/logout',(req,res)=>{
req.session.destroy();//销毁保存的session
res.send(({err:0,msg:"已退出"}))
})
7、关于cookie
调用express-session的时候,创建了一个session,当我们返回这个session的时候,它会自动给我们前端一个cookie。
cookie的特点是:不需要我们人为手动添加,它会自动跟着请求发送过去。
cookie在跨域时不能使用。
二、JWT
JSON Web Tokens
1、用户登录,服务端产生一个token(加密字符串)发送给前端
2、前端将token保存,(保存到cookie也行,保存到localStorage也行)
3、前端发起数据请求时携带token;
4、服务端验证token是否合法,如果合法继续操作,如果不合法中止操作。
1、安装
npm install jsonwebtoken
我们将jwt操作封装到jwt.js文件中,在入口文件中引入
2、引入
帮我们实现加密解密的算法
const jwt=require('jsonwebtoken')
3、定义一个私钥
const screat='sdjfksdjflajflasjflasjflksf'
4、生成token的方法
function creatToken(palyload){
palyload.ctime=Date.now()// 在palyload中添加一个创建时间
playload.exp=1000*60*24*7;//设置一个token有效时间长度
return jwt.sign(palyload,screat)//把载荷和私钥传进去,载荷就是额外传的数据
}
返回一个加密字符串
载荷中不要放密码等隐秘数据。
5、验证token的方法
传入私钥去验证token
function checkToken(token){
return new Promise((resovle,reject)=>{
jwt.verify(token,screat,(err,data)=>{
if(err){ reject('token 验证失败')}
//验证token有无过期,过期了就reject
resovle(data)
})
})
}
最后暴露出去
module.exports={
creatToken,checkToken
}
6、如果登录成功,在登录接口中生成token,返回给前端
调用creatToken方法生成token,传入载荷
router.post("/login",(req,res)=>{
let {us,ps}=req.body;
if(!us||!ps){
return res.send({err:-1,msg:"登录参数不全"});
}
// 用户输入用户名和密码在数据库能查到,说明登录成功
User.find({us,ps}).then((data)=>{
if(data.length!==0){
let token=jwt.creatToken({login:true,name:us})
res.send({err:0,msg:"登录成功",token})
}else{
res.send({err:-2,msg:"用户名或密码不正确"})
}
},(err)=>{
return res.send({err:-1,msg:"内部错误"});
})
})
7、前端请求其他接口时,将token发给服务端,服务端验证token
前端在发送请求时要在请求体对象中加上token
调用checkToken方法验证token
app.use("/food",(req,res,next)=>{
console.log(req.body)
// 在前端发送请求时将token发给服务器
// 通过req.body.token接收token
let {token}=req.body;
jwt.checkToken(token).then(()=>{
next();
},()=>{
res.send({err:-998,msg:'非法token'})
})
}, foodRouter)