node.js +express 實(shí)現(xiàn)token 驗(yàn)證

Token驗(yàn)證的基本流程

1.服務(wù)端收到請(qǐng)求,去驗(yàn)證用戶名與密碼
2.驗(yàn)證成功后旗国,服務(wù)端會(huì)簽發(fā)一個(gè) Token邻悬,再把這個(gè) Token 發(fā)送給客戶端
3.客戶端收到 Token 以后可以把它存儲(chǔ)起來搏明,比如放在 Cookie 里或者 Local Storage 里
4.客戶端每次向服務(wù)端請(qǐng)求資源的時(shí)候需要帶著服務(wù)端簽發(fā)的 Token
5.服務(wù)端收到請(qǐng)求省艳,然后去驗(yàn)證客戶端請(qǐng)求里面帶著的 Token瓜挽,如果驗(yàn)證成功颁股,就向客戶端返回請(qǐng)求的數(shù)據(jù)

Node.js 實(shí)現(xiàn)token么库,需要的插件

 //生成token與驗(yàn)證
 1.jsonwebtoken
 npm install jsonwebtoken --save
 * 生成token的方法 sign
 * 驗(yàn)證token的方法 verify

2.express-jwt
 npm install express-jwt --save
* 驗(yàn)證token是否過期并規(guī)定那些路由不需要驗(yàn)證 express-jwt({})

JWT標(biāo)準(zhǔn)的Token有如下三個(gè)部分

  • header (頭部)
  • payload (數(shù)據(jù))
  • signature (簽名)

形式如下

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX25hbWUiOiJhZG1pbiIsInVzZXJfaWQiOjEsImlhdCI6MTU5NDI2MjQ5NSwiZXhwIjoxNTk0MzQ4ODk1fQ.1MJ_MAFgpBjOjpggj69Xz8F_evBcMAenRK_7a8fdVrc

具體實(shí)現(xiàn)過程

  • 目錄格式


    目錄格式.png
  • 在目錄下新建token文件夾,并創(chuàng)建token.js文件
var jwt = require('jsonwebtoken');
var jwtScrect = 'zgs_first_token';  //簽名

//登錄接口 生成token的方法
var setToken = function (user_name, user_id) {
    return new Promise((resolve, reject) => {
        //expiresln 設(shè)置token過期的時(shí)間
        //{ user_name: user_name, user_id: user_id } 傳入需要解析的值( 一般為用戶名甘有,用戶id 等)
        const token = jwt.sign({ user_name: user_name, user_id: user_id }, jwtScrect, { expiresIn: '24h' });
        resolve(token)
    })
}
//各個(gè)接口需要驗(yàn)證token的方法
var getToken = function (token) {
    return new Promise((resolve, reject) => {
        if (!token) {
            reject({
                error: 'token空'
            })
        }
        else {
            //第二種  改版后的
            var info = jwt.verify(token.split(' ')[1], jwtScrect);
            resolve(info);  //解析返回的值(sign 傳入的值)
        }
    })
}

module.exports = {
    setToken,
    getToken
}

!!!在app.js中自定義方法 驗(yàn)證token是否過期诉儒,如果沒過期,則解析出用戶信息返回req.data, 通過req.data判斷token

//引入插件
var vertoken=require('./token/token')
var expressJwt=require('express-jwt')

//解析token獲取用戶信息
app.use(function(req, res, next) {
   //注意此處token亏掀;authorization設(shè)置要與設(shè)置一致否則可能出現(xiàn)token為空
    var token = req.headers['authorization'];  
    if(token == undefined){
        return next();
    }else{
        vertoken.getToken(token).then((data)=> {
            req.data = data;             //解析成功后返回設(shè)置基本信息(通過req.data判斷是否過期)
            return next();
        }).catch((error)=>{
            return next();
        })
    }
});

//驗(yàn)證token是否過期并規(guī)定那些路由不需要驗(yàn)證
app.use(expressJwt({
    secret:'zgs_first_token',
    algorithms:['HS256']
}).unless({
    path:['/login','/register']  //不需要驗(yàn)證的接口名稱
}))

//設(shè)置托管靜態(tài)目錄; 項(xiàng)目根目錄+ public.可直接訪問public文件下的文件eg:http://localhost:3000/images/url.jpg
app.use(express.static(path.join(__dirname, 'public')));
app.use('/', indexRouter);
app.use('/', usersRouter);



