成人无码视频,亚洲精品久久久久av无码,午夜精品久久久久久毛片,亚洲 中文字幕 日韩 无码

資訊專欄INFORMATION COLUMN

微信小程序開發(fā):python+sanic 實(shí)現(xiàn)小程序登錄注冊(cè)

antz / 2045人閱讀

摘要:參考鏈接微信小程序七日談第五天你可能要在登錄功能上花費(fèi)大力氣理解認(rèn)證及實(shí)踐網(wǎng)站微信登錄實(shí)現(xiàn)最后,感謝女朋友支持。

開發(fā)微信小程序時(shí),接入小程序的授權(quán)登錄可以快速實(shí)現(xiàn)用戶注冊(cè)登錄的步驟,是快速建立用戶體系的重要一步。這篇文章將介紹 python + sanic + 微信小程序?qū)崿F(xiàn)用戶快速注冊(cè)登錄全棧方案。

微信小程序登錄時(shí)序圖如下:

這個(gè)流程分為兩大部分:

小程序使用 wx.login() API 獲取 code,調(diào)用 wx.getUserInfo() API 獲取 encryptedData 和 iv,然后將這三個(gè)信息發(fā)送給第三方服務(wù)器。

第三方服務(wù)器獲取到 code、encryptedData和 iv 后,使用 code 換取 session_key,然后將 session_key 利用 encryptedData 和 iv 解密在服務(wù)端獲取用戶信息。根據(jù)用戶信息返回 jwt 數(shù)據(jù),完成登錄。

下面我們先看一下小程序提供的 API。

小程序登錄 API

在這個(gè)授權(quán)登錄的過程中,用到的 API 如下:

wx.login

wx.getUserInfo

wx.chekSession 是可選的,這里并沒有用到。

wx.login(OBJECT)

調(diào)用此接口可以獲取登錄憑證(code),以用來(lái)?yè)Q取用戶登錄態(tài)信息,包括用戶的唯一標(biāo)識(shí)(openid) 及本次登錄的 會(huì)話密鑰(session_key)。

如果接口調(diào)用成功,返回結(jié)果如下:

參數(shù)名 類型 說(shuō)明
errMsg String 調(diào)用結(jié)果
code String 用戶允許登錄后,回調(diào)內(nèi)容會(huì)帶上 code(有效期五分鐘),開發(fā)者需要將 code 發(fā)送到開發(fā)者服務(wù)器后臺(tái),使用code 換取 session_key api,將 code 換成 openid 和 session_key
code 換取 session_key

開發(fā)者服務(wù)器使用登錄憑證 code 獲取 session_key 和 openid。其中 session_key 是對(duì)用戶數(shù)據(jù)進(jìn)行加密簽名的密鑰。為了自身應(yīng)用安全,session_key 不應(yīng)該在網(wǎng)絡(luò)上傳輸。所以這一步應(yīng)該在服務(wù)器端實(shí)現(xiàn)。

wx.getUserInfo

此接口用來(lái)獲取用戶信息。

當(dāng) withCredentials 為 true 時(shí),要求此前有調(diào)用過 wx.login 且登錄態(tài)尚未過期,此時(shí)返回的數(shù)據(jù)會(huì)包含 encryptedData, iv 等敏感信息;當(dāng) withCredentials 為 false 時(shí),不要求有登錄態(tài),返回的數(shù)據(jù)不包含 encryptedData, iv 等敏感信息。

接口success 時(shí)返回參數(shù)如下:

參數(shù)名 類型 說(shuō)明
userInfo OBJECT 用戶信息對(duì)象,不包含 openid 等敏感信息
rawData String 不包括敏感信息的原始數(shù)據(jù)字符串,用于計(jì)算簽名。
signature String 使用 sha1( rawData + sessionkey ) 得到字符串,用于校驗(yàn)用戶信息,參考文檔 signature。
encryptedData String 包括敏感數(shù)據(jù)在內(nèi)的完整用戶信息的加密數(shù)據(jù),詳細(xì)見加密數(shù)據(jù)解密算法
iv String 加密算法的初始向量,詳細(xì)見加密數(shù)據(jù)解密算法

encryptedData 解密后為以下 json 結(jié)構(gòu),詳見加密數(shù)據(jù)解密算法

