Perl gets WeChat applet user information (including openid, unionid)

The AIPs related to WeChat Mini Programs are as follows:

1、wx.login

2、wx.getUserInfo

3. Signature verification and encryption and decryption of user data

 

wx.getUserInfoThe openId and unionId in the interface are sensitive data, so the plaintext content of the interface will not contain these sensitive data . If developers need to obtain sensitive data, they need to symmetrically decrypt the encrypted data ( encryptedData ) returned by the interface. The decryption algorithm is as follows:

  1. The algorithm used for symmetric decryption is AES-128-CBC, and the data is padded with PKCS#7.
  2. The target ciphertext of symmetric decryption is Base64_Decode(encryptedData).
  3. Symmetric decryption key aeskey = Base64_Decode(session_key), aeskey is 16 bytes.
  4. The initial vector of the symmetric decryption algorithm is Base64_Decode(iv), where iv is returned by the data interface.

WeChat officially provides sample codes in multiple programming languages ​​( click to download ). The interface names are the same for each language type. The calling method can refer to the example.

 

# Front-end and back-end processes

1. The front-end calls the interface wx.login  to get the code

2. The front-end calls the interface wx.getUserInfo (the parameter withCredentials is set to true) to obtain the encrypted data encryptedData, iv

3. The front end transmits encryptedData, iv, and code to the server. The server decrypts the data according to the decryption algorithm, and returns the openid/Unionid to the front end.

 

# The server-side decryption algorithm (Perl CGI method) is implemented as follows:

The front-end will POST encryptedData, iv, and code to the server-side CGI script in json mode. The script is as follows:

use warnings;

use utf8;

binmode(STDIN, ':encoding(utf8)');

binmode(STDOUT, ':encoding(utf8)');

binmode(STDERR, ':encoding(utf8)');

use CGI;

use JSON;

use HTTP::Request;

use HTTP::Headers;

use LWP::UserAgent;

use Data::Dumper;

 

$wechat_config={ 

      appid=>'wxd2317scfhe',

      appSecret=>'0b4d05df3f990c8f5576403b1d216d6b'

};

 

$cgi = CGI->new();

$json = JSON->new;

my $OUTPUT = '{"unionid":"", "openid":"", "status":"failed"}';

 

#前端POST数据 {"iv":"xxx", "encryptedData":"xxx", "js_code":"xxx", "act":"getUid"}

if ($cgi->param("POSTDATA")) {

    my $post_data = $cgi->param("POSTDATA");

    #write_log("post_data.log", Dumper($post_data));

    #$post_data =~s/[\r\n]//g;  #去掉回车换行,以免正则匹配失败

 

    my $input_json = $json->decode($post_data);

    if ($input_json->{act} eq "getUid") {

        if (length($input_json->{iv}) && length($input_json->{encryptedData}) && length($input_json->{js_code}) ) {

            my $obj = $input_json->{obj};

            my $iv = $input_json->{iv};

            my $data = $input_json->{encryptedData};

            my $jscode = $input_json->{js_code};

            my $appId = $wechat_config->{appid};

            my $key = $wechat_config->{appSecret};

            my $ret = get_session_key($appId, $key, $jscode);

            my $session_key = "";

            if (defined $ret->{session_key}) {

                $session_key = $ret->{session_key};

            } else {

                $OUTPUT = '{"unionid":"", "openid":"", "status":"failed", "errMsg":"get sessionKey failed"}';

                print_result($OUTPUT);

                exit;

            }

            #write_log("getUid.log", "jscode=".$jscode."\niv= ".$iv."\nencryptedData= ".$data."\nsession_key=".$session_key);

            my $result = readpipe("python /var/app/aes.py $appId $session_key $iv $data");

            #write_log("getUid.log", "result= ".$result);

            if ($result =~m/{.+}/) {

                my $result_json = $json->decode($result);

                my $unionid = "";

                my $openid = $result_json->{openId};

                if (defined $result_json->{unionId}) {

                    $unionid = $result_json->{unionId};

                } else {

                    $unionid = $result_json->{openId};

                }

                $OUTPUT = '{"unionid":"'.$unionid.'", "openid":"'.$openid.'", "status":"success"}';

            }

        }

    } else {

        write_log("input.log", "act:".$input_json->{act}."Not implement");

    }

    print_result($OUTPUT);

}

exit;

 

 

sub get_session_key {

    my ($appId, $secret, $jscode) = @_;

    my $session_key_api = "https://api.weixin.qq.com/sns/jscode2session?appid=".$appId."&secret=".$secret."&js_code=".$jscode."&grant_type=authorization_code";

 

    my $ua = LWP::UserAgent->new();

    my $req = HTTP::Request->new('GET', $session_key_api); 

    my $response = $ua->request($req);

    my $ret;

    if ($response->message ne "OK" && $response->is_success ne "1") { #出错,或者timeout了

        $ret->{status} = "time out";

    } else {

        $ret = $json->decode($response->decoded_content());

    }

    #write_log("session_key.log", " $session_key_api"."\n wechat.rsp:".Dumper($ret));

    return $ret;

 

}

 

# 由于解密必须采用AES算法,而Perl实现起来比较复杂,所以采用了readpipe方式调用Python的AES解密算法来实现,即:

my $result = readpipe("python /var/app/aes.py $appId $session_key $iv $data");

 

# aes.py内容如下:

import base64

#import json

import sys

from Crypto.Cipher import AES

 

def decryptData(appId, sessionKey, encryptedData, iv):

      # base64 decode

      sessionKey = base64.b64decode(sessionKey)

      encryptedData = base64.b64decode(encryptedData)

      iv = base64.b64decode(iv)

 

      cipher = AES.new(sessionKey, AES.MODE_CBC, iv)

      decrypted = _unpad(cipher.decrypt(encryptedData))

 

      #decrypted = json.loads(_unpad(cipher.decrypt(encryptedData)))

      #if decrypted['watermark']['appid'] != appId:

            #print("{\"errMsg\":\"appid mismatched\", \"status\":\"failed\"}")

            #sys.exit()

 

      return decrypted

 

def _unpad(s):

      return s[:-ord(s[len(s)-1:])]

 

 

if (len(sys.argv) != 5):

      print("{\"errMsg\":\"args not enough\", \"status\":\"failed\"}")

      sys.exit()

 

#sys.argv[0] is "aes.py"

appId = sys.argv[1]

sessionKey = sys.argv[2]

iv = sys.argv[3]

encryptedData = sys.argv[4]

 

decrypted = decryptData(appId, sessionKey, encryptedData, iv)

#print("{\"unionid\":\""+ decrypted['unionId'] + "\", \"status\":\"success\"}")

print(decrypted)

sys.exit()

 

 

# aes.py解密后的数据如下:

{

    u'province': u'Guangdong', u'openId': u'oGZUI0egBJY1zhBYw2KhdUfwVJJE', 

    u'language': u'zh_CN', u'city': u'Guangzhou', u'gender': 1, 

    u'avatarUrl':         u'http://wx.qlogo.cn/mmopen/vi_32/aSKcBBPpibyKNicHNTMM0qJVh8Kjgiak2AHWr8MHM4WgMEm7GFhsf8OYrySdbvAMvTsw3mo8ibKicsnfN5pRjl1p8HQ/0', 

    u'watermark': {u'timestamp': 1477314187, u'appid': u'wx4f4bc4dec97d474b'}, 

    u'country': u'CN', u'nickName': u'Band', u'unionId': u'ocMvos6NjeKLIBqg5Mr9QjxrP1FA'

 

}

 

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=326074246&siteId=291194637