国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

zanePerfor中一套簡單通用的Node前后端Token登錄機(jī)制和github授權(quán)登錄方式

0x584a / 2924人閱讀

摘要:的優(yōu)勢使用簡單,性能足夠強(qiáng)悍,儲存空間無限制,多臺服務(wù)器可以使用統(tǒng)一的登錄態(tài),登錄邏輯代碼的解耦。每次登錄時清除上一次用戶的登錄信息,即清除登錄校驗(yàn)信息,這樣就能保證同一用戶同一時間只能在一個地方登錄。

HI!,你好,我是zane,zanePerfor是一款我開發(fā)的一個前端性能監(jiān)控平臺,現(xiàn)在支持web瀏覽器端和微信小程序端。

我定義為一款完整,高性能,高可用的前端性能監(jiān)控系統(tǒng),這是未來會達(dá)到的目的,現(xiàn)今的架構(gòu)也基本支持了高可用,高性能的部署。實(shí)際上還不夠,在很多地方還有優(yōu)化的空間,我會持續(xù)的優(yōu)化和升級。

開源不易,如果你也熱愛技術(shù),擁抱開源,希望能小小的支持給個star。

項(xiàng)目的github地址:https://github.com/wangweiang...
項(xiàng)目開發(fā)文檔說明:https://blog.seosiwei.com/per...

談起Token登錄機(jī)制,相信絕大部分人都不陌生,相信很多的前端開發(fā)人員都有實(shí)際的開發(fā)實(shí)踐。

此文章的Token登錄機(jī)制主要針對于無實(shí)際開發(fā)經(jīng)驗(yàn)或者開發(fā)過簡單登錄機(jī)制的人員,如果你是大佬幾乎可以略過了,如果你感興趣或者閑來無事也可以稍微瞅它一瞅。

此文章不會教你一步一步的實(shí)現(xiàn)一套登錄邏輯,只會結(jié)合zanePerfor項(xiàng)目闡述它的登錄機(jī)制,講明白其原理比寫一堆代碼來的更實(shí)在和簡單。

zanePerfor項(xiàng)目的主要技術(shù)棧是 egg.js、redis和mongodb, 如果你不懂沒關(guān)系,因?yàn)樗麄兌贾皇呛唵问褂茫苋菀桌斫狻?/p> 登錄實(shí)現(xiàn)結(jié)果:

如果用戶未注冊時先注冊然后直接登錄

用戶每次登錄都會動態(tài)生成session令牌

同一賬號在同一時刻只能在一個地方登錄

cookie在項(xiàng)目中的作用

我們知道http是無狀態(tài)的,因此如果要知道用戶某次請求是否登錄就需要帶一定的標(biāo)識,瀏覽器端http請求帶標(biāo)識常用的方式有兩種:1、使用cookie附帶標(biāo)識,2、使用header信息頭附帶標(biāo)識。
這里我們推薦的方式是使用cooke附帶標(biāo)識,因?yàn)樗喈?dāng)于來說更安全和更容易操作。

更安全體現(xiàn)在:cookie只能在同域下傳輸,還可以設(shè)置httpOnly來禁止js的更改。
更容易操作體現(xiàn)在:cookie傳輸是瀏覽器請求時自帶的傳輸頭信息,我們不需要額外的操作,cookie還能精確到某一個路徑,并且可以設(shè)置過期時間自動過期,這樣就顯得更可控。
當(dāng)然header信息頭也有它的優(yōu)勢和用武之地,這里不做闡述。

redis在項(xiàng)目中的作用

一般的項(xiàng)目我們會把識別用戶的標(biāo)識放存放在Session中,但是Session有其使用的局限性。

Session的局限:Session 默認(rèn)存放在 Cookie 中,但是如果我們的 Session 對象過于龐大,瀏覽器可能拒絕保存,這樣就失去了數(shù)據(jù)的完整性。當(dāng) Session 過大時還會對每次http請求帶來額外的開銷。還有一個比較大的局限性是Session存放在單臺服務(wù)器中,當(dāng)有多臺服務(wù)器時無法保證統(tǒng)一的登錄態(tài)。還會帶來代碼的強(qiáng)耦合性,不能使得登錄邏輯代碼解耦。