{
    "openId": "OPENID",
    "nickName": "NICKNAME",
    "gender": GENDER,
    "city": "CITY",
    "province": "PROVINCE",
    "country": "COUNTRY",
    "avatarUrl": "AVATARURL",
    "unionId": "UNIONID",
    "watermark":
    {
        "appid":"APPID",
    "timestamp":TIMESTAMP
    }
}

由于解密 encryptedData 需要 session_key 和 iv 所以,在給服務(wù)器端發(fā)送授權(quán)驗(yàn)證的過程中需要將 code、encryptedData 和 iv 一起發(fā)送。

服務(wù)器端提供的 API

服務(wù)器端授權(quán)需要提供兩個(gè) API:

/oauth/token 通過小程序提供的驗(yàn)證信息獲取服務(wù)器自己的 token

/accounts/wxapp 如果登錄用戶是未注冊(cè)用戶,使用此接口注冊(cè)為新用戶。

換取第三方 token(/oauth/token)

開始授權(quán)時(shí),小程序調(diào)用此 API 嘗試換取jwt,如果用戶未注冊(cè)返回401,如果用戶發(fā)送參數(shù)錯(cuò)誤,返回403。

接口 獲取 jwt 成功時(shí)返回參數(shù)如下:

參數(shù)名 類型 說(shuō)明
account_id string 當(dāng)前授權(quán)用戶的用戶 ID
access_token string jwt(登錄流程中的第三方 session_key
token_type string token 類型(固定Bearer)

小程序授權(quán)后應(yīng)該先調(diào)用此接口,如果結(jié)果是用戶未注冊(cè),則應(yīng)該調(diào)用新用戶注冊(cè)的接口先注冊(cè)新用戶,注冊(cè)成功后再調(diào)用此接口換取 jwt。

新用戶注冊(cè)(/accounts/wxapp)

注冊(cè)新用戶時(shí),服務(wù)器端需要存儲(chǔ)當(dāng)前用戶的 openid,所以和授權(quán)接口一樣,請(qǐng)求時(shí)需要的參數(shù)為 code、encryptedData 和 iv。

注冊(cè)成功后,將返回用戶的 ID 和注冊(cè)時(shí)間。此時(shí),應(yīng)該再次調(diào)用獲取 token 的接口去換取第三方 token,以用來(lái)下次登錄。

實(shí)現(xiàn)流程

接口定義好之后,來(lái)看下前后端整體的授權(quán)登錄流程。

這個(gè)流程需要注意的是,在 C 步(使用 code 換取 session )之后我們得到 session_key,然后需要用 session_key 解密得到用戶數(shù)據(jù)。

然后使用 openid 判斷用戶是否已經(jīng)注冊(cè),如果用戶已經(jīng)注冊(cè),生成 jwt 返回給小程序。
如果用戶未注冊(cè)返回401, 提示用戶未注冊(cè)。

jwt(3rd_session) 用于第三方服務(wù)器和小程序之間做登錄態(tài)校驗(yàn),為了保證安全性,jwt 應(yīng)該滿足:

足夠長(zhǎng)。建議有 2^128 組合

避免使用 srand(當(dāng)前時(shí)間),然后 rand() 的方法,而是采用操作系統(tǒng)提供的真正隨機(jī)數(shù)機(jī)制。

設(shè)置一定的有效時(shí)間,

當(dāng)然,在小程序中也可以使用手機(jī)號(hào)登錄,不過這是另一個(gè)功能了,就不在這里敘述了。

代碼實(shí)現(xiàn)

說(shuō)了這么多,接下來(lái)看代碼吧。

小程序端代碼

代碼邏輯為:

用戶在小程序授權(quán)

小程序?qū)⑹跈?quán)消息發(fā)送到服務(wù)器,服務(wù)器檢查用戶是否已經(jīng)注冊(cè),如果注冊(cè)返回 jwt,如果沒注冊(cè)提示用戶未注冊(cè),然后小程序重新請(qǐng)求注冊(cè)接口,注冊(cè)用戶,注冊(cè)成功后重復(fù)這一步。

為了簡(jiǎn)便,這里在小程序 啟動(dòng)的時(shí)候就請(qǐng)求授權(quán)。代碼實(shí)現(xiàn)如下。

//app.js
var config = require("./config.js")

