koa2使用JWT實(shí)現(xiàn)登錄

JSON Web Token 入門(mén)教程

什么是jwt

jwt是為了在網(wǎng)絡(luò)應(yīng)用環(huán)境間傳遞聲明而執(zhí)行的一種基于JSON的開(kāi)放標(biāo)準(zhǔn), 主要有三部分組成:Header.Payload.Signature

  • Header 標(biāo)頭通常由兩部分組成:令牌的類(lèi)型栏笆,即JWT戴而,以及正在使用的簽名算法(例如HMAC SHA256或RSA)
// 這個(gè)JSON被編碼為Base64Url劲厌,形成JWT的第一部分
{
  "alg": "HS256",
  "typ": "JWT"
}
  • Payload 令牌的第二部分是有效負(fù)載捐康,其中包含聲明。聲明是關(guān)于實(shí)體(通常是用戶(hù))和其他數(shù)據(jù)的聲明
// 這個(gè)JSON被編碼為Base64Url尚胞,形成JWT的第二部分
{
  "sub": "1234567890",
  "name": "John Doe",
  "admin": true
}
  • Signature 要?jiǎng)?chuàng)建簽名部分签钩,您必須采用編碼標(biāo)頭掏呼,編碼的有效負(fù)載,秘鑰铅檩,標(biāo)頭中指定的算法憎夷,并對(duì)其進(jìn)行簽名
// 以HMAC SHA256算法為例
HMACSHA256(
  base64UrlEncode(header) + "." +
  base64UrlEncode(payload),
  secret)
koa-jwt的工作流程
  • 用戶(hù)通過(guò)登錄Api獲取當(dāng)前用戶(hù)在有效期內(nèi)的token
  • 需要身份驗(yàn)證的API則都需要攜帶此前認(rèn)證過(guò)的token發(fā)送至服務(wù)端
  • koa2會(huì)利用koa-jwt中間件的默認(rèn)驗(yàn)證方式進(jìn)行身份驗(yàn)證,中間件會(huì)進(jìn)行驗(yàn)證成功和驗(yàn)證失敗的分流昧旨。
koa-jwt的驗(yàn)證方式
  • 請(qǐng)求頭中設(shè)置 authorization為Bearer + token拾给,Bearer后有空格check the Authorization header for a bearer token
// koa-jwt的默認(rèn)驗(yàn)證方式: 
{'authorization': "Bearer " + token}
  • 自定義getToken方法 opts.getToken function
  • 利用Cookie(此cookie非彼cookie)此處的Cookie只作為存儲(chǔ)介質(zhì)發(fā)給服務(wù)端的區(qū)域兔沃,校驗(yàn)并不依賴(lài)于服務(wù)端的session機(jī)制蒋得,服務(wù)端不會(huì)進(jìn)行任何狀態(tài)的保存。check the cookies (if opts.cookie is set)
koa-jwt搭配jsonwebtoken的簡(jiǎn)單使用
  1. 安裝依賴(lài)
npm install jsonwebtoken koa-jwt --save
  1. 中間件 請(qǐng)求驗(yàn)證token
// 中間件對(duì)token進(jìn)行驗(yàn)證
app.use(async (ctx, next) => {
    return next().catch((err) => {
        if (err.status === 401) {
            ctx.status = 401;
            ctx.body = {
                code: 401,
                msg: err.message
            }
        } else {
            throw err;
        }
    })
});
  1. 排除不驗(yàn)證的請(qǐng)求
const SECRET = 'shared-secret'; // demo乒疏,可更換
app.use(koajwt({ secret: SECRET, passthrough: true }).unless({
    // 登錄额衙,注冊(cè)接口不需要驗(yàn)證
    path: [/^\/api\/login/]
}));
  1. 登陸簽發(fā)token
// 示例
const USER = {
    username:'zhangsan',
    password:'123456',
    id: 100
}
// 登錄接口簽發(fā)token, 為了簡(jiǎn)便不使用router
app.use(async (ctx, next) => {
    if(ctx.path === '/api/login' && ctx.method === 'POST'){
        // 登錄
        // 判斷用戶(hù)名密碼是否匹配,為簡(jiǎn)單起見(jiàn),直接使用常量
        let checkUser = ctx.request.body.username == USER.username && ctx.request.body.password == USER.password;
        if (checkUser) {
                        let userToken = {name: USER.username, id: USER.id};
            ctx.body = {
                code: 200,
                msg: '登錄成功',
                token: jsonwebtoken.sign(
                        userToken,  // 加密userToken, 等同于上面解密的userToken
                        SECRET, 
                        {expiresIn: '1h'}  // 有效時(shí)長(zhǎng)1小時(shí)
                    )
            }
        }else{
            // 登錄失敗, 用戶(hù)名密碼不正確
            ctx.body = {
                code: 400,
                msg: '用戶(hù)名密碼不匹配'
            }
        }
    }else if (ctx.path=== '/api/user' && ctx.method === 'GET') {
        // 獲取用戶(hù)信息, 這個(gè)api需要token驗(yàn)證
        // 中間件統(tǒng)一驗(yàn)證token
        ctx.body = {
            code: 200,
            data: USER,
            msg: '請(qǐng)求成功'
        }   
    }
}) 
  1. 接口測(cè)試
  • 登錄接口


    登錄
  • 獲取用戶(hù)信息接口


    未傳驗(yàn)證

    加驗(yàn)證