因此這里引入redis進(jìn)行用戶身份識別的儲存。

redis的優(yōu)勢:redis使用簡單,redis性能足夠強(qiáng)悍,儲存空間無限制,多臺服務(wù)器可以使用統(tǒng)一的登錄態(tài),登錄邏輯代碼的解耦。

前端統(tǒng)一登錄態(tài)封裝

前端統(tǒng)一登錄態(tài)應(yīng)該是每位前端童鞋都做過的事情,下面以zanePerfor的Jquery的AJAX為例做簡單的封裝為例:

// 代碼路徑:app/public/js/util.js
ajax(json) {
    // ...代碼略...
    return $.ajax({
        type: json.type || "post",
        url: url,
        data: json.data || "",
        dataType: "json",
        async: asyncVal,
        success: function(data) {
            // ...代碼略...
            // success 時統(tǒng)一使用this.error方法進(jìn)行處理
            if (typeof(data) == "string") {
                This.error(JSON.parse(data), json);
            } else {
                This.error(data, json);
            }
        },
        // ...代碼略...
    });
};

error(data, json) {
    //判斷code 并處理
    var dataCode = parseInt(data.code);
    // code 為1004表示未登錄 需要統(tǒng)一走登錄頁面
    if (!json.isGoingLogin && dataCode == 1004) {
        //判斷app或者web
        if (window.location.href.indexOf(config.loginUrl) == -1) {
            location.href = config.loginUrl + "?redirecturl=" + encodeURIComponent(location.href);
        } else {
            popup.alert({
                type: "msg",
                title: "用戶未登陸,請登錄!"
            });
        }
    } else {
        switch (dataCode) {
            // code 為1000表示請求成功
            case 1000:
                json.success && json.success(data);
                break;
            default:
                if (json.goingError) {
                    //走error回調(diào)
                    json.error && json.error(data);
                } else {
                    //直接彈出錯誤信息
                    popup.alert({
                        type: "msg",
                        title: data.desc
                    });
                };
        }
    };
}

前端的邏輯代碼很簡單,就是統(tǒng)一的判斷返回code, 如果未登錄則跳轉(zhuǎn)到登錄頁面。

User表結(jié)構(gòu)說明
// 代碼路徑 app/model/user.js
const UserSchema = new Schema({
        user_name: { type: String }, // 用戶名稱
        pass_word: { type: String }, // 用戶密碼
        system_ids: { type: Array }, // 用戶所擁有的系統(tǒng)Id
        is_use: { type: Number, default: 0 }, // 是否禁用 0:正常  1:禁用
        level: { type: Number, default: 1 }, // 用戶等級(0:管理員,1:普通用戶)
        token: { type: String }, // 用戶秘鑰
        usertoken: { type: String }, // 用戶登錄態(tài)秘鑰
        create_time: { type: Date, default: Date.now }, // 用戶訪問時間
    });

用戶表中 usertoken 字段比較重要,它表示每次用戶登錄時動態(tài)生成的Token令牌key, 也是存在在redis中用戶信息的key值,此值每次用戶登錄時都會更新,并且是隨機(jī)和唯一的。

Node Servers端登錄邏輯

我們先來一張登錄的頁面