App({
    onLaunch: function() {
        //調(diào)用API從本地緩存中獲取數(shù)據(jù)
        var jwt = wx.getStorageSync("jwt");
        var that = this;
        if (!jwt.access_token){ //檢查 jwt 是否存在 如果不存在調(diào)用登錄
            that.login();
        } else {
            console.log(jwt.account_id);
        }
    },
    login: function() {
        // 登錄部分代碼
        var that = this;
        wx.login({
            // 調(diào)用 login 獲取 code
            success: function(res) {
                var code = res.code;
                wx.getUserInfo({
                    // 調(diào)用 getUserInfo 獲取 encryptedData 和 iv
                    success: function(res) {
                        // success
                        that.globalData.userInfo = res.userInfo;
                        var encryptedData = res.encryptedData || "encry";
                        var iv = res.iv || "iv";
                        console.log(config.basic_token);
                        wx.request({ // 發(fā)送請(qǐng)求 獲取 jwt
                            url: config.host + "/auth/oauth/token?code=" + code,
                            header: {
                                Authorization: config.basic_token
                            },
                            data: {
                                username: encryptedData,
                                password: iv,
                                grant_type: "password",
                                auth_approach: "wxapp",
                            },
                            method: "POST",
                            success: function(res) {
                                if (res.statusCode === 201) {
                                    // 得到 jwt 后存儲(chǔ)到 storage,
                                    wx.showToast({
                                        title: "登錄成功",
                                        icon: "success"
                                    });
                                    wx.setStorage({
                                        key: "jwt",
                                        data: res.data
                                    });
                                    that.globalData.access_token = res.data.access_token;
                                    that.globalData.account_id = res.data.sub;
                                } else if (res.statusCode === 401){
                                    // 如果沒有注冊(cè)調(diào)用注冊(cè)接口
                                    that.register();
                                } else {
                                    // 提示錯(cuò)誤信息
                                    wx.showToast({
                                        title: res.data.text,
                                        icon: "success",
                                        duration: 2000
                                    });
                                }
                            },
                            fail: function(res) {
                                console.log("request token fail");
                            }
                        })
                    },
                    fail: function() {
                        // fail
                    },
                    complete: function() {
                        // complete
                    }
                })
            }
        })

    },
    register: function() {
        // 注冊(cè)代碼
        var that = this;
        wx.login({ // 調(diào)用登錄接口獲取 code
            success: function(res) {
                var code = res.code;
                wx.getUserInfo({
                    // 調(diào)用 getUserInfo 獲取 encryptedData 和 iv
                    success: function(res) {
                        // success
                        that.globalData.userInfo = res.userInfo;
                        var encryptedData = res.encryptedData || "encry";
                        var iv = res.iv || "iv";
                        console.log(iv);
                        wx.request({ // 請(qǐng)求注冊(cè)用戶接口
                            url: config.host + "/auth/accounts/wxapp",
                            header: {
                                Authorization: config.basic_token
                            },
                            data: {
                                username: encryptedData,
                                password: iv,
                                code: code,
                            },
                            method: "POST",
                            success: function(res) {
                                if (res.statusCode === 201) {
                                    wx.showToast({
                                        title: "注冊(cè)成功",
                                        icon: "success"
                                    });
                                    that.login();
                                } else if (res.statusCode === 400) {
                                    wx.showToast({
                                        title: "用戶已注冊(cè)",
                                        icon: "success"
                                    });
                                    that.login();
                                } else if (res.statusCode === 403) {
                                    wx.showToast({
                                        title: res.data.text,
                                        icon: "success"
                                    });
                                }
                                console.log(res.statusCode);
                                console.log("request token success");
                            },
                            fail: function(res) {
                                console.log("request token fail");
                            }
                        })
                    },
                    fail: function() {
                        // fail
                    },
                    complete: function() {
                        // complete
                    }
                })
            }
        })

    },

    get_user_info: function(jwt) {
        wx.request({
            url: config.host + "/auth/accounts/self",
            header: {
                Authorization: jwt.token_type + " " + jwt.access_token
            },
            method: "GET",
            success: function (res) {
                if (res.statusCode === 201) {
                    wx.showToast({
                        title: "已注冊(cè)",
                        icon: "success"
                    });
                } else if (res.statusCode === 401 || res.statusCode === 403) {
                    wx.showToast({
                        title: "未注冊(cè)",
                        icon: "error"
                    });
                }

                console.log(res.statusCode);
                console.log("request token success");
            },
            fail: function (res) {
                console.log("request token fail");
            }
        })
    },

    globalData: {
        userInfo: null
    }
})
服務(wù)端代碼

