Wechat applet development: Python + sanic to achieve small program login and registration

Time:2020-10-23

When developing wechat applet, the authorized login of access applet can quickly realize the steps of user registration and login, which is an important step to quickly establish user system. This article will introduce the full stack solution of Python + sanic + wechat applet to realize users’ fast registration and login.

The login sequence diagram of wechat applet is as follows:

Wechat applet development: Python + sanic to achieve small program login and registration

This process is divided into two parts:

  1. Applet usage wx.login () API gets code and calls wx.getUserInfo The () API gets encrypted data and IV, and then sends these three information to the third-party server.

  2. After the third-party server obtains the code, encrypted data and IV, it exchanges the session with the code_ Key, and then set the session_ Key uses encrypted data and IV decryption to obtain user information at the server. According to the user information, JWT data is returned to complete the login.

Let’s take a look at the API provided by the applet.

Applet login API

The API used in this authorization login process is as follows:

  • wx.login

  • wx.getUserInfo

wx.chekSessionIt’s optional. It’s not used here.

wx.login(OBJECT)

Calling this interface can obtain the login Certificate (code) in exchange for the user’s login status information, including the user’s unique ID (openid) and the session key (session) of this login_ key)。

If the interface call is successful, the return result is as follows:

Parameter name type explain
errMsg String Call result
code String After the user is allowed to log in, the callback content will carry the code (valid for five minutes). The developer needs to send the code to the developer SERVER background and exchange the code for the session_ Key API, changing the code to openid and session_ Key

Code for session_ Key

The developer server uses the login credential code to obtain the session_ Key and openid. Where session_ Key is the key used to encrypt and sign user data. For its own application security, session_ The key should not be transmitted over the network. So this step should be implemented on the server side.

wx.getUserInfo

This interface is used to obtain user information.

WhenwithCredentialsWhen true, it is required that a call has been made before wx.login If the login status has not expired, the returned data will contain sensitive information such as encrypted data and IV. when withcredentials is false, there is no requirement for login status, and the returned data does not contain sensitive information such as encrypted data and IV.

When interface success, the returned parameters are as follows:

Parameter name type explain
userInfo OBJECT The user information object does not contain sensitive information such as openid
rawData String A raw data string that does not include sensitive information and is used to calculate the signature.
signature String Use SHA1 (raw data + sessionkey) to get the string, which is used to verify the user information. Refer to the document signature.
encryptedData String For encrypted data of complete user information including sensitive data, see encryption data decryption algorithm for details
iv String For the initial vector of encryption algorithm, see encryption data decryption algorithm for details

encryptedDataAfter decryption, it is the following JSON structure. See the encryption data decryption algorithm for details

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

Session is required to decrypt encrypted data_ Key and IV. therefore, code, encrypted data and IV need to be sent together in the process of sending authorization verification to the server.

API provided by server

Server side authorization needs to provide two APIs:

  1. /OAuth / token obtains the server’s own token through the verification information provided by the applet

  2. /Accounts / wxApp if the login user is an unregistered user, use this interface to register as a new user.

Exchange for a third party token (/ OAuth / token)

At the beginning of authorization, the applet calls this API to try to exchange JWT. If the user is not registered, it returns 401; if the user sends wrong parameters, it returns 403.

When the interface gets JWT successfully, the returned parameters are as follows:

Parameter name type explain
account_id string The user ID of the current authorized user
access_token string JWT (third party session in login process)_ Key
token_type string Token type (fixed bearer)

After authorization, the applet should call this interface first. If the result is that the user is not registered, it should call the interface registered by the new user, register the new user first, and then call this interface to exchange for JWT after successful registration.

New user registration (/ accounts / wxApp)

When registering a new user, the server needs to store the current user’s openid. Therefore, as with the authorization interface, the required parameters are code, encrypted data and IV.

After successful registration, the user’s ID and registration time will be returned. At this point, the interface that obtains the token should be called again to exchange for the third-party token for the next login.

Implementation process

After the interface is defined, take a look at the overall authorization login process of the front and back-end.

Wechat applet development: Python + sanic to achieve small program login and registration

It should be noted in this process that we get the session after step C (exchange the code for the session)_ Key, and then you need to use session_ Key decrypts to get user data.

Then use openid to determine whether the user has registered. If the user has registered, generate JWT and return it to the applet.
If the user is not registered, 401 is returned to prompt that the user is not registered.