//token失效返回信息
app.use(function(err,req,res,next){
    if(err.status==401){
        return res.status(401).send('token失效')
         //可以設(shè)置返回json 形式  res.json({message:'token失效'})
    }
})

  • 在登陸時(shí)生成token
  //引入token 
  var vertoken=require('../token/token')
  /* user login. */
  router.post('/login', function (req, res) {
    //獲取參數(shù)
    const params = [];
    params[0] = req.body.user_name
    params[1] = req.body.password
    console.log(params)
    if (params[0] === '' || params[1] === '') {
      return res.json({
            code: 1,
            message: '賬戶或密碼不能為空'
        })
    } else {
      // 查詢數(shù)據(jù)是否存在數(shù)據(jù)庫中(user表沒有設(shè)置索引)
        connection.query(sqlobj.sqls.login, params, function (err, result) {
            if (err) {
                throw err;
            } else {
                if (result.length != 0) {   //else判斷哪里出錯(cuò)
                    console.log(result) 
                    //調(diào)用生成token的方法
                    vertoken.setToken(result[0].user_name,result[0].user_id).then(token=>{
                       return  res.json({
                            code: 200,
                            message: '登錄成功',
                            token:token
                             //前端獲取token后存儲(chǔ)在localStroage中,
                            //**調(diào)用接口時(shí) 設(shè)置axios(ajax)請(qǐng)求頭Authorization的格式為`Bearer ` +token
                        })
                    })
               }
          }
    }
});

  • 驗(yàn)證token 及調(diào)用
//sql語句
var sqls={
    login: "select * from users where user_name = ? and password = ? ",
    users:'select user_name,password from users where user_name  = ?',
}

//封裝需要token驗(yàn)證的方法忱反,
var conn_query = function (req, res, sql, params) {
    connection.query(sql, params, function (err, result) {
        if (err) {
            //遇到過的錯(cuò)誤(可直接throw err)
            let messageError = '';
            if (err.errno == 1048) {
                messageError = '參數(shù)不能為空'
            } else if (err.errno == 1062) {
                messageError = '數(shù)據(jù)已存在'
            } else if (err.errno == 1265) {
                messageError = '格式錯(cuò)誤'
            }
            else {
                messageError = '請(qǐng)求失敗'
            }
            return res.json({
                code: err.errno,
                message: messageError
            })
        } else {
            //第二種 改版后req.data 是驗(yàn)證后的返回值 (生成token傳的數(shù)據(jù))
            if (req.data) {
                return res.json({
                    code: 200,
                    message: 'success',
                    data: result
                })
            } else {
                return res.json({
                    code: 1,
                    status: 401,
                    message: "error"
                })
            }
        }
    })
};
//導(dǎo)出
module.exports = {
    sqls,
    conn_query 
}

/#在其他文件接口中調(diào)用#/

 //  先引入該文件
  var sqlobj = require('../db/sql');  //目錄結(jié)構(gòu)在上方
    router.post('/api/addWebsite', function (req, res, next) {
    const params = [req.body.name, req.body.url, req.body.alexa, req.body.country];
    sqlobj.conn_query(req, res, sqlobj.sqls.addWebsite, params)
   })

寫在最后

在token失效時(shí)返回status:401,前端清除token 根據(jù)status狀態(tài)碼返回登錄頁面
完整代碼 Github

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末滤愕,一起剝皮案震驚了整個(gè)濱河市温算,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌间影,老刑警劉巖注竿,帶你破解...
    沈念sama閱讀 211,290評(píng)論 6 491
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異魂贬,居然都是意外死亡巩割,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,107評(píng)論 2 385
  • 文/潘曉璐 我一進(jìn)店門付燥,熙熙樓的掌柜王于貴愁眉苦臉地迎上來宣谈,“玉大人,你說我怎么就攤上這事机蔗∑哑恚” “怎么了?”我有些...
    開封第一講書人閱讀 156,872評(píng)論 0 347
  • 文/不壞的土叔 我叫張陵萝嘁,是天一觀的道長梆掸。 經(jīng)常有香客問我,道長牙言,這世上最難降的妖魔是什么酸钦? 我笑而不...
    開封第一講書人閱讀 56,415評(píng)論 1 283
  • 正文 為了忘掉前任,我火速辦了婚禮咱枉,結(jié)果婚禮上卑硫,老公的妹妹穿的比我還像新娘徒恋。我一直安慰自己,他們只是感情好欢伏,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,453評(píng)論 6 385
  • 文/花漫 我一把揭開白布入挣。 她就那樣靜靜地躺著,像睡著了一般硝拧。 火紅的嫁衣襯著肌膚如雪径筏。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,784評(píng)論 1 290
  • 那天障陶,我揣著相機(jī)與錄音滋恬,去河邊找鬼。 笑死抱究,一個(gè)胖子當(dāng)著我的面吹牛恢氯,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播鼓寺,決...
    沈念sama閱讀 38,927評(píng)論 3 406
  • 文/蒼蘭香墨 我猛地睜開眼勋拟,長吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了侄刽?” 一聲冷哼從身側(cè)響起指黎,我...
    開封第一講書人閱讀 37,691評(píng)論 0 266
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎州丹,沒想到半個(gè)月后醋安,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,137評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡墓毒,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,472評(píng)論 2 326
  • 正文 我和宋清朗相戀三年吓揪,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片所计。...
    茶點(diǎn)故事閱讀 38,622評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡柠辞,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出主胧,到底是詐尸還是另有隱情叭首,我是刑警寧澤,帶...
    沈念sama閱讀 34,289評(píng)論 4 329
  • 正文 年R本政府宣布踪栋,位于F島的核電站焙格,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏夷都。R本人自食惡果不足惜眷唉,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,887評(píng)論 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧冬阳,春花似錦蛤虐、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,741評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至见坑,卻和暖如春嚷掠,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背荞驴。 一陣腳步聲響...
    開封第一講書人閱讀 31,977評(píng)論 1 265
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留贯城,地道東北人熊楼。 一個(gè)月前我還...
    沈念sama閱讀 46,316評(píng)論 2 360
  • 正文 我出身青樓,卻偏偏與公主長得像能犯,于是被迫代替她去往敵國和親鲫骗。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,490評(píng)論 2 348

推薦閱讀更多精彩內(nèi)容