共计 5021 个字符,预计需要花费 13 分钟才能阅读完成。
签名生成的通用步骤如下:
第一步,设所有发送或者接管到的数据为汇合 M,将汇合 M 内非空参数值的参数依照参数名 ASCII 码从小到大排序(字典序),应用 URL 键值对的格局(即 key1=value1&key2=value2…)拼接成字符串 stringA。
特地留神以下重要规定:
◆ 参数名 ASCII 码从小到大排序(字典序);
◆ 如果参数的值为空不参加签名;
◆ 参数名辨别大小写;
◆ 验证调用返回或微信被动告诉签名时,传送的 sign 参数不参加签名,将生成的签名与该 sign 值作校验。
◆ 微信接口可能减少字段,验证签名时必须反对减少的扩大字段
第二步,在 stringA 最初拼接上 key 失去 stringSignTemp 字符串,并对 stringSignTemp 进行 MD5 运算,再将失去的字符串所有字符转换为大写,失去 sign 值 signValue。
举例:
假如传送的参数如下:
appid:wxd930ea5d5a258f4f
mch_id:10000100
device_info:1000
body:test
第一步:对参数依照 key=value 的格局,并依照参数名 ASCII 字典序排序如下:
appid=”appid=wxd930ea5d5a258f4f&body=test&device_info=1000&mch_id=10000100
第二步:拼接 API 密钥:
string=stringA+”&key=192006250b4c09247ec02edce69f6a2d” // 注:key 为实现依据 sign_source 调配的密钥 key
sign=strtoupper(md5(string))/ 注:MD5 签名形式
例子:
$requestUrl = 'http://api-common-baidu.com/v1/api/family/getfamilyInfo'; // 接口申请地址
$params['user_id'] = $user_id;
$params['member_id'] = $member_id;
$params['sign_source'] = 'baidu_hao123';
$signKey = 'c3609e87bfd28c2f34aba9f18269bd0e';// 调配的 key
ksort($params);
$string = http_build_query($params);
$sign = $string."&key=".$signKey;
$params['sign'] = strtoupper(md5($sign));
$result = Curl::to($requestUrl)->withData($params)->post();
$result = json_decode($result,true);
sha1 版本
// 验证签名
public function checkSignature($token,$signature,$timestamp,$nonce){$tmpStr = $this->sign($token, $timestamp, $nonce);
// 将 sha1 加密后的字符串可与 signature 比照
if(empty($tmpStr)){return false;}
if($tmpStr!=strtoupper($signature)){return false;}
return true;
}
// 生成签名
public function sign($token, $timestamp, $nonce){$arr = [$token,$timestamp,$nonce];
sort($arr,SORT_FLAG_CASE|SORT_LOCALE_STRING);
$content="";
for ($i=0;$i<count($arr);$i++){$content .=$arr[$i];
}
$signature = sha1($content);
$signature = strtoupper($signature);
Log::log('info','create-signature',["signature"=>$signature]);
return $signature;
0 = SORT_REGULAR - 默认。把每一项按惯例顺序排列(Standard ASCII,不扭转类型)。
1 = SORT_NUMERIC – 把每一项作为数字来解决。
2 = SORT_STRING – 把每一项作为字符串来解决。
3 = SORT_LOCALE_STRING – 把每一项作为字符串来解决,基于以后区域设置(可通过 setlocale() 进行更改)。
4 = SORT_NATURAL – 把每一项作为字符串来解决,应用相似 natsort() 的天然排序。
5 = SORT_FLAG_CASE – 能够联合(按位或)SORT_STRING 或 SORT_NATURAL 对字符串进行排序,不辨别大小写
golang 版本实现
package main
import (
"sort"
"crypto/sha1"
"encoding/hex"
"strings"
"strconv"
"time"
"fmt"
"math/rand"
)
const Token = "d6a0b3aacfe5417d60q5s23f3e21ac21"
var letters = []rune("0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ")
func init(){
// 以工夫作为初始化种子
rand.Seed(time.Now().UnixNano())
}
func main(){Nonce := randSeq(10)
signature,_:=CreateSign(Token,strconv.Itoa(int(time.Now().Unix())),Nonce)
fmt.Println("Signature:",signature)
}
// 按字典序生成签名
func CreateSign(Token string,TimeStamp string,Nonce string) (string,error) {
var signature string
strArr:=[]string{Token,TimeStamp,Nonce}
sort.Strings(strArr)
var content string
content = ""
for _,v:=range strArr{content += v}
hash := sha1.New()
hash.Write([]byte(content))
cipherStr:=hash.Sum(nil)
// 二进制数组转换为 16 进制
signature = strings.ToUpper(hex.EncodeToString(cipherStr))
return signature,nil
}
// 返回指定长度得字符串随机数
func randSeq(n int) string {b := make([]rune, n)
for i := range b {b[i] = letters[rand.Intn(len(letters))]
}
return string(b)
}
java 版本实现
private static final String SHA_ALG = "SHA-1";
/**
* 验证签名
*
* @param token 加盐字符串
* @param signature 加密签名
* @param timestamp 工夫戳
* @param nonce 随机数
* @return
*/
public static boolean checkSignature(String token, String signature, String timestamp, String nonce) {String tmpStr = sign(token, timestamp, nonce);
// 将 sha1 加密后的字符串可与 signature 比照
return tmpStr != null ? tmpStr.equals(signature.toUpperCase()) : false;
}
public static String sign(String token, String timestamp, String nonce) {String[] arr = new String[]{token, timestamp, nonce};
// 将 token、timestamp、nonce、三个参数进行字典序排序
Arrays.sort(arr);
StringBuilder content = new StringBuilder();
for (int i = 0; i < arr.length; i++) {content.append(arr[i]);
}
MessageDigest md = null;
String signature = null;
try {md = MessageDigest.getInstance(SHA_ALG);
// 将三个参数字符串拼接成一个字符串进行 sha1 加密
byte[] digest = md.digest(content.toString().getBytes());
signature = byteToStr(digest);
} catch (NoSuchAlgorithmException e) {log.error(e.getMessage());
}
return signature;
}
/**
* 将字节数组转换为十六进制字符串
*
* @param byteArray
* @return
*/
private static String byteToStr(byte[] byteArray) {
String strDigest = "";
for (int i = 0; i < byteArray.length; i++) {strDigest += byteToHexStr(byteArray[i]);
}
return strDigest;
}
/**
* 将字节转换为十六进制字符串
*
* @param mByte
* @return
*/
private static String byteToHexStr(byte mByte) {char[] Digit = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};
char[] tempArr = new char[2];
tempArr[0] = Digit[(mByte >>> 4) & 0X0F];
tempArr[1] = Digit[mByte & 0X0F];
String s = new String(tempArr);
return s;
}
URL 传参 + 号变空格
http://api.baidu.com/retail/l…
服务器端的失去的 username 参数发现 + 号变成空格了, 能够传参数时对该参数进行 urlencode, 使 + 变为 %2B,接管参数时候再 urldecode 该参数
golang 版本
package main
import (
"fmt"
"net/url"
)
func main() {
v := url.Values{}
v.Add("p", "1")
v.Add("drive", "ios")
params := v.Encode()
fmt.Println(params)
//// url decode
m, _ := url.ParseQuery(params)
fmt.Println(m)
var address = "http://www.baidu.com?name=jack+"+params
encodeUrl:=url.QueryEscape(address)
fmt.Println(encodeUrl)
uncodeUrl,err:=url.QueryUnescape(encodeUrl)
if err!=nil {fmt.Println(err)
}
fmt.Println(uncodeUrl)
//drive=ios&p=1
//map[drive:[ios] p:[1]]
//http%3A%2F%2Fwww.baidu.com%3Fname%3Djack%2Bdrive%3Dios%26p%3D1
//http://www.baidu.com?name=jack+drive=ios&p=1
}
php 版本
<?php
echo urlencode(“jia+”);
echo “————\n”;
echo urldecode($strurl);
echo “\n\r”;