上一篇中,講了下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天才能研究透的問題。 若是你們喜歡,別忘了點個贊哈, 哈哈哈