php7.1版本下钉钉回调接口(解决undefined function)

钉钉官方的php demo文档是几年前的了,其中aes加密这块用的是老版本的函数,会报错Fatal error: Call to undefined function mcrypt_get_block_size()错误 ,其他几个mcrypt_开头函数也会报错。最直观的后果就是返回 { [“errcode”]=> int(71009) [“errmsg”]=> string(22) “返回文本非success” }

mcrypt函数适用于php4、php5版本?php7.0及之前使用该函数只要屏蔽掉报错信息在用到mcrypt函数语句的地方前面加@屏蔽就可以正常使用。但是7.1及以上版本就完全弃用了,这时候可以使用openssl函数来操作。只要是用到AES的地方都需要注意匹配你的php版本哦!下面直接将官方demo包的pkcs7Encoder.php内容替换成我的就行了(根据获取的php版本号自动调用AES加解密函数,因为这个问题测试了无数次,请忽略php文件中的杂乱信息)

<?php
include_once "errorCode.php";
class PKCS7Encoder
{
	public static $block_size = 32;
	function encode($text)
	{
		$block_size = PKCS7Encoder::$block_size;
		$text_length = strlen($text);
		$amount_to_pad = PKCS7Encoder::$block_size - ($text_length % PKCS7Encoder::$block_size);
		if ($amount_to_pad == 0) {
			$amount_to_pad = PKCS7Encoder::block_size;
		}
		$pad_chr = chr($amount_to_pad);
		$tmp = "";
		for ($index = 0; $index < $amount_to_pad; $index++) {
			$tmp .= $pad_chr;
		}
		return $text . $tmp;
	}
	function decode($text)
	{
		$pad = ord(substr($text, -1));
		if ($pad < 1 || $pad > PKCS7Encoder::$block_size) {
			$pad = 0;
		}
		return substr($text, 0, (strlen($text) - $pad));
	}
}
class Prpcrypt
{
	public $key;
	
	
	function __construct($k){

	$this->key = base64_decode($k . "=");        //为变量赋值

	}
	
/*	function Prpcrypt($k)
	{
		$this->key = base64_decode($k . "=");
	}*/
	public function encrypt($text, $corpid)
	{
		try {
			//获得16位随机字符串,填充到明文之前
			$random = $this->getRandomStr();
			$text = $random . pack("N", strlen($text)) . $text . $corpid;
			// 网络字节序
		$version=substr(PHP_VERSION,0,3);
		if($version<'7.1'){//(PHP 4, PHP 5 正常使用) (php6-php7.0 可能会warning提示但是屏蔽错误后可继续使用)(php7.1+ 由openssl_encrypt替代)
		
			// mcrypt 函数在php7.1 以上版本被遗弃,可使用替代函数。。。 这里以@屏蔽错误,  @的使用会影响效率
			@$size = mcrypt_get_block_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_CBC);//mcrypt_get_block_size() 用来获取 cipher (其中包括了加密模式) 加密算法分组大小。
			@$module = mcrypt_module_open(MCRYPT_RIJNDAEL_128, '', MCRYPT_MODE_CBC, '');/// 打开加密算法和模式 
			$iv = substr($this->key, 0, 16);
			//使用自定义的填充方式对明文进行补位填充
			$pkc_encoder = new PKCS7Encoder;
			$text = $pkc_encoder->encode($text);
			@mcrypt_generic_init($module, $this->key, $iv);//mcrypt_generic_init — 初始化加密所需的缓冲区  在每次调用 mcrypt_generic() 或 mdecrypt_generic() 函数之前必须调用本函数。
			//加密
			@$encrypted = mcrypt_generic($module, $text);//本函数用来加密数据。 传入数据长度必须是 n * 分组大小,否则需要后补 "\0"。 本函数返回加密后的数据。 注意,根据数据补齐不同, 返回的数据可能比输入的数据长度有所增加。
			//$moudle为加密描述符,$text为加密前的数据,数据加密函数返回加密后的字符串
			
			@mcrypt_generic_deinit($module);//mcrypt_generic_deinit — 对加密模块进行清理工作   它会清理缓冲区,但是并不关闭模块。 要想关闭加密模块, 你需要自行调用 mcrypt_module_close() 函数。
			@mcrypt_module_close($module);
			//print(base64_encode($encrypted));
			//使用BASE64对加密后的字符串进行编码
			
		}else{	
		//php 7.1	//echo base64_encode(openssl_encrypt($str, 'aes-128-cbc', $key, true, $iv));
	//		$data = openssl_encrypt($text, 'aes-128-cbc',$this->key,true,$iv);
     //   $encrypted = $data;
     $iv = substr($this->key, 0, 16);
        $aes_encrypt=openssl_encrypt($text, 'AES-256-CBC',$this->key,OPENSSL_RAW_DATA, $iv); 
	//	$msg_encrypt =base64_encode($aes_encrypt);
		$encrypted =$aes_encrypt;
        //return $data;
		//php 7.1		
			
		}	
	
			
			return array(ErrorCode::$OK, base64_encode($encrypted));
		} catch (Exception $e) {
			print $e;
			return array(ErrorCode::$EncryptAESError, null);
		}
	}
	public function decrypt($encrypted, $corpid)
	{
		try {
			$ciphertext_dec = base64_decode($encrypted);
			$iv = substr($this->key, 0, 16);
			$version=substr(PHP_VERSION,0,3);
		if($version<'7.1'){
			@$module = mcrypt_module_open(MCRYPT_RIJNDAEL_128, '', MCRYPT_MODE_CBC, '');
			
			@mcrypt_generic_init($module, $this->key, $iv);
			@$decrypted = mdecrypt_generic($module, $ciphertext_dec);
			@mcrypt_generic_deinit($module);
			@mcrypt_module_close($module);
		}else{	
			
			///php7.1 jiemi
			
		//echo openssl_decrypt(base64_decode($strEncode), 'aes-128-cbc', $key, true, $iv);	
		//$decrypted = openssl_decrypt($ciphertext_dec, 'AES-256-CBC',$this->key, OPENSSL_RAW_DATA, $iv);//使用OPENSSL_RAW_DATA不行
$decrypted = openssl_decrypt($encrypted, 'AES-256-CBC',$this->key,OPENSSL_ZERO_PADDING, $iv);//注意openssl_decrypt第一个参数是base64加密过的字符串,第四个参数是OPENSSL_ZERO_PADDING不是上面加密时的 OPENSSL_RAW_DATA
			//	$decrypted .='xx';
			///php7.1 jiemi
			
		}
			
		
		} catch (Exception $e) {
			return array(ErrorCode::$DecryptAESError, null);
		}
		try {
			//去除补位字符
			$pkc_encoder = new PKCS7Encoder;
			$result = $pkc_encoder->decode($decrypted);
			//去除16位随机字符串,网络字节序和AppId
			if (strlen($result) < 16)
				return "";
			$content = substr($result, 16, strlen($result));
			$len_list = unpack("N", substr($content, 0, 4));
			$xml_len = $len_list[1];
			$xml_content = substr($content, 4, $xml_len);
			$from_corpid = substr($content, $xml_len + 4);
		} catch (Exception $e) {
			print $e;
			return array(ErrorCode::$DecryptAESError, null);
		}
		if ($from_corpid != $corpid)
			return array(ErrorCode::$ValidateSuiteKeyError, null);
		return array(0, $xml_content);
	}
	function getRandomStr()
	{
		$str = "";
		$str_pol = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyz";
		$max = strlen($str_pol) - 1;
		for ($i = 0; $i < 16; $i++) {
			$str .= $str_pol[mt_rand(0, $max)];
		}
		return $str;
	}
}
?>

