签名生成的通用步骤如下:
第一步,设所有发送或者接管到的数据为汇合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';//调配的keyksort($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 decodem, _ := url.ParseQuery(params)fmt.Println(m)var address = "http://www.baidu.com?name=jack+"+paramsencodeUrl:=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";