服務(wù)端使用 sanic 框架 + swagger_py_codegen 生成 rest-api。
數(shù)據(jù)庫(kù)使用 MongoDB,python-weixin 實(shí)現(xiàn)了登錄過程中 code 換取 session_key 以及 encryptedData 解密的功能,所以使用python-weixin 作為 python 微信 sdk 使用。

為了過濾無(wú)效請(qǐng)求,服務(wù)器端要求用戶在獲取 token 或授權(quán)時(shí)在 header 中帶上 Authorization 信息。 Authorization 在登錄前使用的是 Basic 驗(yàn)證(格式 (Basic hashkey) 注 hashkey為client_id + client_secret 做BASE64處理),只是用來(lái)校驗(yàn)請(qǐng)求的客戶端是否合法。不過Basic 基本等同于明文,并不能用它來(lái)進(jìn)行嚴(yán)格的授權(quán)驗(yàn)證。

jwt 原理及使用參見 理解JWT(JSON Web Token)認(rèn)證及實(shí)踐

使用 swagger 生成代碼結(jié)構(gòu)如下:

由于代碼太長(zhǎng),這里只放獲取 jwt 的邏輯:

def get_wxapp_userinfo(encrypted_data, iv, code):
    from weixin.lib.wxcrypt import WXBizDataCrypt
    from weixin import WXAPPAPI
    from weixin.oauth2 import OAuth2AuthExchangeError
    appid = Config.WXAPP_ID
    secret = Config.WXAPP_SECRET
    api = WXAPPAPI(appid=appid, app_secret=secret)
    try:
        # 使用 code  換取 session key    
        session_info = api.exchange_code_for_session_key(code=code)
    except OAuth2AuthExchangeError as e:
        raise Unauthorized(e.code, e.description)
    session_key = session_info.get("session_key")
    crypt = WXBizDataCrypt(appid, session_key)
    # 解密得到 用戶信息
    user_info = crypt.decrypt(encrypted_data, iv)
    return user_info


def verify_wxapp(encrypted_data, iv, code):
    user_info = get_wxapp_userinfo(encrypted_data, iv, code)
    # 獲取 openid
    openid = user_info.get("openId", None)
    if openid:
        auth = Account.get_by_wxapp(openid)
        if not auth:
            raise Unauthorized("wxapp_not_registered")
        return auth
    raise Unauthorized("invalid_wxapp_code")
    
    
def create_token(request):
    # verify basic token
    approach = request.json.get("auth_approach")
    username = request.json["username"]
    password = request.json["password"]
    if approach == "password":
        account = verify_password(username, password)
    elif approach == "wxapp":
        account = verify_wxapp(username, password, request.args.get("code"))
    if not account:
        return False, {}
    payload = {
        "iss": Config.ISS,
        "iat": int(time.time()),
        "exp": int(time.time()) + 86400 * 7,
        "aud": Config.AUDIENCE,
        "sub": str(account["_id"]),
        "nickname": account["nickname"],
        "scopes": ["open"]
    }
    token = jwt.encode(payload, "secret", algorithm="HS256")
    # 由于 account 中 _id 是一個(gè) object 需要轉(zhuǎn)化成字符串
    return True, {"access_token": token, "account_id": str(account["_id"])}

具體代碼可以在 Metis:https://github.com/gusibi/Metis 查看。

Note: 如果試用代碼,請(qǐng)先設(shè)定 oauth2_client,使用自己的配置。

不要將私密配置信息提交到 github。

參考鏈接

《微信小程序七日談》- 第五天:你可能要在登錄功能上花費(fèi)大力氣

理解JWT(JSON Web Token)認(rèn)證及實(shí)踐

網(wǎng)站微信登錄-python 實(shí)現(xiàn)

最后,感謝女朋友支持。

歡迎關(guān)注(April_Louisa) 請(qǐng)我喝芬達(dá)

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

轉(zhuǎn)載請(qǐng)注明本文地址:http://m.hztianpu.com/yun/84479.html