業(yè)務(wù)代碼如下:
// 代碼路徑  app/service/user.js
// 用戶登錄
    async login(userName, passWord) {
        // 檢測用戶是否存在
        const userInfo = await this.getUserInfoForUserName(userName);
        if (!userInfo.token) throw new Error("用戶名不存在!");
        if (userInfo.pass_word !== passWord) throw new Error("用戶密碼不正確!");
        if (userInfo.is_use !== 0) throw new Error("用戶被凍結(jié)不能登錄,請聯(lián)系管理員!");

        // 清空以前的登錄態(tài)
        if (userInfo.usertoken) this.app.redis.set(`${userInfo.usertoken}_user_login`, "");

        // 設(shè)置新的redis登錄態(tài)
        const random_key = this.app.randomString();
        this.app.redis.set(`${random_key}_user_login`, JSON.stringify(userInfo), "EX", this.app.config.user_login_timeout);
        // 設(shè)置登錄cookie
        this.ctx.cookies.set("usertoken", random_key, {
            maxAge: this.app.config.user_login_timeout * 1000,
            httpOnly: true,
            encrypt: true,
            signed: true,
        });
        // 更新用戶信息
        await this.updateUserToken({ username: userName, usertoken: random_key });

        return userInfo;
    }
對照user表來進(jìn)行邏輯的梳理。

每次登錄前都會清除上一次在redis中的登錄態(tài)信息,所以上一次的登錄令牌對應(yīng)的redis信息會失效,因此我們只需要做一個校驗(yàn)用戶Token的信息在redis中是否存在即可判斷用戶當(dāng)前登錄態(tài)是否有效。

清除上一次登錄態(tài)信息之后立即生成一個隨機(jī)并唯一的key值做為新的Token令牌,并更新redis中Token的令牌信息 和 設(shè)置新的cookie令牌,這樣就保證了以前的登錄態(tài)失效,當(dāng)前的登錄態(tài)有效。

redis 和 cookie 都設(shè)置相同的過期時間,以保證Token的時效性和安全性。

cookie的httpOnly 我們需要開啟,這樣就保證的Token的不可操作性,encrypt 和 signed參數(shù)是egg.js 的參數(shù),主要負(fù)責(zé)對cookie進(jìn)行加密,讓前端的cookie不已明文的方式呈現(xiàn),提高安全性。

最后再更新用戶的Token令牌信息,以保證用戶的Token每次都是最新的,也用以下次登錄時的清除操作。

Servers 端用戶登錄校驗(yàn)中間件

中間件的概念相信大家都不陌生,用過koa,express和redux都應(yīng)該知道,egg.js的中間件來自于與koa,在這里就不說概念了。

在zanePerfor項(xiàng)目中我們只需要對所有需要進(jìn)行登錄校驗(yàn)的路由(請求)進(jìn)行中間件校驗(yàn)即可。

在egg中可這樣使用:
// 代碼來源 app/router/api.js
// 獲得controller 和 middleware(中間件)
const { controller, middleware } = app;

// 對需要校驗(yàn)的路由進(jìn)行校驗(yàn)
// 退出登錄
apiV1Router.get("user/logout", tokenRequired, user.logout);

業(yè)務(wù)代碼如下:
// 代碼路徑  app/middleware/token_required.js
// Token校驗(yàn)中間件
module.exports = () => {
    return async function(ctx, next) {
        const usertoken = ctx.cookies.get("usertoken", {
            encrypt: true,
            signed: true,
        }) || "";
        if (!usertoken) {
            ctx.body = {
                code: 1004,
                desc: "用戶未登錄",
            };
            return;
        }
        const data = await ctx.service.user.finUserForToken(usertoken);
        if (!data || !data.user_name) {
            ctx.cookies.set("usertoken", "");
            const descr = data && !data.user_name ? data.desc : "登錄用戶無效!";
            ctx.body = {
                code: 1004,
                desc: descr,
            };
            return;
        }
        await next();
    };
};

// finUserForToken方法代碼路徑
// 代碼路徑  app/service/user.js

// 根據(jù)token查詢用戶信息
async finUserForToken(usertoken) {
    let user_info = await this.app.redis.get(`${usertoken}_user_login`);

    if (user_info) {
        user_info = JSON.parse(user_info);
        if (user_info.is_use !== 0) return { desc: "用戶被凍結(jié)不能登錄,請聯(lián)系管理員!" };
    } else {
        return null;
    }
    return await this.ctx.model.User.findOne({ token: user_info.token }).exec();
}
邏輯梳理:

首先會獲得上傳的token令牌,這里cookie.get方法的 encrypt 和 signed 需要為true,這會把Token解析為明文。

