签名机制
:::highlight blue 📌
为了防止API调用过程中被黑客恶意篡改,调用API需要携带签名,服务端会根据请求参数,对签名进行验证,签名不合法的请求将会被拒绝。目前支持的签名算法有两种:MD5(sign_method=md5),HMAC_SHA256(sign_method=hmac-sha256),
:::
签名大体过程如下:
以访问凭证令牌举例:
1) appid=557ADoER&sign_method=hmac_sha256×tamp=16854380449999 + 秘钥
示例:
2) md5:
appid=557ADoER&sign_method=md5×tamp=16854380449999 + 秘钥
示例:
Java示例
以访问凭证令牌举例:
import org.apache.commons.codec.digest.HmacAlgorithms;
import org.apache.commons.codec.digest.HmacUtils;
import org.springframework.util.DigestUtils;
md5加密
String sign = DigestUtils.md5DigestAsHex((appid=557ADoER&sign_method=md5×tamp=16854380449999密钥).getBytes())
HMAC_SHA_256 加密
String sign = new HmacUtils(HmacAlgorithms.HMAC_SHA_256, 密钥).hmacHex(appid=557ADoER&sign_method=hmac_sha256×tamp=16854380449999)
签名方法代码示例:
package com.das.libcore.sign.utils;
import com.das.libcore.common.utils.JsonUtils;
import com.das.libcore.sign.handle.SignMethodHandle;
import org.apache.commons.codec.digest.HmacAlgorithms;
import org.apache.commons.codec.digest.HmacUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.util.DigestUtils;
import java.util.HashMap;
import java.util.Map;
import java.util.TreeMap;
/**
* TODO
*
* @Author: yaok
* @CreateTime: 2023-04-23 15:40
* @Version: 1.0
*/
public class SignUtils {
/**
* 应用id
*/
private static final String APPID = "appid";
/**
* 时间戳
*/
private static final String TIMESTAMP = "timestamp";
/**
* 签名
*/
private static final String SIGNATURE = "sign";
/**
* 加密方式
*/
private static final String SIGN_METHOD = "sign_method";
/**
* 签名类型
*/
private static final String SIGN_TYPE = "sign_type";
/**
* 防重复提价序号
*/
private static final String NONCE = "nonce";
/**
* 处理方式map
*/
public static HashMap<String, SignMethodHandle> handleMap = new HashMap<>(8);
/**
* 支持算法
*/
public static String supportSignMethod;
{
//每个类较小,暂时通过java8新特性函数指针来实现策略类
SignMethodHandles signMethodHandles = new SignMethodHandles();
handleMap.put("md5", signMethodHandles::getMd5Sign);
handleMap.put("hmac_sha256", signMethodHandles::getHmacSha256Sign);
StringBuilder temp = new StringBuilder();
for (String key : handleMap.keySet()) {
temp.append(key).append(",");
}
supportSignMethod = temp.length() > 0 ? temp.substring(0, temp.length() - 1) : temp.toString();
}
/**
* 生成秘钥
*
* @param params 路径参数、param参数
* @param signMethodType 加密类型
* @param appSecret 秘钥
* @param bodyObject body对象
* @return: {@link String}
* @throws:
*/
private String sign(Map<String, String> params, String signMethodType, String appSecret, Object bodyObject) throws Exception {
SignMethodHandle signMethodHandle = handleMap.get(signMethodType);
TreeMap<String, String> treeMap = new TreeMap<>(params);
//获取body(对应@RequestBody), 只能是json, 为文件表单和null跳过
//对body进行压缩未做,java没找到好的压缩工具
String newSign = generateSign(JsonUtils.toJson(bodyObject), treeMap, appSecret, signMethodHandle);
return newSign;
}
/**
* 生成签名
* <p>
* 签名规则:将query参数名和body升序排序后:
* HMACSHA256(body={"police":"noPo"}&appid=knasdfnas&age=17&name=yuhun×tamp=111111&sign_method=md5,secret)
* <p>
* 如果是md5,则在query参数末尾追加secret
* md5(body={"police":"noPo"}&appid=knasdfnas&age=17&name=yuhun×tamp=111111&sign_method=md5+secret)
*
* @param body 请求体内容
* @param params 参数
* @param appSecret appSecret
* @param signMethodHandle 签名方法
* @return {@link String}
*/
private String generateSign(String body, Map<String, String> params, String appSecret, SignMethodHandle signMethodHandle) {
StringBuilder sb = new StringBuilder();
StringBuilder paramSb = new StringBuilder();
for (Map.Entry<String, String> entry : params.entrySet()) {
String name = entry.getKey();
String value = entry.getValue();
paramSb.append(name).append("=").append(value).append("&");
}
String paramStr = paramSb.length() > 0 ? paramSb.substring(0, paramSb.length() - 1) : paramSb.toString();
if (!StringUtils.isBlank(body)) {
sb.append("body=").append(body).append("&");
}
String str = sb.append(paramStr).toString();
return signMethodHandle.getSign(str, appSecret);
}
/**
* 签名方式实现类(一个方法可对应一个接口实现,也可以通过单独集成类来实现)
*
* @author tangsq
* @date 2022/10/19
*/
public class SignMethodHandles {
/**
* MD5加密
*
* @param paramStr 参数信息
* @param secret 密文
* @return {@link String}
*/
public String getMd5Sign(String paramStr, String secret) {
return DigestUtils.md5DigestAsHex((paramStr + secret).getBytes());
}
/**
* Sha256加密
*
* @param paramStr 参数信息
* @param secret 密文
* @return {@link String}
*/
public String getHmacSha256Sign(String paramStr, String secret) {
return new HmacUtils(HmacAlgorithms.HMAC_SHA_256, secret).hmacHex(paramStr);
}
}
}
/**
* 签名方式处理接口
*
* @author tangsq
* @date 2022/10/18
*/
public interface SignMethodHandle {
/**
* 不同算方返回签名策略
*
* @param paramStr 参数合并的字符串
* @param secret 密文
* @return {@link String} 返回签名
*/
String getSign(String paramStr, String secret);
}
C#示例
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Cryptography;
using System.Text;
namespace SignUtilsDemo
{
public class SignUtils
{
private const string APPID = "appid";
private const string TIMESTAMP = "timestamp";
private const string SIGNATURE = "sign";
private const string SIGN_METHOD = "sign_method";
private const string SIGN_TYPE = "sign_type";
private const string NONCE = "nonce";
private Dictionary<string, Func<string, string, string>> _handleMap = new Dictionary<string, Func<string, string, string>>()
{
{ "md5", GetMd5Sign },
{ "hmac_sha256", GetHmacSha256Sign }
};
public string SupportSignMethod => string.Join(",", _handleMap.Keys);
private static string GetMd5Sign(string paramStr, string secret)
{
using (var md5 = MD5.Create())
{
var bytes = md5.ComputeHash(Encoding.UTF8.GetBytes(paramStr + secret));
return BitConverter.ToString(bytes).Replace("-", "").ToLower();
}
}
private static string GetHmacSha256Sign(string paramStr, string secret)
{
using (var hmacSha256 = new HMACSHA256(Encoding.UTF8.GetBytes(secret)))
{
var bytes = hmacSha256.ComputeHash(Encoding.UTF8.GetBytes(paramStr));
return BitConverter.ToString(bytes).Replace("-", "").ToLower();
}
}
private string GenerateSign(string body, Dictionary<string, string> parameters, string appSecret, Func<string, string, string> signMethodHandle)
{
var sortedParams = parameters.OrderBy(p => p.Key, StringComparer.Ordinal);
var paramStr = string.Join("&", sortedParams.Select(p => $"{p.Key}={p.Value}"));
var sb = new StringBuilder();
if (!string.IsNullOrWhiteSpace(body))
{
sb.Append($"body={body}&");
}
sb.Append(paramStr);
var str = sb.ToString();
return signMethodHandle(str, appSecret);
}
public string Sign(Dictionary<string, string> parameters, string signMethodType, string appSecret, object bodyObject)
{
if (!_handleMap.TryGetValue(signMethodType, out var signMethodHandle))
{
throw new ArgumentException($"Unsupported sign method: {signMethodType}");
}
var body = JsonConvert.SerializeObject(bodyObject);
var newSign = GenerateSign(body, parameters, appSecret, signMethodHandle);
return newSign;
}
}
}
F#示例
open System
open System.Collections.Generic
open System.Security.Cryptography
open System.Text
open Newtonsoft.Json
type SignUtils() =
let mutable support_sign_method = "md5,hmac_sha256"
let get_md5_sign (param_str: string) (secret: string) =
let data = param_str + secret
use md5 = MD5.Create()
md5.ComputeHash(Encoding.UTF8.GetBytes(data)) |> Array.map (fun b -> b.ToString("x2")) |> String.concat ""
let get_hmac_sha256_sign (param_str: string) (secret: string) =
let data = Encoding.UTF8.GetBytes(param_str)
let key = Encoding.UTF8.GetBytes(secret)
use sha256 = new HMACSHA256(key)
sha256.ComputeHash(data) |> Array.map (fun b -> b.ToString("x2")) |> String.concat ""
let generate_sign (body: string) (parameters: IDictionary<string, string>) (app_secret: string) (sign_method_handle: Func<string, string, string>) =
let keys = parameters.Keys |> Seq.sort
let param_str =
match body with
| "" -> ""
| _ -> "body=" + body + "&"
parameters
|> Seq.fold (fun acc k -> acc + k + "=" + parameters.[k] + "&") param_str
|> fun s -> s + app_secret
sign_method_handle s app_secret
member this.Sign (parameters: IDictionary<string, string>) (sign_method_type: string) (app_secret: string) (body_object: obj) =
let sign_method_handle =
match sign_method_type with
| "md5" -> this.get_md5_sign
| "hmac_sha256" -> this.get_hmac_sha256_sign
| _ -> failwith "Unsupported sign method: " + sign_method_type
let body = JsonConvert.SerializeObject(body_object)
let timestamp = int (DateTime.UtcNow - DateTime(1970, 1, 1)).TotalSeconds
parameters.[APPID_KEY] <- "your_app_id"
parameters.[TIMESTAMP_KEY] <- string timestamp
parameters.[NONCE_KEY] <- "your_nonce"
parameters.[SIGN_METHOD_KEY] <- sign_method_type
parameters.[SIGN_TYPE_KEY] <- "MD5"
this.generate_sign body parameters app_secret sign_method_handle
member this.GetSupportSignMethod() = support_sign_method
member this.SetSupportSignMethod(s: string) = support_sign_method <- s
Golang示例
package signutils
import (
"crypto/hmac"
"crypto/md5"
"crypto/sha256"
"encoding/hex"
"encoding/json"
"fmt"
"sort"
"strings"
"time"
)
const (
appIDKey = "appid"
timestampKey = "timestamp"
signatureKey = "sign"
signMethodKey = "sign_method"
signTypeKey = "sign_type"
nonceKey = "nonce"
)
type SignUtils struct {
SupportSignMethod string
}
type SignMethodHandle func(string, string) string
func NewSignUtils() *SignUtils {
return &SignUtils{
SupportSignMethod: "md5,hmac_sha256",
}
}
func (su *SignUtils) GetMd5Sign(paramStr, secret string) string {
h := md5.New()
h.Write([]byte(paramStr + secret))
return hex.EncodeToString(h.Sum(nil))
}
func (su *SignUtils) GetHmacSha256Sign(paramStr, secret string) string {
h := hmac.New(sha256.New, []byte(secret))
h.Write([]byte(paramStr))
return hex.EncodeToString(h.Sum(nil))
}
func (su *SignUtils) GenerateSign(body string, parameters map[string]string, appSecret string, signMethodHandle SignMethodHandle) string {
keys := make([]string, 0, len(parameters))
for k := range parameters {
keys = append(keys, k)
}
sort.Strings(keys)
sb := strings.Builder{}
if len(body) > 0 {
sb.WriteString(fmt.Sprintf("body=%s&", body))
}
for _, k := range keys {
sb.WriteString(fmt.Sprintf("%s=%s&", k, parameters[k]))
}
str := sb.String()
return signMethodHandle(str, appSecret)
}
func (su *SignUtils) Sign(parameters map[string]string, signMethodType, appSecret string, bodyObject interface{}) string {
handleMap := map[string]SignMethodHandle{
"md5": su.GetMd5Sign,
"hmac_sha256": su.GetHmacSha256Sign,
}
signMethodHandle, ok := handleMap[signMethodType]
if !ok {
panic(fmt.Sprintf("Unsupported sign method: %s", signMethodType))
}
bodyBytes, _ := json.Marshal(bodyObject)
body := string(bodyBytes)
timestamp := time.Now().Unix()
parameters[appIDKey] = "your_app_id"
parameters[timestampKey] = fmt.Sprintf("%d", timestamp)
parameters[nonceKey] = "your_nonce"
parameters[signMethodKey] = signMethodType
parameters[signTypeKey] = "MD5"
newSign := su.GenerateSign(body, parameters, appSecret, signMethodHandle)
return newSign
}
PHP示例
<?php
class SignUtils
{
const APPID = "appid";
const TIMESTAMP = "timestamp";
const SIGNATURE = "sign";
const SIGN_METHOD = "sign_method";
const SIGN_TYPE = "sign_type";
const NONCE = "nonce";
private $supportSignMethod = "md5,hmac_sha256";
public function getSupportSignMethod()
{
return $this->supportSignMethod;
}
public function setSupportSignMethod($supportSignMethod)
{
$this->supportSignMethod = $supportSignMethod;
}
private function getMd5Sign($paramStr, $secret)
{
return md5($paramStr . $secret);
}
private function getHmacSha256Sign($paramStr, $secret)
{
return hash_hmac("sha256", $paramStr, $secret);
}
private function generateSign($body, $parameters, $appSecret, $signMethodHandle)
{
ksort($parameters);
$paramStr = http_build_query($parameters, "", "&", PHP_QUERY_RFC3986);
$urlEncodedParamStr = urlencode($paramStr);
$urlEncodedAppSecret = urlencode($appSecret);
$str = $urlEncodedParamStr . $urlEncodedAppSecret;
return $signMethodHandle($str, $appSecret);
}
public function sign($parameters, $signMethodType, $appSecret, $bodyObject)
{
$handleMap = array(
"md5" => array($this, "getMd5Sign"),
"hmac_sha256" => array($this, "getHmacSha256Sign")
);
$signMethodHandle = $handleMap[$signMethodType];
if (!$signMethodHandle) {
throw new InvalidArgumentException("Unsupported sign method: " . $signMethodType);
}
$body = json_encode($bodyObject);
$timestamp = time();
$parameters[self::APPID] = "your_app_id";
$parameters[self::TIMESTAMP] = $timestamp;
$parameters[self::NONCE] = "your_nonce";
$parameters[self::SIGN_METHOD] = $signMethodType;
$parameters[self::SIGN_TYPE] = "MD5";
$newSign = $this->generateSign($body, $parameters, $appSecret, $signMethodHandle);
return $newSign;
}
}
NodeJS示例
const crypto = require('crypto');
class SignUtils {
constructor() {
this.supportSignMethod = "md5,hmac_sha256";
}
getSupportSignMethod() {
return this.supportSignMethod;
}
setSupportSignMethod(supportSignMethod) {
this.supportSignMethod = supportSignMethod;
}
getMd5Sign(paramStr, secret) {
return crypto.createHash('md5').update(paramStr + secret).digest('hex');
}
getHmacSha256Sign(paramStr, secret) {
return crypto.createHmac('sha256', secret).update(paramStr).digest('hex');
}
generateSign(body, parameters, appSecret, signMethodHandle) {
const sortedKeys = Object.keys(parameters).sort();
const sortedParams = sortedKeys.map(key => `${key}=${parameters[key]}`).join('&');
const paramStr = `${sortedParams}${appSecret}`;
return signMethodHandle(paramStr, appSecret);
}
sign(parameters, signMethodType, appSecret, bodyObject) {
const handleMap = {
"md5": this.getMd5Sign,
"hmac_sha256": this.getHmacSha256Sign
};
const signMethodHandle = handleMap[signMethodType];
if (!signMethodHandle) {
throw new Error(`Unsupported sign method: ${signMethodType}`);
}
const body = JSON.stringify(bodyObject);
const timestamp = Math.floor(Date.now() / 1000);
parameters["appid"] = "your_app_id";
parameters["timestamp"] = timestamp;
parameters["nonce"] = "your_nonce";
parameters["sign_method"] = signMethodType;
parameters["sign_type"] = "MD5";
const newSign = this.generateSign(body, parameters, appSecret, signMethodHandle);
return newSign;
}
}
module.exports = SignUtils;
Rust示例
use std::collections::HashMap;
use openssl::sha::Sha256;
use openssl::md5::Md5;
use openssl::hash::MessageDigest;
use openssl::pkey::PKey;
use openssl::sign::Signer;
use serde_json;
const APPID_KEY: &str = "appid";
const TIMESTAMP_KEY: &str = "timestamp";
const SIGNATURE_KEY: &str = "sign";
const SIGN_METHOD_KEY: &str = "sign_method";
const SIGN_TYPE_KEY: &str = "sign_type";
const NONCE_KEY: &str = "nonce";
struct SignUtils {
support_sign_method: String,
}
impl SignUtils {
fn new() -> Self {
SignUtils {
support_sign_method: String::from("md5,hmac_sha256"),
}
}
fn get_md5_sign(&self, param_str: &str, secret: &str) -> String {
let mut md5 = Md5::new();
md5.update(param_str.as_bytes());
md5.update(secret.as_bytes());
hex::encode(md5.finalize())
}
fn get_hmac_sha256_sign(&self, param_str: &str, secret: &str) -> String {
let key = PKey::hmac(secret.as_bytes()).unwrap();
let mut signer = Signer::new(MessageDigest::sha256(), &key).unwrap();
signer.update(param_str.as_bytes()).unwrap();
hex::encode(signer.sign_to_vec().unwrap())
}
fn generate_sign(&self, body: &str, parameters: &HashMap<String, String>, app_secret: &str, sign_method_handle: fn(&SignUtils, &str, &str) -> String) -> String {
let mut keys: Vec<&String> = parameters.keys().collect();
keys.sort();
let mut param_str = String::new();
if !body.is_empty() {
param_str.push_str(&format!("{}={}&", "body", body));
}
for key in keys {
let value = parameters.get(key).unwrap();
param_str.push_str(&format!("{}={}&", key, value));
}
param_str.push_str(app_secret);
sign_method_handle(self, ¶m_str, app_secret)
}
fn sign(&self, parameters: &mut HashMap<String, String>, sign_method_type: &str, app_secret: &str, body_object: &serde_json::Value) -> String {
let handle_map: HashMap<&str, fn(&SignUtils, &str, &str) -> String> = [
("md5", SignUtils::get_md5_sign),
("hmac_sha256", SignUtils::get_hmac_sha256_sign),
].iter().cloned().collect();
let sign_method_handle = *handle_map.get(sign_method_type).unwrap_or_else(|| panic!("Unsupported sign method: {}", sign_method_type));
let body = serde_json::to_string(body_object).unwrap_or_default();
let timestamp = chrono::Utc::now().timestamp();
parameters.insert(APPID_KEY.to_string(), "your_app_id".to_string());
parameters.insert(TIMESTAMP_KEY.to_string(), timestamp.to_string());
parameters.insert(NONCE_KEY.to_string(), "your_nonce".to_string());
parameters.insert(SIGN_METHOD_KEY.to_string(), sign_method_type.to_string());
parameters.insert(SIGN_TYPE_KEY.to_string(), "MD5".to_string());
let new_sign = self.generate_sign(&body, parameters, app_secret, sign_method_handle);
new_sign
}
}
C++示例
#include <iostream>
#include <string>
#include <vector>
#include <map>
#include <algorithm>
#include <openssl/md5.h>
#include <openssl/hmac.h>
#include <openssl/evp.h>
#include <openssl/bio.h>
#include <openssl/buffer.h>
#include <chrono>
#include <ctime>
#include <nlohmann/json.hpp>
using json = nlohmann::json;
const std::string APPID_KEY = "appid";
const std::string TIMESTAMP_KEY = "timestamp";
const std::string SIGNATURE_KEY = "sign";
const std::string SIGN_METHOD_KEY = "sign_method";
const std::string SIGN_TYPE_KEY = "sign_type";
const std::string NONCE_KEY = "nonce";
class SignUtils {
private:
std::string support_sign_method = "md5,hmac_sha256";
std::string get_md5_sign(std::string param_str, std::string secret) {
unsigned char md[MD5_DIGEST_LENGTH];
std::string data = param_str + secret;
MD5(reinterpret_cast<const unsigned char*>(data.c_str()), data.size(), md);
std::stringstream ss;
for (int i = 0; i < MD5_DIGEST_LENGTH; i++) {
ss << std::hex << std::setfill('0') << std::setw(2) << static_cast<unsigned int>(md[i]);
}
return ss.str();
}
std::string get_hmac_sha256_sign(std::string param_str, std::string secret) {
unsigned int result_len;
unsigned char result[EVP_MAX_MD_SIZE];
HMAC(EVP_sha256(), reinterpret_cast<const unsigned char*>(secret.c_str()), secret.size(), reinterpret_cast<const unsigned char*>(param_str.c_str()), param_str.size(), result, &result_len);
std::stringstream ss;
for (unsigned int i = 0; i < result_len; i++) {
ss << std::hex << std::setfill('0') << std::setw(2) << static_cast<unsigned int>(result[i]);
}
return ss.str();
}
std::string generate_sign(std::string body, std::map<std::string, std::string> parameters, std::string app_secret, std::function<std::string(std::string, std::string)> sign_method_handle) {
std::vector<std::string> keys;
for (const auto& kv : parameters) {
keys.push_back(kv.first);
}
std::sort(keys.begin(), keys.end());
std::stringstream ss;
for (const auto& key : keys) {
ss << key << "=" << parameters[key] << "&";
}
ss << app_secret;
std::string param_str = ss.str();
return sign_method_handle(param_str, app_secret);
}
public:
std::string sign(std::map<std::string, std::string>& parameters, std::string sign_method_type, std::string app_secret, json body_object) {
std::function<std::string(std::string, std::string)> sign_method_handle;
if (sign_method_type == "md5") {
sign_method_handle = [this](std::string param_str, std::string secret) { return this->get_md5_sign(param_str, secret); };
} else if (sign_method_type == "hmac_sha256") {
sign_method_handle = [this](std::string param_str, std::string secret) { return this->get_hmac_sha256_sign(param_str, secret); };
} else {
throw std::invalid_argument("Unsupported sign method: " + sign_method_type);
}
auto body = body_object.dump();
auto timestamp = std::chrono::duration_cast<std::chrono::seconds>(std::chrono::system_clock::now().time_since_epoch()).count();
parameters[APPID_KEY] = "your_app_id";
parameters[TIMESTAMP_KEY] = std::to_string(timestamp);
parameters[NONCE_KEY] = "your_nonce";
parameters[SIGN_METHOD_KEY] = sign_method_type;
parameters[SIGN_TYPE_KEY] = "MD5";
auto new_sign = generate_sign(body, parameters, app_secret, sign_method_handle);
return new_sign;
}
std::string get_support_sign_method() {
return support_sign_method;
}
void set_support_sign_method(std::string support_sign_method) {
this->support_sign_method = support_sign_method;
}
};
Lua示例
local json = require("json")
local openssl = require("openssl")
local md5 = openssl.digest.get("md5")
local hmac = openssl.hmac
local sha256 = openssl.digest.get("sha256")
local SignUtils = {}
local APPID_KEY = "appid"
local TIMESTAMP_KEY = "timestamp"
local SIGNATURE_KEY = "sign"
local SIGN_METHOD_KEY = "sign_method"
local SIGN_TYPE_KEY = "sign_type"
local NONCE_KEY = "nonce"
function SignUtils:new()
local newObj = {}
self.__index = self
setmetatable(newObj, self)
newObj.support_sign_method = "md5,hmac_sha256"
return newObj
end
function SignUtils:get_md5_sign(param_str, secret)
local data = param_str .. secret
return md5:final(data)
end
function SignUtils:get_hmac_sha256_sign(param_str, secret)
return hmac.new(secret, param_str, sha256):final()
end
function SignUtils:generate_sign(body, parameters, app_secret, sign_method_handle)
local keys = {}
for k in pairs(parameters) do
table.insert(keys, k)
end
table.sort(keys)
local param_str = ""
if body ~= "" then
param_str = "body=" .. body .. "&"
end
for _, key in ipairs(keys) do
param_str = param_str .. key .. "=" .. parameters[key] .. "&"
end
param_str = param_str .. app_secret
return sign_method_handle(param_str, app_secret)
end
function SignUtils:sign(parameters, sign_method_type, app_secret, body_object)
local sign_method_handle
if sign_method_type == "md5" then
sign_method_handle = function(param_str, secret) return self:get_md5_sign(param_str, secret) end
elseif sign_method_type == "hmac_sha256" then
sign_method_handle = function(param_str, secret) return self:get_hmac_sha256_sign(param_str, secret) end
else
error("Unsupported sign method: " .. sign_method_type)
end
local body = json.encode(body_object)
local timestamp = os.time()
parameters[APPID_KEY] = "your_app_id"
parameters[TIMESTAMP_KEY] = tostring(timestamp)
parameters[NONCE_KEY] = "your_nonce"
parameters[SIGN_METHOD_KEY] = sign_method_type
parameters[SIGN_TYPE_KEY] = "MD5"
local new_sign = self:generate_sign(body, parameters, app_secret, sign_method_handle)
return new_sign
end
function SignUtils:get_support_sign_method()
return self.support_sign_method
end
function SignUtils:set_support_sign_method(support_sign_method)
self.support_sign_method = support_sign_method
end
return SignUtils
Python示例
import hashlib
import hmac
import time
import json
APPID_KEY = "appid"
TIMESTAMP_KEY = "timestamp"
SIGNATURE_KEY = "sign"
SIGN_METHOD_KEY = "sign_method"
SIGN_TYPE_KEY = "sign_type"
NONCE_KEY = "nonce"
class SignUtils:
def __init__(self):
self.support_sign_method = "md5,hmac_sha256"
def get_md5_sign(self, param_str, secret):
data = param_str + secret
return hashlib.md5(data.encode()).hexdigest()
def get_hmac_sha256_sign(self, param_str, secret):
data = param_str.encode()
secret = secret.encode()
return hmac.new(secret, data, hashlib.sha256).hexdigest()
def generate_sign(self, body, parameters, app_secret, sign_method_handle):
keys = sorted(list(parameters.keys()))
param_str = ""
if body != "":
param_str = "body=" + body + "&"
for key in keys:
param_str += key + "=" + parameters[key] + "&"
param_str += app_secret
return sign_method_handle(param_str, app_secret)
def sign(self, parameters, sign_method_type, app_secret, body_object):
if sign_method_type == "md5":
sign_method_handle = self.get_md5_sign
elif sign_method_type == "hmac_sha256":
sign_method_handle = self.get_hmac_sha256_sign
else:
raise ValueError("Unsupported sign method: " + sign_method_type)
body = json.dumps(body_object)
timestamp = str(int(time.time()))
parameters[APPID_KEY] = "your_app_id"
parameters[TIMESTAMP_KEY] = timestamp
parameters[NONCE_KEY] = "your_nonce"
parameters[SIGN_METHOD_KEY] = sign_method_type
parameters[SIGN_TYPE_KEY] = "MD5"
new_sign = self.generate_sign(body, parameters, app_secret, sign_method_handle)
return new_sign
def get_support_sign_method(self):
return self.support_sign_method
def set_support_sign_method(self, support_sign_method):
self.support_sign_method = support_sign_method
Swift示例
import Foundation
import CommonCrypto
class SignUtils {
private var support_sign_method = "md5,hmac_sha256"
private func getMd5Sign(_ paramStr: String, _ secret: String) -> String {
let data = (paramStr + secret).data(using: .utf8)!
var result = [UInt8](repeating: 0, count: Int(CC_MD5_DIGEST_LENGTH))
data.withUnsafeBytes {
_ = CC_MD5($0.baseAddress, CC_LONG(data.count), &result)
}
return result.map { String(format: "%02x", $0) }.joined()
}
private func getHmacSha256Sign(_ paramStr: String, _ secret: String) -> String {
let data = paramStr.data(using: .utf8)!
let key = secret.data(using: .utf8)!
var result = [UInt8](repeating: 0, count: Int(CC_SHA256_DIGEST_LENGTH))
CCHmac(CCHmacAlgorithm(kCCHmacAlgSHA256), key.bytes, key.count, data.bytes, data.count, &result)
return result.map { String(format: "%02x", $0) }.joined()
}
private func generateSign(_ body: String, _ parameters: [String: String], _ appSecret: String, _ signMethodHandle: (String, String) -> String) -> String {
let keys = parameters.keys.sorted()
var paramStr = body.isEmpty ? "" : "body=\(body)&"
for key in keys {
paramStr += "\(key)=\(parameters[key]!)&"
}
paramStr += appSecret
return signMethodHandle(paramStr, appSecret)
}
func sign(_ parameters: [String: String], _ signMethodType: String, _ appSecret: String, _ bodyObject: Any?) -> String {
var signMethodHandle: ((String, String) -> String)
switch signMethodType {
case "md5":
signMethodHandle = getMd5Sign
case "hmac_sha256":
signMethodHandle = getHmacSha256Sign
default:
fatalError("Unsupported sign method: \(signMethodType)")
}
let body = bodyObject != nil ? String(data: try! JSONSerialization.data(withJSONObject: bodyObject!), encoding: .utf8)! : ""
let timestamp = Int(Date().timeIntervalSince1970)
var parameters = parameters
parameters["appid"] = "your_app_id"
parameters["timestamp"] = "\(timestamp)"
parameters["nonce"] = "your_nonce"
parameters["sign_method"] = signMethodType
parameters["sign_type"] = "MD5"
return generateSign(body, parameters, appSecret, signMethodHandle)
}
func getSupportSignMethod() -> String {
return support_sign_method
}
func setSupportSignMethod(_ supportSignMethod: String) {
support_sign_method = supportSignMethod
}
}
Kotlin示例
import java.security.MessageDigest
import javax.crypto.Mac
import javax.crypto.spec.SecretKeySpec
class SignUtils {
private var support_sign_method = "md5,hmac_sha256"
private fun getMd5Sign(paramStr: String, secret: String): String {
val data = "$paramStr$secret".toByteArray(Charsets.UTF_8)
val result = MessageDigest.getInstance("MD5").digest(data)
return result.joinToString("") { "%02x".format(it) }
}
private fun getHmacSha256Sign(paramStr: String, secret: String): String {
val data = paramStr.toByteArray(Charsets.UTF_8)
val key = SecretKeySpec(secret.toByteArray(Charsets.UTF_8), "HmacSHA256")
val hmac = Mac.getInstance("HmacSHA256").apply { init(key) }
val result = hmac.doFinal(data)
return result.joinToString("") { "%02x".format(it) }
}
private fun generateSign(body: String, parameters: Map<String, String>, appSecret: String, signMethodHandle: (String, String) -> String): String {
val keys = parameters.keys.sorted()
var paramStr = if (body.isEmpty()) "" else "body=$body&"
for (key in keys) {
paramStr += "$key=${parameters[key]}&"
}
paramStr += appSecret
return signMethodHandle(paramStr, appSecret)
}
fun sign(parameters: Map<String, String>, signMethodType: String, appSecret: String, bodyObject: Any?): String {
val signMethodHandle: (String, String) -> String = when (signMethodType) {
"md5" -> this::getMd5Sign
"hmac_sha256" -> this::getHmacSha256Sign
else -> throw IllegalArgumentException("Unsupported sign method: $signMethodType")
}
val body = bodyObject?.let { body ->
@Suppress("UNCHECKED_CAST")
val json = jacksonObjectMapper().writeValueAsString(body as Map<String, Any>)
String(json.toByteArray(Charsets.UTF_8))
} ?: ""
val timestamp = System.currentTimeMillis() / 1000
val parameters = parameters.toMutableMap().apply {
this["appid"] = "your_app_id"
this["timestamp"] = timestamp.toString()
this["nonce"] = "your_nonce"
this["sign_method"] = signMethodType
this["sign_type"] = "MD5"
}
return generateSign(body, parameters, appSecret, signMethodHandle)
}
fun getSupportSignMethod(): String {
return support_sign_method
}
fun setSupportSignMethod(supportSignMethod: String) {
support_sign_method = supportSignMethod
}
}
调用示例
- 设置参数值
参数 | 名称 |
---|---|
appid | AppKey |
sign_method | 签名算法, 目前支持的签名算法有两种:MD5(sign_method=md5),HMAC_SHA256(sign_method=hmac-sha256) |
timestamp | 时间戳,默认2分钟内有效 |
sign | 签名内容 |
按ASCII顺序排序
```
签名示例:
签名方式:hmac-sha256
明文:appid=PltO3gTJ×tamp=1685590016123
密钥(appsecret):627ab1bb0c3095e75b4a864edb060db499fa2863
签名结果(sign):da42d239f9114218d3ac3cd221b9b6376ccdbbe6ef4abb8c07f9236ad9d3c151
方法:new HmacUtils(HmacAlgorithms.HMAC_SHA_256, 密钥).hmacHex(明文)签名方式:md5
明文:appid=PltO3gTJ&sign_method=md5×tamp=1685597220000
密钥(appsecret):627ab1bb0c3095e75b4a864edb060db499fa2863
签名结果(sign):8c9be0645d8afc6406fd32a319a80c0c
方法: DigestUtils.md5DigestAsHex((明文 + 密钥).getBytes())
```
注意:签名结果应是小写