相關(guān)文章

  • 微信開發(fā)python+sanic 實(shí)現(xiàn)登錄注冊(cè)

    摘要:參考鏈接微信小程序七日談第五天你可能要在登錄功能上花費(fèi)大力氣理解認(rèn)證及實(shí)踐網(wǎng)站微信登錄實(shí)現(xiàn)最后,感謝女朋友支持。 開發(fā)微信小程序時(shí),接入小程序的授權(quán)登錄可以快速實(shí)現(xiàn)用戶注冊(cè)登錄的步驟,是快速建立用戶體系的重要一步。這篇文章將介紹 python + sanic + 微信小程序?qū)崿F(xiàn)用戶快速注冊(cè)登錄全棧方案。 微信小程序登錄時(shí)序圖如下: showImg(https://segmentfaul...

    Nino 評(píng)論0 收藏0
  • 開發(fā):上傳圖片到騰訊云

    這是小程序開發(fā)第二篇,主要介紹如何上傳圖片到騰訊云,之所以選擇騰訊云,是因?yàn)轵v訊云免費(fèi)空間大? 準(zhǔn)備工作 上傳圖片主要是將圖片上傳到騰訊云對(duì)象存儲(chǔ)(COS)。 要使用對(duì)象存儲(chǔ) API,需要先執(zhí)行以下步驟: 購(gòu)買騰訊云對(duì)象存儲(chǔ)(COS)服務(wù) 在騰訊云 對(duì)象存儲(chǔ)控制臺(tái) 里創(chuàng)建一個(gè) Bucket 在控制臺(tái) 個(gè)人 API 密鑰 頁(yè)面里獲取 AppID、SecretID、SecretKey 內(nèi)容 編寫一個(gè)...

    why_rookie 評(píng)論0 收藏0
  • 開發(fā):上傳圖片到騰訊云

    這是小程序開發(fā)第二篇,主要介紹如何上傳圖片到騰訊云,之所以選擇騰訊云,是因?yàn)轵v訊云免費(fèi)空間大? 準(zhǔn)備工作 上傳圖片主要是將圖片上傳到騰訊云對(duì)象存儲(chǔ)(COS)。 要使用對(duì)象存儲(chǔ) API,需要先執(zhí)行以下步驟: 購(gòu)買騰訊云對(duì)象存儲(chǔ)(COS)服務(wù) 在騰訊云 對(duì)象存儲(chǔ)控制臺(tái) 里創(chuàng)建一個(gè) Bucket 在控制臺(tái) 個(gè)人 API 密鑰 頁(yè)面里獲取 AppID、SecretID、SecretKey 內(nèi)容 編寫一個(gè)...

    idisfkj 評(píng)論0 收藏0
  • ??蘇州程大白一文教你學(xué)會(huì)微信開發(fā)??《??記得收藏??》

    ??蘇州程序大白一文教你學(xué)會(huì)微信小程序開發(fā)??《??記得收藏??》 目錄 ????開講啦?。。。????蘇州程序大白?????博主介紹?前言?講講專享小程序有什么優(yōu)勢(shì)? ?小程序文件分析?事件綁定?圖片問題?輪播圖swiper?自定義組件?生命周期?頁(yè)面生命周期?項(xiàng)目制作?緩沖事件?`es7 async`語(yǔ)法 ?觸底事件??下拉刷新頁(yè)面??css省略號(hào)??預(yù)覽大圖??購(gòu)物車模擬??獲取地...

    劉明 評(píng)論0 收藏0
  • 微信開發(fā)由0到1開發(fā),快速開發(fā)上線

    摘要:首先先注冊(cè)微信小程序管理一登錄微信公眾平臺(tái)二點(diǎn)擊立即注冊(cè)。注意這里不要用微信公眾號(hào)登錄,小程序賬號(hào)和微信公眾號(hào)是不同的。最好從項(xiàng)目直接入手,這里有微信小程序個(gè)例子,鏈接密碼有可能會(huì)過期,留言或者加我,給你最新的 首先先注冊(cè)微信小程序管理 一、登錄微信公眾平臺(tái)https://mp.weixin.qq.com 二、點(diǎn)擊立即注冊(cè)。 注意:這里不要用微信公眾號(hào)登錄,小程序賬號(hào)和微信公眾號(hào)是不...

    seal_de 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

閱讀需要支付1元查看
<