php+java加密对接算法
本文为joshua317原创文章,转载请注明:转载自joshua317博客 https://www.joshua317.com/article/322
请求示例:
Sign生成方式
- 拼接参数:signType=?&appKey=?&appSecret=?&nonce=?×tamp=? 如:signType=HmacSHA256&appKey=demo&appSecret=demo&nonce=12345×tamp=1663998514。
注意:需要将appSecret设置入签名拼接参数
- 字符串转大写 SIGNTYPE=HMACSHA256&APPKEY=DEMO&APPSECRET=DEMO&NONCE=12345&TIMESTAMP=1663998514
- 字符串中的字符升序排序前:SIGNTYPE=HMACSHA256&APPKEY=DEMO&APPSECRET=DEMO&NONCE=12345&TIMESTAMP=1663998514
- 按字符排序:&&&&111223344555666899=====AAAAACCCDDEEEEEEEEGHHIIKMMMMMNNNOOOPPPPPPRSSSSTTTTYY
- HmacMD5或者HmacSHA256加密生成签名,利用秘钥和所需的加密签名算法对上述字符串进行加密成密文,作为sign签名字符串一并发送到请求接口;
- 将获取到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;
}
本文为joshua317原创文章,转载请注明:转载自joshua317博客 https://www.joshua317.com/article/322