http请求特点是无状态的,也即是请求接口者未知,为了防止随意访问,加一个身份验证。在登录成功以后,发布一个加密字符串(用户信息相关),给前端,以后每次给后端请求接口,都要将加密字符串传递给后端来验证。
处理该问题总体有两种方式:
1、session+cookie 跨域不可用cookie,导致ip不一致时候容易出现问题,用起来麻烦
使用步骤:
在服务中引入插件并做配置:
const session = require('express-session');
const cookParse = require('cookie-parse');
...
// 在需要拦截的地方进行中间件配置
app.use('/food',(req, res, next)=>{
if(req.body.session.login){
next()
}else{
res.send({success: false, message: '没有登录,请先登录'});
}
}, foodRouter);
app.use(session({
secret: 'keyboard cat',
resave: true,
saveUninitialized: true,
cookie: { masAge: 60 * 1000 * 60 } //设置过期时间
}));
在登录接口中,登录成功以后,由于安装了express-session插件并配置了,在req.body.session能获取到seeeion值,同理也可以赋值,登录成功后,做如下处理:
router.post('/login', (req,res)=>{
const {us, ps}=req.body;
if(us&&ps){
User.find({name: us,pass: ps}).then(data=>{
if(data.length>0){
// 给session设置值
req.session.login=true;
req.session.name=us;
res.send({success: true, message: '登录成功', user:data});
}else{
res.send({success: false, message: '账号或者密码错误'});
}
}).catch(error=>{
res.send({success:false, message: '程序错误请联系管理员'});
})
}else{
res.send({success: false, message: '缺少参数'})
}
})
之后再访问food模块内容东西,由于配置了拦截器,如果获取不到session中的login,就会返回失败信息。
在退出接口做如下配置就可:
router.get('/logOut', (req,res)=>{
req.session.destroy();
res.send({message: '退出成功', success: true})
});
这样就会清除掉cookie信息,并清除登录session。
2、使用jwt是json web token 简写
前端登录时候,后端返回一个token,以后每次调接口都要传递此token,没有token就会验证不通过,禁止访问。
jwt又分为两类,一类是对称性加密,一种是非对称加密。
打开jwt官网,就会发现有三部分组成,一个是头信息,确定使用那种加密方式,图中使用的是hs256(哈希256),第二个是参数,第三个是私钥通过你随便输入的私钥,输的越乱越好。通过加密算法将前两个合成一个东西,生成蓝色的内容,就是token。注意不要在payload中放密码信息,通过官网能解析出来token。
1、下面使用node的jsonwebtoken插件来实现对称性加密,默认采用hs256:
// 简单实现
const jwt=require('jsonwebtoken');
const payload={
us: 'ling',
login: true,
};
const screat = 'jsladfjklsdjlkfjlk;asdjlfk;jaskldjfklas';
// 生成token
const token=jwt.sign(payload, screat, {expiresIn: 300}); // hs256 数据 载荷 screat私钥 过期时间,单位是秒,设为5分钟过期
// 验证token合法性
jwt.verify(token, screat, (err,data)=>{
console.log(err, data)
})
console.log(token);
2、封装函数
const jwt=require('jsonwebtoken');
const screat = 'jsladfjklsdjlkfjlk;asdjlfk;jaskldjfklas';
// 生成token
function createToken(payload){
return jwt.sign(payload, screat, { expiresIn: 180 });// 设置过期时间为3分钟
}
// 验证token合法性
function checkedToken(token) {
return new Promise((resolve, reject)=>{
jwt.verify(token, screat, (err, data)=>{
if(err){reject('token验证失败')}
resolve(data);
})
})
}
module.exports={
createToken,
checkedToken,
}
将上述封装函数放在utils文件下,文件命名为jwt。在登录成功接口,做如下处理:
const JWT=require('../utils/jwt');
router.post('/login', (req,res)=>{
const {us, ps}=req.body;
if(us&&ps){
User.find({name: us,pass: ps}).then(data=>{
if(data.length>0){
const token=JWT.createToken({login: true, name: us});
res.send({success: true, message: '登录成功', user:data, token});
}else{
res.send({success: false, message: '账号或者密码错误'});
}
}).catch(error=>{
res.send({success:false, message: '程序错误请联系管理员'});
})
}else{
res.send({success: false, message: '缺少参数'})
}
})
前端将token信息保存到本地,以后调用接口将该token传进去。
后端在需要登录才能访问的功能下,服务中加如拦截器:
const JWT=require('../utils/jwt');
app.use('/food',(req, res, next)=>{
const {token}=req.body;
JWT.checkedToken(token).then((data)=>{
next()
}).catch((err)=>{
res.send({success: false, message: 'token无效'});
})
}, foodRouter);