Perlは微信ウィジェットのユーザー情報(openid,unionidを含む)を取得する


詳細
微信ウィジェット関連AIPは以下の通りである.
1、wx.login
2、wx.getUserInfo
3、ユーザーデータの署名検証と復号化
 
インタフェースwx.getUserInfoのopenIdおよびunionIdは機密データに属するため、インタフェースの明文コンテンツにはこれらの機密データは含まれない.開発者が機密データを取得する必要がある場合は,インタフェースから返される暗号化データ(encryptedData)を対称的に復号する必要がある.復号アルゴリズムは次のとおりです.
  • 対称復号で使用されるアルゴリズムはAES-128-CBCであり、データはPKCS#7で埋め込まれている.
  • 対称復号のターゲット暗号文はBase 64_Decode(encryptedData).
  • 対称復号鍵aeskey=Base 64_Decode(session_key)、aeskeyは16バイトです.
  • 対称復号アルゴリズム初期ベクトルはBase 64_Decode(iv)は、ivがデータインタフェースによって返される.

  • 微信公式には、さまざまなプログラミング言語のサンプルコードが提供されています(ダウンロードをクリック).各言語タイプのインタフェースの名前は一致しています.呼び出し方法は例を参照できます.
     
    #フロントエンドプロセス
    1.フロントエンド呼び出しインタフェースwx.login,codeを得る
    2.フロントエンドコールインタフェースwx.getUserInfo(パラメータwithCredentialsをtrueに設定)、暗号化データencryptedData,ivを取得
    3、フロントエンドは暗号化データencryptedData,iv,およびcodeをサービス側に伝え、サービス側は復号アルゴリズムに従ってデータを復号し、openid/Unionidを得てフロントエンドに戻る
     
    #サービス側復号アルゴリズム(Perl CGI方式)は以下のように実現される:
    フロントエンドでは、encryptedData、iv、およびcodeをjson方式のPOSTでサービス側に渡すCGIスクリプトが次のようにスクリプトされます.
    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]//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."iv= ".$iv."encryptedData= ".$data."session_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_successne「1」){#エラー、またはtimeout
            $ret->{status} = "time out";
        } else {
            $ret = $json->decode($response->decoded_content());
        }
        #write_log("session_key.log", "$session_key_api"."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'
     
    }