签名生成的通用步骤如下:

第一步,设所有发送或者接收到的数据为集合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";


Peanut
6 声望0 粉丝

引用和评论

0 条评论