在finUserForToken方法中主要是獲取Token令牌對應(yīng)的redis用戶信息,只有當(dāng)用戶的信息為真值時才會通過校驗(yàn)

在中間件這一環(huán)節(jié)還有一個比較常規(guī)的驗(yàn)證 就是 驗(yàn)證請求的 referer, referer也是瀏覽器請求時自帶的,在瀏覽器端不可操作,這相對的增加了一些安全性(項(xiàng)目中暫未做,這個驗(yàn)證比較簡單,如果有需要的自己去實(shí)現(xiàn))。

到此zanePerfor的Token校驗(yàn)機(jī)制其實(shí)已經(jīng)完全實(shí)現(xiàn)完了,只是未做整體的總結(jié),下面來繼續(xù)的完成注冊的邏輯。 用戶注冊邏輯實(shí)現(xiàn)

業(yè)務(wù)代碼如下:
// 代碼路徑  app/service/user.js

// 用戶注冊
async register(userName, passWord) {
    // 檢測用戶是否存在
    const userInfo = await this.getUserInfoForUserName(userName);
    if (userInfo.token) throw new Error("用戶注冊:用戶已存在!");

    // 新增用戶
    const token = this.app.randomString();

    const user = this.ctx.model.User();
    user.user_name = userName;
    user.pass_word = passWord;
    user.token = token;
    user.create_time = new Date();
    user.level = userName === "admin" ? 0 : 1;
    user.usertoken = token;
    const result = await user.save();

    // 設(shè)置redis登錄態(tài)
    this.app.redis.set(`${token}_user_login`, JSON.stringify(result), "EX", this.app.config.user_login_timeout);
    // 設(shè)置登錄cookie
    this.ctx.cookies.set("usertoken", token, {
        maxAge: this.app.config.user_login_timeout * 1000,
        httpOnly: true,
        encrypt: true,
        signed: true,
    });

    return result;
}

用戶注冊的代碼比較簡單,首先檢測用戶是否存在,不存在則儲存

生成動態(tài)并唯一的Token令牌,并保持?jǐn)?shù)據(jù)到redis 和設(shè)置 cookie令牌信息, 這里都設(shè)置相同的過期時間,并加密cookie信息和httpOnly。

退出登錄邏輯

退出登錄邏輯很簡單,直接清除用戶Token對應(yīng)的redis信息和cookie token令牌即可。

// 登出
logout(usertoken) {
    this.ctx.cookies.set("usertoken", "");
    this.app.redis.set(`${usertoken}_user_login`, "");
    return {};
}
凍結(jié)用戶邏輯

凍結(jié)用戶的邏輯也比較簡單,唯一需要注意的是,凍結(jié)的時候需要清除用戶Token對應(yīng)的redis信息。

// 凍結(jié)解凍用戶
async setIsUse(id, isUse, usertoken) {
    // 凍結(jié)用戶信息
    isUse = isUse * 1;
    const result = await this.ctx.model.User.update(
        { _id: id },
        { is_use: isUse },
        { multi: true }
    ).exec();
    // 清空登錄態(tài)
    if (usertoken) this.app.redis.set(`${usertoken}_user_login`, "");
    return result;
}
刪除用戶邏輯

刪除用戶邏輯跟凍結(jié)用戶邏輯一致,也需要注意清除用戶Token對應(yīng)的redis信息。

// 刪除用戶
async delete(id, usertoken) {
    // 刪除
    const result = await this.ctx.model.User.findOneAndRemove({ _id: id }).exec();
    // 清空登錄態(tài)
    if (usertoken) this.app.redis.set(`${usertoken}_user_login`, "");
    return result;
}
第三方github登錄說明

根據(jù)zanePerfor的登錄校驗(yàn)機(jī)制可以得出以下的結(jié)論:

User表的用戶名必須存在,密碼可無,并且用戶名在代碼中強(qiáng)校驗(yàn)不能重復(fù),但是在數(shù)據(jù)庫中用戶名是可以重復(fù)的。

