php+java加密对接算法

请求示例:

{
  "appKey": "demo",
  "nonce": "12345",
  "sign": "04a8ba0a19ffc491716131a542729a9c250d84ce4211889a15f920ce974cf23b",
  "signType": "HmacSHA256",
  "timestamp": 1663998514
}
JSON

Sign生成方式

  1. 拼接参数:signType=?&appKey=?&appSecret=?&nonce=?&timestamp=? 如:signType=HmacSHA256&appKey=demo&appSecret=demo&nonce=12345&timestamp=1663998514。

注意:需要将appSecret设置入签名拼接参数

  1. 字符串转大写 SIGNTYPE=HMACSHA256&APPKEY=DEMO&APPSECRET=DEMO&NONCE=12345&TIMESTAMP=1663998514
  2. 字符串中的字符升序排序前:SIGNTYPE=HMACSHA256&APPKEY=DEMO&APPSECRET=DEMO&NONCE=12345&TIMESTAMP=1663998514
  3. 按字符排序:&&&&111223344555666899=====AAAAACCCDDEEEEEEEEGHHIIKMMMMMNNNOOOPPPPPPRSSSSTTTTYY
  4. HmacMD5或者HmacSHA256加密生成签名,利用秘钥和所需的加密签名算法对上述字符串进行加密成密文,作为sign签名字符串一并发送到请求接口;
  5. 将获取到sign字符串设置进参数即可。

php代码

        $nonce =  rand(100000, 999999);//随机数,可以根据需要自己写函数实现
        $timestamp = time();
        $appKey = 'demo';
        $appSecret = 'demo';
        $params = [
            'appKey' => $appKey,//$appKey,
            'nonce' => $nonce,//$nonce,
            'signType' => 'HmacSHA256',
            'timestamp' => $timestamp,//$timestamp
        ];
        $tempParams = $params;
        $tempParams['appSecret'] = $appSecret;
        $httpBuidParams = http_build_query($tempParams);
        $httpBuidParams = Str::upper($httpBuidParams);

        $charArray = str_split($httpBuidParams);
        // 对字符数组进行排序
        sort($charArray);
        // 将排序后的字符数组转换回字符串
        $data = implode("", $charArray);

        $sign = hash_hmac("sha256", $data, $appSecret);
        echo $sign;
        

java代码

package com.xxx.utils;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.bean.copier.CopyOptions;
import com.ssc.common.security.annotation.SignIgnore;
import org.apache.commons.codec.digest.HmacUtils;
import org.apache.commons.lang3.StringUtils;

import java.lang.annotation.Annotation;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;

/**
 * 签名入参工具类
 *
 */

public class SignUtils {

    private static final ConcurrentMap<String, String[]> IGNORE_FIELD_CACHE = new ConcurrentHashMap<>();

    /**
     * 签名
     *
     * @param dto           原始参数
     * @param signType      签名算法
     * @param securityKey   加密key
     * @return 签名
     */
    public static String sign(Object dto, String signType, String securityKey) {
        if (Objects.isNull(dto)) {
            return "";
        }

        String[] ignoreFieldArray = getFieldNameByAnn(dto, com.ssc.common.security.annotation.SignIgnore.class);

        HmacUtils util = new HmacUtils(signType, securityKey);
        String sortStr = sortChar(buildPlainText(dto, ignoreFieldArray).toUpperCase());

        return util.hmacHex(sortStr);
    }

    /**
     * 校验签名
     *
     * @param dto           原始参数
     * @param signType      签名算法
     * @param securityKey   加密key
     * @param sign          签名串
     * @return  验证是否通过
     */
    public static boolean baseVerify(Object dto, String signType, String securityKey, String sign) {
        if (Objects.isNull(dto)) {
            return false;
        }

        String[] ignoreFieldArray = getFieldNameByAnn(dto, SignIgnore.class);

        HmacUtils util = new HmacUtils(signType, securityKey);
        String sortStr = sortChar(buildPlainText(dto, ignoreFieldArray).toUpperCase());

        String signVerify = util.hmacHex(sortStr);
        return Objects.equals(signVerify, sign);
    }

    public static Map<String, Object> obj2Map(Object dto, String... ignoreProperties) {
        CopyOptions options = new CopyOptions();
        options.setIgnoreProperties(ignoreProperties);
        return  BeanUtil.beanToMap(dto, new HashMap<>(), options);
    }

    public static String buildPlainText(Object dto, String... ignoreProperties ) {
        if (Objects.isNull(dto)) {
            return "";
        }

        Map<String, Object> map = obj2Map(dto, ignoreProperties);
        StringBuilder sb = new StringBuilder();
        for (Map.Entry<String, Object> entry : map.entrySet()) {
            sb.append(entry.getKey())
            .append("=")
            .append(entry.getValue())
            .append("&");
        }
        return sb.substring(0, sb.length() - 1);
    }

    public static String sortChar(String plainText) {
        if (StringUtils.isBlank(plainText)) {
            return "";
            }

            char[] charArray = plainText.toCharArray();
            Arrays.sort(charArray);

            return new String(charArray);
            }

            private String[] getFieldNameByAnn(Object object, Class<? extends Annotation> ann) {
            assert object != null;
            String[] ignoreFieldList = IGNORE_FIELD_CACHE.get(object.getClass().getName());
            if (Objects.isNull(ignoreFieldList)) {
            List<String> ignoreFields = new ArrayList<>();
            Class<?> entityType = object.getClass();
            while (entityType != null) {
            Field[] declaredFields = entityType.getDeclaredFields();
            for (Field declaredField : declaredFields) {
            if (declaredField.isAnnotationPresent(ann)) {
            ignoreFields.add(declaredField.getName());
            }
            }
            entityType = entityType.getSuperclass();

            }
            ignoreFieldList = ignoreFields.toArray(new String[0]);
            IGNORE_FIELD_CACHE.put(object.getClass().getName(), ignoreFieldList);
            }
            return ignoreFieldList;
            }


            @Target({ElementType.FIELD})
            @Retention(RetentionPolicy.RUNTIME)
            public @interface SignIgnore {

            }
            }
ackage com.xxx.dto;

import com.fasterxml.jackson.annotation.JsonIgnore;
import com.xxx.utils.SignUtils.SignIgnore;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.experimental.Accessors;

import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
import java.io.Serializable;

/**
 * 签名DTO
 *
 */

@Data
public class BaseSignDTO implements Serializable {

    @SignIgnore
    @ApiModelProperty(value = "签名", required = true, example = "abcdefg")
    private String sign;

    @ApiModelProperty(value = "时间戳", required = true, example = "1663998514")
    private Long timestamp;

    @ApiModelProperty(value = "签名方式", required = true, example = "HmacSHA256")
    private String signType;

    @ApiModelProperty(value = "随机数", required = true, example = "12345")
    private String nonce;

    @ApiModelProperty(value = "应用Key", required = true)
    private String appKey;

    @ApiModelProperty(value = "应用密匙", hidden = true)
    private String appSecret;
}

 

posted @ 2024-03-08 15:20  joshua317  阅读(48)  评论(0编辑  收藏  举报