php下使用钉钉回调的方法参考我之前的文章: https://ranjuan.cn/php7-0%E4%B8%8B%E9%92%89%E9%92%89%E5%9B%9E%E8%B0%83%E6%8E%A5%E5%8F%A3%E6%B3%A8%E5%86%8C/

出现“ 返回文本非success ”第一步 ,先把所有错误显示 打印出来,解决掉再去排查其他原因。第二步,直接访问回调url地址看有没有除规定返回的json字符串外有其他输出没有(右键–查看网页源代码 里面找有没有 多余的换行、空格等)。第三步、测试加解密函数。 钉钉的说明文档有点反人类,等哪天有空了写篇文章把钉钉的aes加解密单独分析一下。

2019.11.01提示://注意openssl_decrypt第一个参数是base64加密过的字符串,第四个参数是OPENSSL_ZERO_PADDING不是上面加密时的 OPENSSL_RAW_DATA

基于互联网精神,在注明出处的前提下本站文章可自由转载!

本文链接:https://ranjuan.cn/php7-1版本下钉钉回调接口(解决undefined-function)/

赞赏

微信赞赏支付宝赞赏

p20200706
雅图lw316投影仪换灯泡后亮警示灯
wordpress后台登录报错500

2条评论

Gavin.chen 发布于下午2:25 - 2020年6月1日

感谢感谢!太有用了,直接替换就能用!全网就此一篇解决这个问题,太难得了!感谢大神分享!!!

另外我遇到的问题稍微有点不同,PHP版本都是7.3,但是在一台win7上使用官方文件可以正常注册回调,在其他电脑win10就提示错误,不知道是什么问题。而且作为入门小白,想不到怎么去测试回调,都不知道问题在哪里

    染卷 发布于下午10:24 - 2020年6月1日

    如果你之前已经成功注册过一个回调地址,后面就不能继续注册了(有且仅能注册一个回调地址),此时你需要使用回调接口的更新接口或删除接口才行!见官方文档:https://ding-doc.dingtalk.com/doc#/serverapi2/pwz3r5