usertoken字段很重要,是實(shí)現(xiàn)所有Token機(jī)制的核心字段,每次登錄和注冊都會是隨機(jī)并唯一的值

基于以上兩點(diǎn)做第三方登錄我們只需要實(shí)現(xiàn)以下幾點(diǎn)即可:

只要給用戶名賦值即可,因?yàn)橛脩裘艽a登錄和第三方登錄是兩套邏輯,因此用戶名可以重復(fù),這就解決了第三方登錄一定不會存在用戶已注冊的提示。

第一次登錄時注冊用戶,并把第三方的用戶名當(dāng)做表的用戶名,第三方的secret作為用戶的token字段。

第二次登錄時使用token字段檢測用戶是否已注冊,已注冊走登錄邏輯,未注冊走注冊邏輯。

// 代碼地址  app/service/user.js

// github register 核心注冊邏輯
async githubRegister(data = {}) {
    // 此字段為github用戶名
    const login = data.login;
   // 此字段為github 唯一用戶標(biāo)識
    const token = data.node_id;
    let userInfo = {};
    if (!login || !token) {
        userInfo = { desc: "github 權(quán)限驗(yàn)證失敗, 請重試!" };
        return;
    }
    // 通過token去查詢用戶是否存在
    userInfo = await this.getUserInfoForGithubId(token);
    // 身材Token隨機(jī)并唯一令牌
    const random_key = this.app.randomString();
    if (userInfo.token) {
        // 存在則直接登錄
        if (userInfo.is_use !== 0) {
            userInfo = { desc: "用戶被凍結(jié)不能登錄,請聯(lián)系管理員!" };
        } else {
            // 清空以前的登錄態(tài)
            if (userInfo.usertoken) this.app.redis.set(`${userInfo.usertoken}_user_login`, "");
            // 設(shè)置redis登錄態(tài)
            this.app.redis.set(`${random_key}_user_login`, JSON.stringify(userInfo), "EX", this.app.config.user_login_timeout);
            // 設(shè)置登錄cookie
            this.ctx.cookies.set("usertoken", random_key, {
                maxAge: this.app.config.user_login_timeout * 1000,
                httpOnly: true,
                encrypt: true,
                signed: true,
            });
            // 更新用戶信息
            await this.updateUserToken({ username: login, usertoken: random_key });
        }
    } else {
        // 不存在 先注冊 再登錄
        const user = this.ctx.model.User();
        user.user_name = login;
        user.token = token;
        user.create_time = new Date();
        user.level = 1;
        user.usertoken = random_key;
        userInfo = await user.save();
        // 設(shè)置redis登錄態(tài)
        this.app.redis.set(`${random_key}_user_login`, JSON.stringify(userInfo), "EX", this.app.config.user_login_timeout);
        // 設(shè)置登錄cookie
        this.ctx.cookies.set("usertoken", random_key, {
            maxAge: this.app.config.user_login_timeout * 1000,
            httpOnly: true,
            encrypt: true,
            signed: true,
        });
    }
    return userInfo;
}

詳細(xì)的github第三方授權(quán)方式請參考:https://blog.seosiwei.com/per...

總結(jié):

前端封裝統(tǒng)一的登錄驗(yàn)證,項(xiàng)目中 code 1004 為用戶未登錄,1000為成功。

user數(shù)據(jù)表中儲存一個usertoken字段,此字段是隨機(jī)并唯一的標(biāo)識,在注冊時存入此字段,在每次登錄時更新此字段。

瀏覽器端的Token令牌即usertoken字段,redis的每個Token存儲的是相應(yīng)的用戶信息。

每次登錄時清除上一次用戶的登錄信息,即清除redis登錄校驗(yàn)信息,這樣就能保證同一用戶同一時間只能在一個地方登錄。

usertoken字段是隨時在變的,redis用戶信息和cookie Token令牌都有過期時間,cookie經(jīng)過加密和httpOnly,更大的保證了Token的安全性。