jwt(3rd_session)It is used to verify the login state between the third-party server and the applet. In order to ensure security, JWT should meet the following requirements:

  1. It’s long enough. A combination of 2 ^ 128 is recommended

  2. Instead of using the method of srand (current time), and then rand (), use the real random number mechanism provided by the operating system.

  3. Set a certain effective time,

Of course, you can also log in with your mobile phone number in the applet, but this is another function, which will not be described here.

code implementation

With all that said, let’s look at the code.

Applet side code

The code logic is as follows:

  1. User authorization in applet

  2. The applet sends the authorization message to the server. The server checks whether the user has registered. If the user is registered, JWT is returned. If not, the user is prompted not to register. Then the applet requests the registration interface again to register the user. Repeat this step after the registration is successful.

For simplicity, authorization is requested when the applet starts. The code implementation is as follows.

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

App({
    onLaunch: function() {
        //Call API to get data from local cache
        var jwt = wx.getStorageSync('jwt');
        var that = this;
        if (! jwt.access_ Token) {// check whether JWT exists, if not, call login
            that.login();
        } else {
            console.log(jwt.account_id);
        }
    },
    login: function() {
        //Login part code
        var that = this;
        wx.login({
            //Call login to get the code
            success: function(res) {
                var code = res.code;
                wx.getUserInfo({
                    //Call getUserInfo to get encrypteddata and 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 ({// send a request to get 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) {
                                    //After the JWT is stored, it is stored,
                                    wx.showToast({
                                        Title: 'login succeeded',
                                        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){
                                    //If there is no registration, call the registration interface
                                    that.register();
                                } else {
                                    //Prompt error message
                                    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() {
        //Registration code
        var that = this;
        wx.login ({// call the login interface to get the code
            success: function(res) {
                var code = res.code;
                wx.getUserInfo({
                    //Call getUserInfo to get encrypteddata and 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 ({// request to register user interface
                            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: 'registered successfully',
                                        icon: 'success'
                                    });
                                    that.login();
                                } else if (res.statusCode === 400) {
                                    wx.showToast({
                                        Title: 'user registered',
                                        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: 'registered',
                        icon: 'success'
                    });
                } else if (res.statusCode === 401 || res.statusCode === 403) {
                    wx.showToast({
                        Title: 'not registered',
                        icon: 'error'
                    });
                }

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

    globalData: {
        userInfo: null
    }
})

Server code

Server side usesanicFramework+swagger_py_codegenGenerate rest API.
The database uses mongodb,python-weixinThe code is exchanged for session in the login process_ Key and encrypted data decryption function, so Python Weixin is used as Python wechat SDK.

In order to filter invalid requests, the server requires the user to bring the token or authorization in the headerAuthorizationInformation.AuthorizationBefore logging in, basic hashkey is used. Note that hashkey is client_ id + client_ Secret does Base64 processing), which is only used to verify whether the requested client is legal. However, basic is basically equivalent to plaintext, and can not be used for strict authorization verification.

The principle and use of JWT refer to understanding JWT (JSON web token) authentication and practice

The code structure generated by swagger is as follows:

Wechat applet development: Python + sanic to achieve small program login and registration

Because the code is too long, only the logic to get JWT is put here

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:
        #Use code to exchange 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)
    #Decrypt to get user information
    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)
    #Get 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')
    #Due to the_ ID is an object that needs to be converted into a string
    return True, {'access_token': token, 'account_id': str(account['_id'])}

Specific codes can be found in Metis: https://github.com/gusibi/Metis see.

Note: if you try the code, please set oauth2 first_ Client, use your own configuration.

Do not submit private configuration information to GitHub.

Reference link

  • “Wechat app 7 days talk” – day 5: you may have to spend a lot of effort on the login function

  • Understanding JWT (JSON web token) authentication and Practice

  • Website wechat login – Python implementation


Finally, thank your girlfriend for your support.

Welcome to (April_ Louisa) Treat me to Fanta
Wechat applet development: Python + sanic to achieve small program login and registration Wechat applet development: Python + sanic to achieve small program login and registration

Recommended Today

NOR flash memory begins the development of automobile field

As cars become more intelligent and require more storage space, many technologies are struggling for the driver’s seat, but it is certain that the NOR flash can at least use a shot gun.With its programmable ability, nor flash exists in many applicationsEEPROMIt has found new opportunities in applications that require fast, nonvolatile memory, including communications, […]