背景
公司需要对外开放接口,因此需要进行签名和验签。所以,自定义了一个签名和验签规则。
具体实现
- 1.定义抽象类
<?php
namespace App\Library\Signature;
abstract class AbstractSecret
{
/**
* 对接ID
* @var string
*/
protected $appId = 'ty808732';
/**
* @var string
*/
protected $key = '6c340d5113a7689168796dd65d2391d9cd26a4859051467c';
/**
* 接口名
* @var string
*/
protected $method = 'chip.api.goods.list';
/**
* 商户号
* @var string
*/
protected $partnerId = 'chip';
/**
* 渠道服务编码
* @var int
*/
protected $channelType = 5900;
/**
* 版本号
* @var int
*/
protected $version = 'V1';
/**
* 数据格式
* @var int
*/
protected $dateType = 'json';
/**
* 字符
* @var string
*/
protected $charset = 'utf-8';
/**
* 对接密钥
* @var string
*/
//protected static $secretKey = 'zYYB0E8YdyNwuveyJUDGWr';//测试
protected $secretKey = 'I1pFucrCa78EO9BrNocNuS';//生产
/**
* 请求参数
* @var array
*/
protected $item;
}
- 2.封装签名规则
<?php
namespace App\Library\Signature;
/**
* 加解密demo
* Class Secret
* @package App\Library\Signature
*/
class Secret extends AbstractSecret
{
/**
* 加密类
* @var object
*/
private $encrypt;
/**
* 解密类
* @var object
*/
private $decrypt;
public function __construct($item,$key='')
{
$this->item = $item;
if($key) $this->key = $key;
$this->encrypt = new Encrypt();
$this->decrypt = new Decrypt();
}
/**
* @return array
* @throws \Exception
*/
public function encrypt()
{
if(!$this->item){
throw new \Exception('业务参数不能为空');
}
$bizContent = $this->item;
if(is_array($bizContent)){
$bizContent = json_encode($bizContent,1);
}
$request = $this->baseRequest($bizContent);
ksort($request);
$secret = urldecode(http_build_query($request));
$sign = md5(sha1($secret));
$sign = base64_encode(strtoupper($sign));
$request['sign'] = $sign;
$request['sign_type'] = 'MD5';
unset($request['key']);
return $request;
}
/**
* @param string $biz_content
* @param null $datetime
* @return array
*/
private function baseRequest(string $biz_content, $datetime = null)
{
$request = [
'partner_id' => $this->partnerId,
'method' => $this->method,
'format' => 'JSON',
'charset' => 'utf-8',
'time_stamp' => $datetime ?: date('Y-m-d H:i:s'),
'version' => '2.0',
'app_id' => $this->appId,
'channel_type' => $this->channelType,
'biz_content' => $this->encrypt->encrypt3DES($biz_content),
'key' => $this->secretKey,
];
unset($this->encrypt);
return $request;
}
}
- 3.对数据进行验签
<?php
namespace App\Library\Signature;
use App\Model\ChipmallApiModel;
/**
* 签名
* Class Signature
* @package App\Library\Signature
*/
class Signature extends AbstractSecret
{
/**
* @return array
*/
private function baseRequest()
{
$timestamp = empty($this->item['time_stamp'])
? date('Y-m-d H:i:s')
: $this->item['time_stamp'];
$request = [
'partner_id' => $this->partnerId,
'method' => $this->method,
'format' => $this->dateType,
'charset' => $this->charset,
'time_stamp' => $timestamp,
'version' => $this->version,
'app_id' => $this->appId,
'channel_type' => $this->channelType,
'biz_content' => $this->item['biz_content'],
'key' => $this->secretKey,
];
return $request;
}
private function getAppInfo($appId)
{
if(!isset($appId) || empty($appId)){
throw new \Exception('App ID not allow empty');
}
$appInfo = ChipmallApiModel::where('app_id',$appId)->first();
if(!$appInfo){
throw new \Exception('App Info is null');
}
$this->appId = $appInfo->app_id;
$this->key = $appInfo->app_sign;
$this->method = $appInfo->type;
$this->channelType = $appInfo->channel ?: 5900;
$this->partnerId = $appInfo->partner_id;
$this->version = $appInfo->version;
$this->dateType = $appInfo->data_type;
unset($appInfo);
return $this;
}
/**
* @return array
* @throws \Exception
*/
public function sign()
{
if(!$this->item){
throw new \Exception('Business parameters cannot be empty');
}
if (!isset($this->item['time_stamp']) || empty($this->item['time_stamp'])) {
throw new \Exception('timestamp is empty');
}
if (!isset($this->item['biz_content']) || empty($this->item['biz_content'])) {
throw new \Exception('Biz content is empty');
}
$request = $this->baseRequest();
ksort($request);
$secret = urldecode(http_build_query($request));
$sign = md5(sha1($secret));
$sign = base64_encode(strtoupper($sign));
$request['sign'] = $sign;
$request['sign_type'] = 'sha1';
unset($request['key']);
return $request;
}
/**
* 验签
* @param string $sign
* @return $this
* @throws \Exception
*/
public function verifySign(string $sign)
{
if(!$this->item){
throw new \Exception('Business parameters cannot be empty');
}
if(!isset($this->item['app_id']) || empty($this->item['app_id'])){
throw new \Exception('App ID is empty');
}
if(!isset($this->item['time_stamp']) || empty($this->item['time_stamp'])){
throw new \Exception('timestamp is empty');
}
$appId = $this->item['app_id'];
$this->getAppInfo($appId);
$encrypt = $this->sign();
$appSign = $encrypt['sign'];
if (!isset($sign) || empty($sign)) {
throw new \Exception('Signature is empty');
}
if ($appSign != $sign) {
throw new \Exception('Signature verification failed');
}
return $this;
}
}
- 4.数据进行加密
<?php
namespace App\Library\Signature;
/**
* 加密
* Class Encrypt
* @package App\Library\Signature
*/
class Encrypt extends AbstractSecret
{
/**
* 加密3DES
* @param string $encrypt 需要加密的数据
* @return string
*/
public function encrypt3DES($encrypt)
{
$out = openssl_encrypt($encrypt, 'DES-EDE3', $this->key, OPENSSL_RAW_DATA);
$encrypt = base64_encode($out);
return $encrypt;
}
/**
* 加密AES-128-ECB
* @param string $encrypt 需要加密的数据
* @return string
*/
public function encrypt($encrypt) {
$encrypted = openssl_encrypt($encrypt, 'AES-128-ECB', $this->key, OPENSSL_RAW_DATA);
return base64_encode($encrypted);
}
}
- 5.数据进行解密
<?php
namespace App\Library\Signature;
/**
* 解密
* Class Decrypt
* @package App\Library\Signature
*/
class Decrypt extends AbstractSecret
{
/**
* 解密AES-128-ECB
* @param string $data 需要解密的数据
* @return string
*/
public function decrypt($data) {
$decrypted = base64_decode($data);
return openssl_decrypt($decrypted, 'AES-128-ECB', $this->key, OPENSSL_RAW_DATA);
}
/**
* 解密3DES
* @param string $data 需要解密的数据
* @return false|string
*/
public function decrypt3DES($data)
{
$key = base64_decode($this->key);
$decrypted = openssl_decrypt($data,'DES-EDE3',$key,OPENSSL_RAW_DATA);
return $decrypted;
}
}
以上是我根据自己项目需要封装的加解密和验签。你可以根据你的项目实际需要进行封装。