對所有需要校驗(yàn)的http請求做中間件校驗(yàn),通過Token令牌獲取redis用戶信息并驗(yàn)證,驗(yàn)證即通過,驗(yàn)證失敗則重新去登錄。

第三方登錄使用token做用戶是否重復(fù)校驗(yàn),第一次時登錄注冊,第二次登錄時則走登錄邏輯。

原文地址:https://blog.seosiwei.com/det...

文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請注明本文地址:http://m.specialneedsforspecialkids.com/yun/19442.html

相關(guān)文章

  • 系統(tǒng)講解 - SSO單點(diǎn)登錄

    摘要:概念英文全稱,單點(diǎn)登錄。登錄如上述流程圖一致。系統(tǒng)和系統(tǒng)使用認(rèn)證登錄。退出上圖,表示的是從某一個系統(tǒng)退出的流程圖。與的關(guān)系如果企業(yè)有多個管理系統(tǒng),現(xiàn)由原來的每個系統(tǒng)都有一個登錄,調(diào)整為統(tǒng)一登錄認(rèn)證。 概念 SSO 英文全稱 Single Sign On,單點(diǎn)登錄。 在多個應(yīng)用系統(tǒng)中,只需要登錄一次,就可以訪問其他相互信任的應(yīng)用系統(tǒng)。 比如:淘寶網(wǎng)(www.taobao.com),天貓網(wǎng)...

    Kylin_Mountain 評論0 收藏0
  • 聊聊鑒權(quán)那些事

    摘要:什么是鑒權(quán)鑒權(quán)是指驗(yàn)證用戶是否擁有訪問系統(tǒng)的權(quán)利。傳統(tǒng)的鑒權(quán)是通過密碼來驗(yàn)證的。這種方式的前提是,每個獲得密碼的用戶都已經(jīng)被授權(quán)。接下來就一一介紹一下這三種鑒權(quán)方式。 在系統(tǒng)級項(xiàng)目開發(fā)時常常會遇到一個問題就是鑒權(quán),身為一個前端來說可能我們距離鑒權(quán)可能比較遠(yuǎn),一般來說我們也只是去應(yīng)用,并沒有對權(quán)限這一部分進(jìn)行深入的理解。 什么是鑒權(quán) 鑒權(quán):是指驗(yàn)證用戶是否擁有訪問系統(tǒng)的權(quán)利。傳統(tǒng)的鑒權(quán)是...

    wslongchen 評論0 收藏0
  • github 授權(quán)登錄教程與如何設(shè)計(jì)第三方授權(quán)登錄用戶表

    摘要:本文講解的就是授權(quán)登錄的教程。從拿到的用戶信息如下圖最終效果參與文章如何設(shè)計(jì)第三方授權(quán)登錄的用戶表第三方授權(quán)登錄的時候,第三方的用戶信息是存數(shù)據(jù)庫原有的表還是新建一張表呢答案這得看具體項(xiàng)目了,做法多種,請看下文。 showImg(https://segmentfault.com/img/remote/1460000018372844?w=1210&h=828); 需求:在網(wǎng)站上想評論一...

    Acceml 評論0 收藏0
  • 深入理解令牌認(rèn)證機(jī)制token

    摘要:訪問令牌表示授權(quán)授予授予的范圍持續(xù)時間和其他屬性。該規(guī)范還定義了一組通用客戶端元數(shù)據(jù)字段和值,供客戶端在注冊期間使用。授權(quán)服務(wù)器可以為客戶端元數(shù)據(jù)中遺漏的任何項(xiàng)提供默認(rèn)值。 以前的開發(fā)模式是以MVC為主,但是隨著互聯(lián)網(wǎng)行業(yè)快速的發(fā)展逐漸的演變成了前后端分離,若項(xiàng)目中需要做登錄的話,那么token成為前后端唯一的一個憑證。 token即標(biāo)志、記號的意思,在IT領(lǐng)域也叫作令牌。在計(jì)算機(jī)身份...

    hsluoyz 評論0 收藏0

發(fā)表評論

0條評論

最新活動
閱讀需要支付1元查看
<