基于token的身份驗證

傳統(tǒng)基于服務(wù)器的驗證方式

傳統(tǒng)的驗證方式是基于服務(wù)器的,就是把登陸信息存在服務(wù)端,每次登陸需要去辨別存儲的登陸信息,一般都是通過session來實現(xiàn), 這樣會有一些問題,比如每次認(rèn)證用戶發(fā)起請求時,服務(wù)器都需要創(chuàng)建一個用記錄來存儲信息,當(dāng)越來越多的用戶發(fā)起請求時,內(nèi)存的開銷也會不斷的增加

基于token的驗證原理

基于token的身份驗證是無狀態(tài)的,我們不用將信息存儲在服務(wù)器或者session中。token通過請求頭傳輸,而不是把認(rèn)證信息存儲在服務(wù)器或者session中,就意味著可以從任意一種可以發(fā)送HTTP請求的終端向服務(wù)器發(fā)送請求

實現(xiàn)步驟

  1. 登陸時,客戶端發(fā)送用戶名密碼
  2. 服務(wù)端驗證用戶名密碼是否正確,校驗通過就會生成一個有時效的token串,發(fā)送給客戶端
  3. 客戶端儲存token,一般都會存儲在localStorage或者cookie里面
  4. 客戶端每次請求時都帶有token,可以將其放在請求頭里,每次請求都攜帶token
  5. 服務(wù)端驗證token,所有需要校驗身份的接口都會被校驗token,若token解析后的數(shù)據(jù)包含用戶身份信息,則身份驗證通過,返回數(shù)據(jù)

node + jwt(jsonwebtoken) 搭建token身份驗證

npm i jsonwebtoken  --save // 安裝jsonwebtoken模塊
// 引入模塊依賴
const fs = require('fs');
const path = require('path');
const jwt = require('jsonwebtoken');
// 創(chuàng)建 token 類
class Jwt {
    constructor(data) {
        this.data = data;

    }

    //生成token
    generateToken() {
        let data = this.data;
        let created = Math.floor(Date.now() / 1000);
        let cert = fs.readFileSync(path.join(__dirname, '../pem/private_key.pem'));//私鑰 可以自己生成
        let token = jwt.sign({
            data,
            exp: created + 60 * 30,
        }, cert, {algorithm: 'RS256'});
        return token;
    }

    // 校驗token
    verifyToken() {
        let token = this.data;
        let cert = fs.readFileSync(path.join(__dirname, '../pem/public_key.pem'));//公鑰 可以自己生成
        let res;
        try {
            let result = jwt.verify(token, cert, {algorithms: ['RS256']}) || {};
            let {exp = 0} = result, current = Math.floor(Date.now() / 1000);
            if (current <= exp) {
                res = result.data || {};
            }
        } catch (e) {
            res = 'err';
        }
        return res;
    }
}
module.exports = Jwt;

使用jwt token工具

/ 引入jwt token工具
const JwtUtil = require('../public/utils/jwt');
// 我這里的是aes加密密碼的可以去掉
const AesUtil = require('../public/utils/aes');
// 登錄
router.post('/login',(req,res) => {
    var userName = req.body.user;
    var pass = req.body.pass;
    new Promise((resolve, reject) => {
        // 根據(jù)用戶名查詢用戶
        users.findOne({'username':userName}).exec((err,result) => {
           if(err){
               reject(err);
           }else{
               resolve(result);
           }
        });
    }).then((result) => {
        console.log(result);
        if(result){
            // 密碼解密 利用aes
            var aes = new AesUtil(result.password);
            var password = aes.deCryto();
            if(pass == password){
                // 登陸成功,添加token驗證
                let _id = result._id.toString();
                // 將用戶id傳入并生成token
                let jwt = new JwtUtil(_id);
                let token = jwt.generateToken();
                // 將 token 返回給客戶端
                res.send({status:200,msg:'登陸成功',token:token});
            }else{
                res.send({status:400,msg:'賬號密碼錯誤'});
            }
        }else{
            res.send({status:404,msg:'賬號不存在'})
        }
    }).catch((err) => {
        console.log(err);
        res.send({status:500,msg:'賬號密碼錯誤'});
    })
});

app.use(function (req, res, next) {
    // 我這里知識把登陸和注冊請求去掉了,其他的多有請求都需要進(jìn)行token校驗 
    if (req.url != '/user/login' && req.url != '/user/register') {
        let token = req.headers.token;
        let jwt = new JwtUtil(token);
        let result = jwt.verifyToken();
        // 如果考驗通過就next,否則就返回登陸信息不正確
        if (result == 'err') {
            console.log(result);
            res.send({status: 403, msg: '登錄已過期,請重新登錄'});
            // res.render('login.html');
        } else {
            next();
        }
    } else {
        next();
    }
});

前端請求封裝

export default class FetchAsync {
    // get
    static getFatch(url) {
        let geturl = url;
        return new Promise((resolve, reject) => {
                var url = 'http://127.0.0.1:3001/' + geturl;
                fetch(url, {
                    method: 'GET',
                    header: {
                        'Accept': 'application/json',
                        'Content-Type': 'application/json',
                        'Token': localStorage.getItem('token')
                    },
                }).then((response) => {
                    if (response.ok) {
                        return response.json();
                    } else {
                        reject({status: response.status})
                    }
                }).then((res) => {
                    resolve(res)
                }).catch((err) => {
                    reject(err)
                })
            }
        )
    }

    // post
    static postFatch(url, params) {
        console.log(params);
        var url = 'http://127.0.0.1:3001/' + url;
        return new Promise((resolve, reject) => {
                fetch(url, {
                    method: 'POST',
                    headers: {
                        "Content-Type": "application/json;charset=utf-8",
                        'Token': localStorage.getItem('token')
                    },
                    body: JSON.stringify(params)
                }).then(response => response.json()).then((res) => {
                    resolve(res);
                }).catch((err) => {
                    reject(err)
                });
            }
        )
    }
}
公鑰可以使用openssl通過命令來生成
  • window下生成命令
公鑰生成  genrsa -out rsa_private_key.pem 1024
私鑰生成  rsa -in rsa_private_key.pem -pubout -out rsa_public_key.pem
  • mac下生成命令
私鑰生成 openssl genrsa -out rsa_private_key.pem 1024
公鑰生成 openssl rsa -in rsa_private_key.pem -pubout -out rsa_public_key.pem
使用node自帶的加密模塊crypto
const crypto = require('crypto')
function md5(content) {
  crypto.createHash('md5').update(content).digest('hex')
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容