完整源碼
const koa = require('koa');
const koajwt = require('koa-jwt');
const jsonwebtoken = require('jsonwebtoken');
const util = require('util');
const koabody = require('koa-body');
const app = new koa();

const SECRET = 'shared-secret'; // demo入偷,可更換

app.use(koabody());

// 中間件對(duì)token進(jìn)行驗(yàn)證
app.use(async (ctx, next) => {
    // let token = ctx.header.authorization;
    // let payload = await util.promisify(jsonwebtoken.verify)(token.split(' ')[1], SECRET);
    return next().catch((err) => {
        if (err.status === 401) {
            ctx.status = 401;
            ctx.body = {
                code: 401,
                msg: err.message
            }
        } else {
            throw err;
        }
    })
});

app.use(koajwt({ secret: SECRET }).unless({
    // 登錄接口不需要驗(yàn)證
    path: [/^\/api\/login/]
}));

// 示例
const USER = {
    username: 'zhangsan',
    password: '123456',
    id: 100
}
// 登錄接口簽發(fā)token, 為了簡(jiǎn)便不使用router
app.use(async (ctx, next) => {
    if (ctx.path === '/api/login' && ctx.method === 'POST') {
        // 登錄
        // 判斷用戶(hù)名密碼是否匹配
        let checkUser = ctx.request.body.username == USER.username && ctx.request.body.password == USER.password;
        if (checkUser) {
            ctx.body = {
                code: 200,
                msg: '登錄成功',
                token: jsonwebtoken.sign(
                    { name: USER.username, id: USER.id },  // 加密userToken
                    SECRET,
                    { expiresIn: '1h' }
                )
            }
        } else {
            // 登錄失敗, 用戶(hù)名密碼不正確
            ctx.body = {
                code: 400,
                msg: '用戶(hù)名密碼不匹配'
            }
        }
    } else if (ctx.path === '/api/user' && ctx.method === 'GET') {
        // 獲取用戶(hù)信息
        // 中間件統(tǒng)一驗(yàn)證token
        ctx.body = {
            code: 200,
            data: USER,
            msg: '請(qǐng)求成功'
        }
    }
})
app.listen(3000);
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末追驴,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子疏之,更是在濱河造成了極大的恐慌,老刑警劉巖暇咆,帶你破解...
    沈念sama閱讀 221,273評(píng)論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件锋爪,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡爸业,警方通過(guò)查閱死者的電腦和手機(jī)其骄,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,349評(píng)論 3 398
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)扯旷,“玉大人拯爽,你說(shuō)我怎么就攤上這事【觯” “怎么了毯炮?”我有些...
    開(kāi)封第一講書(shū)人閱讀 167,709評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)耸黑。 經(jīng)常有香客問(wèn)我桃煎,道長(zhǎng),這世上最難降的妖魔是什么大刊? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 59,520評(píng)論 1 296
  • 正文 為了忘掉前任为迈,我火速辦了婚禮,結(jié)果婚禮上缺菌,老公的妹妹穿的比我還像新娘葫辐。我一直安慰自己,他們只是感情好伴郁,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,515評(píng)論 6 397
  • 文/花漫 我一把揭開(kāi)白布耿战。 她就那樣靜靜地躺著,像睡著了一般蛾绎。 火紅的嫁衣襯著肌膚如雪昆箕。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 52,158評(píng)論 1 308
  • 那天租冠,我揣著相機(jī)與錄音鹏倘,去河邊找鬼。 笑死顽爹,一個(gè)胖子當(dāng)著我的面吹牛纤泵,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 40,755評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼捏题,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼玻褪!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起公荧,我...
    開(kāi)封第一講書(shū)人閱讀 39,660評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤带射,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后循狰,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體窟社,經(jīng)...
    沈念sama閱讀 46,203評(píng)論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,287評(píng)論 3 340
  • 正文 我和宋清朗相戀三年绪钥,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了灿里。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,427評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡程腹,死狀恐怖匣吊,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情寸潦,我是刑警寧澤色鸳,帶...
    沈念sama閱讀 36,122評(píng)論 5 349
  • 正文 年R本政府宣布,位于F島的核電站甸祭,受9級(jí)特大地震影響缕碎,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜池户,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,801評(píng)論 3 333
  • 文/蒙蒙 一咏雌、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧校焦,春花似錦赊抖、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 32,272評(píng)論 0 23
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至耸成,卻和暖如春报亩,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背井氢。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,393評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工弦追, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人花竞。 一個(gè)月前我還...
    沈念sama閱讀 48,808評(píng)論 3 376
  • 正文 我出身青樓劲件,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子零远,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,440評(píng)論 2 359