上一篇中,讲了下cookie+session的方式check用户状态,可是处理CSRF(跨站请求伪造)上会麻烦一点。css
既然说到了CSRF,那先稍微解释一下。前端
csrf是一种攻击方法,通俗的就是说攻击者假装成你进行行骗
。 这种是如何作到的呢? 从原理上说,这种攻击方式不须要窃取到用户的cookie,而是直接使用用户的cookie去进行违法行为。vue
我模拟一下这种状况:node
这可能会形成一个很大的问题,好比,会让某个视频资源点赞数很是高而顺序排的很是靠前, 或者是将一个资源标记为举报,致使资源下架。ios
还可能会由于网站的不严谨形成更严重的资金损失的问题,等等等等。web
到这里,你们可能会看到问题的严重性了。前端同窗由于以前不须要关注这些问题,当转到node时,也很容易忽略这类安全问题。express
若是你们经过上面的例子知道了深层缘由,那也就有了大概的防范思路。可是咱们做为技术开发者,不能由于有这种问题而强制用户每次访问后都当即登出,这对用户是很是不友好的。json
对了,那咱们就不让居心不良的人使用cookie经过session让服务端肯定用户。也就是说,让其即便在B网站进行A网站的接口请求,A网站服务端并匹配不上用户,致使登陆不成功,那就没有问题了。axios
因此,token这种形式就特别适合了。浏览器
能够从网上搜一下,会看到不少的解释。我想通俗的说一下,就是在登陆成功时,咱们产生一段惟一的,不会轻易被解析的字符串,发送到客户端,客户端把这个字符串存起来,每次请求时把这段字符串带着,让服务端反解。
看到这里,是否是感受跟cookie+session的形式差很少? 是的,从原理上来讲,是差很少的,只是token咱们通常不把它放在cookie中,会放在好比loaclstorage中,即使坏人想用csrf的方式搞破坏,可是,他拿不到token,因此也就无法让服务端认证为登陆状态,他的阴谋也就没法得逞了。
第一,在node中生成好token
// 写一个中间件token-middleware.js
const setting = require('../../config/setting');
const verify = require('../../config/verify');
function tokenMiddleWare(req, res, next) {
let token = req.headers[setting.token.header];
if(token === undefined){
return next();
}else{
// 能够token校验并将校验结果保存至请求头中
verify.getToken(token).then(data => {
logger.info('校验的data是:::', data);
req.data = data;
return next();
}).catch(err =>{
logger.error('校验出现错误:', err);
return next();
})
}
}
module.exports = tokenMiddleWare;
复制代码
//setting.js
module.exports = {
token: {
// token密钥
signKey: 'test_key_@@',
// 过时时间300s
signTime: 300,
// 请求头参数
header: 'authorization',
// 不用校验的路由
unRoute: [
{url: /\.(jpg|png|css|js)$/, methods: ['GET']}
]
}
}
复制代码
// verify.js
const jwt = require('jsonwebtoken');
const setting = require('./setting');
const verify = {
// 设置token
setToken(username, _id){
return new Promise(resolve => {
let token = jwt.sign(
// 存储数据,自定义
{username, _id},
// 密钥
setting.token.signKey,
{expiresIn: setting.token.signTime, algorithm: 'HS256'}
);
resolve(token);
})
},
getToken(token){
return new Promise((resolve, reject) => {
// 处理token字符串
if(!token.split(' ').length){
reject({error: 'The token value is empty'})
}else{
// 解密token并返回数据
let data = jwt.verify(token.split(' ')[1],setting.token.signKey)
resolve(data)
}
})
}
}
module.exports = verify;
复制代码
当登陆成功时,进行token的设置:
const verify = require('../../config/verify');
// loginInfo.username -> 登陆名
// loginInfo.passwd -> 登陆密码
verify.setToken(loginInfo.username, loginInfo.passwd).then(token => {
// 生成token后,返回给客户端
res.json({
code: 0,
mesg: 'success',
token
});
});
复制代码
而后,须要把写的中间件和express-jwt应用在app.js(你的根文件)中
const expressJwt = require('express-jwt');
// 加载token中间件
app.use(tokenMiddleware);
// 验证token是否可用
app.use(expressJwt({
secret: setting.token.signKey,
algorithms: ['HS256'],
credentialsRequired: false, // 容许无token请求
requestProperty: 'auth' // 把解析的值放在req.auth上
})
.unless({
//除了这个path,其余的URL都须要验证
path: setting.token.unRoute
}));
复制代码
注意,当使用express-jwt中间件时,须要一个兜底的中间件,来承接解析错误、token过时等结果。
若是出现错误,咱们默认返回401
,因此咱们来设置一下。
app.use(function (err, req, res, next) {
// 当验证token出现问题时,好比对不上,过时等状况,则返回401
if (err.name === 'UnauthorizedError') {
res.status(401).send(err.message);
}
});
复制代码
这样node这一层就处理好了。 注意: 上面本身写的那个中间件是本身简单写的express-jwt功能,因此用express-jwt, 能够不用我写的那个中间件。
第二,客户端处理(使用vue)
客户端,首先要作的,就是保存服务端返回的token,我把它保存到了loaclstorage上。 好比:
res.data.token && localStorage.setItem('authToken', res.data.token);
复制代码
而后,须要处理每次前端向服务端的请求头:
// 一样,仍是用axios
// 拦截前端要发出去的请求
axios.interceptors.request.use(config => {
let token = localStorage.getItem('authToken');
if (token) {
config.headers['Authorization'] = 'Bearer ' + token;
}
return config;
},
error => {
console.error('拦截request出现错误', error);
});
复制代码
这样,每次前端的请求,都会带着这个Authorization头,node层拿到而且解析就能够了。
同时须要注意,若是token解析后,返回401,那么咱们也须要承接
,而且转到登陆页面
。
axios.interceptors.response.use(response => {
return response
},
error => {
if(error.response.status === 401) {
router.push('/login');
}
}
);
复制代码
要想token不被csrf利用,前提是别让攻击者经过xss获取到,因此,须要处理好xss攻击。
好了,关于登陆的时候涉及到的点和要规避的坑就先写到这。
但愿你们能有所收获,用1个多小时的看文章和实地开发测试,解决新手可能要3天才能研究透的问题。 若是你们喜欢,别忘了点个赞哈, 哈哈哈