Perlは微信ウィジェットのユーザー情報(openid,unionidを含む)を取得する
7695 ワード
詳細
微信ウィジェット関連AIPは以下の通りである.
1、wx.login
2、wx.getUserInfo
3、ユーザーデータの署名検証と復号化
インタフェース対称復号で使用されるアルゴリズムは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'
}
微信ウィジェット関連AIPは以下の通りである.
1、wx.login
2、wx.getUserInfo
3、ユーザーデータの署名検証と復号化
インタフェース
wx.getUserInfo
のopenIdおよびunionIdは機密データに属するため、インタフェースの明文コンテンツにはこれらの機密データは含まれない.開発者が機密データを取得する必要がある場合は,インタフェースから返される暗号化データ(encryptedData)を対称的に復号する必要がある.復号アルゴリズムは次のとおりです.微信公式には、さまざまなプログラミング言語のサンプルコードが提供されています(ダウンロードをクリック).各言語タイプのインタフェースの名前は一致しています.呼び出し方法は例を参照できます.
#フロントエンドプロセス
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'
}