taobao_model
tanghc 5 years ago
parent eaecc720b3
commit bcd762c309
  1. 2
      sop-admin/sop-admin-server/src/main/resources/META-INF/sop-admin.properties
  2. 3
      sop-common/sop-bridge-gateway/src/main/java/com/gitee/sop/bridge/SopGatewayAutoConfiguration.java
  3. 3
      sop-common/sop-bridge-zuul/src/main/java/com/gitee/sop/bridge/SopGatewayAutoConfiguration.java
  4. 10
      sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/validate/taobao/Constants.java
  5. 97
      sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/validate/taobao/TaobaoSignature.java
  6. 50
      sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/validate/taobao/TaobaoSigner.java
  7. 3
      sop-example/sop-story/sop-story-web/src/main/java/com/gitee/sop/storyweb/config/OpenServiceConfig.java
  8. 45
      sop-example/sop-story/sop-story-web/src/main/java/com/gitee/sop/storyweb/controller/TaobaoController.java
  9. 91
      sop-test/src/test/java/com/gitee/sop/test/TaobaoClientPostTest.java

@ -7,7 +7,7 @@ spring.application.name=sop-admin
# session过期时间,分钟 # session过期时间,分钟
admin.access-token.timeout-minutes=30 admin.access-token.timeout-minutes=30
# 签名方式,rsa:支付宝开放平台签名方式,md5:淘宝开放平台签名方式 # 签名方式,rsa:支付宝开放平台签名方式,md5:淘宝开放平台签名方式
sop.sign-type=rsa sop.sign-type=md5
# nacos配置 # nacos配置
nacos.config.server-addr=${nacos.url} nacos.config.server-addr=${nacos.url}

@ -2,6 +2,7 @@ package com.gitee.sop.bridge;
import com.gitee.sop.gatewaycommon.config.BaseGatewayAutoConfiguration; import com.gitee.sop.gatewaycommon.config.BaseGatewayAutoConfiguration;
import com.gitee.sop.gatewaycommon.gateway.configuration.AlipayGatewayConfiguration; import com.gitee.sop.gatewaycommon.gateway.configuration.AlipayGatewayConfiguration;
import com.gitee.sop.gatewaycommon.gateway.configuration.TaobaoGatewayConfiguration;
import org.springframework.boot.autoconfigure.AutoConfigureBefore; import org.springframework.boot.autoconfigure.AutoConfigureBefore;
import org.springframework.cloud.netflix.ribbon.RibbonAutoConfiguration; import org.springframework.cloud.netflix.ribbon.RibbonAutoConfiguration;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
@ -12,7 +13,7 @@ import org.springframework.context.annotation.Import;
* @author tanghc * @author tanghc
*/ */
@Configuration @Configuration
@Import(AlipayGatewayConfiguration.class) @Import(TaobaoGatewayConfiguration.class)
@AutoConfigureBefore(RibbonAutoConfiguration.class) @AutoConfigureBefore(RibbonAutoConfiguration.class)
public class SopGatewayAutoConfiguration extends BaseGatewayAutoConfiguration { public class SopGatewayAutoConfiguration extends BaseGatewayAutoConfiguration {
} }

@ -2,6 +2,7 @@ package com.gitee.sop.bridge;
import com.gitee.sop.gatewaycommon.config.BaseGatewayAutoConfiguration; import com.gitee.sop.gatewaycommon.config.BaseGatewayAutoConfiguration;
import com.gitee.sop.gatewaycommon.zuul.configuration.AlipayZuulConfiguration; import com.gitee.sop.gatewaycommon.zuul.configuration.AlipayZuulConfiguration;
import com.gitee.sop.gatewaycommon.zuul.configuration.TaobaoZuulConfiguration;
import org.springframework.boot.autoconfigure.AutoConfigureBefore; import org.springframework.boot.autoconfigure.AutoConfigureBefore;
import org.springframework.boot.autoconfigure.web.servlet.error.ErrorMvcAutoConfiguration; import org.springframework.boot.autoconfigure.web.servlet.error.ErrorMvcAutoConfiguration;
import org.springframework.cloud.netflix.zuul.EnableZuulProxy; import org.springframework.cloud.netflix.zuul.EnableZuulProxy;
@ -14,7 +15,7 @@ import org.springframework.context.annotation.Import;
*/ */
@Configuration @Configuration
@EnableZuulProxy @EnableZuulProxy
@Import(AlipayZuulConfiguration.class) @Import(TaobaoZuulConfiguration.class)
// 在ErrorMvcAutoConfiguration之前加载 // 在ErrorMvcAutoConfiguration之前加载
// 如果不加会出现basicErrorController和zuulErrorController冲突 // 如果不加会出现basicErrorController和zuulErrorController冲突
// zuulErrorController是SOP中的,提前加载后basicErrorController就不会加载 // zuulErrorController是SOP中的,提前加载后basicErrorController就不会加载

@ -0,0 +1,10 @@
package com.gitee.sop.gatewaycommon.validate.taobao;
/**
* @author tanghc
*/
public class Constants {
public static final String SIGN_METHOD_HMAC = "hmac";
public static final String SIGN_METHOD_MD5 = "md5";
public static final String CHARSET_UTF8 = "UTF-8";
}

@ -0,0 +1,97 @@
package com.gitee.sop.gatewaycommon.validate.taobao;
import com.gitee.sop.gatewaycommon.validate.alipay.StringUtils;
import javax.crypto.Mac;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.security.GeneralSecurityException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Arrays;
import java.util.Map;
/**
* @author tanghc
*/
public class TaobaoSignature {
public static String signTopRequest(Map<String, String> params, String secret, String signMethod) throws IOException {
String content = getSignContent(params);
return doSign(content, secret, signMethod);
}
public static String getSignContent(Map<String, ?> params) {
// 第一步:检查参数是否已经排序
String[] keys = params.keySet().toArray(new String[0]);
Arrays.sort(keys);
// 第二步:把所有参数名和参数值串在一起
StringBuilder query = new StringBuilder();
for (String key : keys) {
Object val = params.get(key);
String value = val == null ? null : val.toString();
if (StringUtils.areNotEmpty(key, value)) {
query.append(key).append(value);
}
}
return query.toString();
}
public static String doSign(String content,String secret, String signMethod) throws IOException {
// 第三步:使用MD5/HMAC加密
byte[] bytes;
if (Constants.SIGN_METHOD_HMAC.equals(signMethod)) {
bytes = encryptHMAC(content, secret);
} else {
bytes = encryptMD5(secret + content + secret);
}
// 第四步:把二进制转化为大写的十六进制(正确签名应该为32大写字符串,此方法需要时使用)
return byte2hex(bytes);
}
public static byte[] encryptHMAC(String data, String secret) throws IOException {
byte[] bytes = null;
try {
SecretKey secretKey = new SecretKeySpec(secret.getBytes(StandardCharsets.UTF_8), "HmacMD5");
Mac mac = Mac.getInstance(secretKey.getAlgorithm());
mac.init(secretKey);
bytes = mac.doFinal(data.getBytes(StandardCharsets.UTF_8));
} catch (GeneralSecurityException gse) {
throw new IOException(gse.toString());
}
return bytes;
}
public static byte[] encryptMD5(String data) throws IOException {
return encryptMD5(data.getBytes(StandardCharsets.UTF_8));
}
public static byte[] encryptMD5(byte[] data) {
try {
MessageDigest digest = MessageDigest.getInstance(Constants.SIGN_METHOD_MD5);
return digest.digest(data);
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
return null;
}
public static String byte2hex(byte[] bytes) {
StringBuilder sign = new StringBuilder();
for (int i = 0; i < bytes.length; i++) {
String hex = Integer.toHexString(bytes[i] & 0xFF);
if (hex.length() == 1) {
sign.append("0");
}
sign.append(hex.toUpperCase());
}
return sign.toString();
}
}

@ -5,62 +5,30 @@ import com.gitee.sop.gatewaycommon.bean.SopConstants;
import com.gitee.sop.gatewaycommon.message.ErrorEnum; import com.gitee.sop.gatewaycommon.message.ErrorEnum;
import com.gitee.sop.gatewaycommon.param.ApiParam; import com.gitee.sop.gatewaycommon.param.ApiParam;
import com.gitee.sop.gatewaycommon.validate.AbstractSigner; import com.gitee.sop.gatewaycommon.validate.AbstractSigner;
import com.gitee.sop.gatewaycommon.validate.SignConfig; import lombok.extern.slf4j.Slf4j;
import com.gitee.sop.gatewaycommon.validate.SignEncipher;
import com.gitee.sop.gatewaycommon.validate.SignEncipherHMAC_MD5;
import com.gitee.sop.gatewaycommon.validate.SignEncipherMD5;
import org.apache.commons.lang3.StringUtils;
import java.util.ArrayList; import java.io.IOException;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
/** /**
* 淘宝开放平台签名验证实现http://open.taobao.com/doc.htm?docId=101617&docType=1 * 淘宝开放平台签名验证实现http://open.taobao.com/doc.htm?docId=101617&docType=1
* *
* @author tanghc * @author tanghc
*/ */
@Slf4j
public class TaobaoSigner extends AbstractSigner { public class TaobaoSigner extends AbstractSigner {
private Map<String, SignEncipher> signEncipherMap = new HashMap<>();
public TaobaoSigner() {
signEncipherMap.put("md5", new SignEncipherMD5());
signEncipherMap.put("hmac", new SignEncipherHMAC_MD5());
}
@Override @Override
public String buildServerSign(ApiParam param, String secret) { public String buildServerSign(ApiParam param, String secret) {
String signMethod = param.fetchSignMethod(); String signMethod = param.fetchSignMethod();
if (signMethod == null) { if (signMethod == null) {
signMethod = SopConstants.DEFAULT_SIGN_METHOD; signMethod = SopConstants.DEFAULT_SIGN_METHOD;
} }
SignEncipher signEncipher = signEncipherMap.get(signMethod); String signContent = TaobaoSignature.getSignContent(param);
if (signEncipher == null) { try {
throw ErrorEnum.ISV_INVALID_SIGNATURE_TYPE.getErrorMeta().getException(signMethod); return TaobaoSignature.doSign(signContent, secret, signMethod);
} catch (IOException e) {
log.error("淘宝签名失败, param:{}", param, e);
throw ErrorEnum.ISP_UNKNOWN_ERROR.getErrorMeta().getException();
} }
// 第一步:参数排序
Set<String> keySet = param.keySet();
List<String> paramNames = new ArrayList<>(keySet);
Collections.sort(paramNames);
// 第二步:把所有参数名和参数值串在一起
StringBuilder paramNameValue = new StringBuilder();
for (String paramName : paramNames) {
String val = SignConfig.wrapVal(param.get(paramName));
paramNameValue.append(paramName).append(val);
}
// 第三步:使用MD5/HMAC加密
String source = paramNameValue.toString();
byte[] bytes = signEncipher.encrypt(source, secret);
// 第四步:把二进制转化为大写的十六进制
return byte2hex(bytes).toUpperCase();
} }
} }

@ -2,6 +2,7 @@ package com.gitee.sop.storyweb.config;
import com.gitee.sop.servercommon.bean.ServiceConfig; import com.gitee.sop.servercommon.bean.ServiceConfig;
import com.gitee.sop.servercommon.configuration.AlipayServiceConfiguration; import com.gitee.sop.servercommon.configuration.AlipayServiceConfiguration;
import com.gitee.sop.servercommon.configuration.TaobaoServiceConfiguration;
import com.gitee.sop.servercommon.swagger.SwaggerSupport; import com.gitee.sop.servercommon.swagger.SwaggerSupport;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import springfox.documentation.swagger2.annotations.EnableSwagger2; import springfox.documentation.swagger2.annotations.EnableSwagger2;
@ -11,7 +12,7 @@ import springfox.documentation.swagger2.annotations.EnableSwagger2;
* @author tanghc * @author tanghc
*/ */
@Configuration @Configuration
public class OpenServiceConfig extends AlipayServiceConfiguration { public class OpenServiceConfig extends TaobaoServiceConfiguration {
static { static {
ServiceConfig.getInstance().getI18nModules().add("i18n/isp/goods_error"); ServiceConfig.getInstance().getI18nModules().add("i18n/isp/goods_error");

@ -0,0 +1,45 @@
package com.gitee.sop.storyweb.controller;
import com.gitee.sop.servercommon.annotation.ApiAbility;
import com.gitee.sop.storyweb.controller.param.StoryParam;
import com.gitee.sop.storyweb.controller.result.StoryResult;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
/**
*
* @author tanghc
*/
@RestController
@Slf4j
// 注解放在这里,表示类中的方法都具备接口开放能力
@ApiAbility
public class TaobaoController {
@RequestMapping("/order/get")
public StoryResult get(@RequestParam String data) {
StoryResult story = new StoryResult();
story.setId(1L);
story.setName(data);
return story;
}
@RequestMapping("/order/save")
public StoryResult save(StoryParam param) {
StoryResult story = new StoryResult();
story.setId((long) param.getId());
story.setName(param.getName());
return story;
}
@RequestMapping("/order/json")
public StoryResult json(@RequestBody StoryParam param) {
StoryResult story = new StoryResult();
story.setId((long) param.getId());
story.setName(param.getName());
return story;
}
}

@ -15,40 +15,57 @@ public class TaobaoClientPostTest extends TestBase {
String url = "http://localhost:8081"; String url = "http://localhost:8081";
String appId = "taobao_test"; String appId = "20190331562013861008375808";
// 淘宝私钥 // 淘宝私钥
String secret = "G9w0BAQEFAAOCAQ8AMIIBCgKCA"; String secret = "29864b93427447f5ac6c44df746f84ef";
/* @Test
参数名称 参数类型 是否必须 参数描述 public void testGet() throws Exception {
method String API接口名称
app_key String TOP分配给应用的AppKey这里要注意正式环境和沙箱环境的AppKey是不同的包括AppSecret使用时要注意区分进入开放平台控制台应用管理-概览 应用管理-沙箱环境管理可分别查看正式环境及沙箱环境的AppKeyAppSecret // 公共请求参数
session String 用户登录授权成功后TOP颁发给应用的授权信息详细介绍请点击这里当此API文档的标签上注明需要授权则此参数必传不需要授权则此参数不需要传可选授权则此参数为可选 Map<String, String> params = new HashMap<String, String>();
timestamp String 时间戳格式为yyyy-MM-dd HH:mm:ss时区为GMT+8例如2016-01-01 12:00:00淘宝API服务端允许客户端请求最大时间误差为10分钟 params.put("app_key", appId);
format String 响应格式默认为xml格式可选值xmljson params.put("method", "order.get");
v String API协议版本可选值2.0 params.put("format", "json");
partner_id String 合作伙伴身份标识 params.put("sign_method", "md5");
target_app_key String 被调用的目标AppKey仅当被调用的API为第三方ISV提供时有效 params.put("timestamp", new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));
simplify Boolean 是否采用精简JSON返回格式仅当format=json时有效默认值为false params.put("v", "");
sign_method String 签名的摘要算法可选值为hmacmd5
sign String API输入参数签名结果签名算法参照下面的介绍 // 业务参数
*/ params.put("data", "get11111");
System.out.println("----------- 请求信息 -----------");
System.out.println("请求参数:" + buildParamQuery(params));
System.out.println("商户秘钥:" + secret);
String content = TaobaoSignature.getSignContent(params);
System.out.println("待签名内容:" + content);
String sign = TaobaoSignature.doSign(content, secret, "md5");
System.out.println("签名(sign):" + sign);
params.put("sign", sign);
System.out.println("----------- 返回结果 -----------");
String responseData = get(url, params);// 发送请求
System.out.println(responseData);
}
@Test @Test
public void testPost() throws Exception { public void testPost() throws Exception {
// 公共请求参数 // 公共请求参数
Map<String, String> params = new HashMap<String, String>(); Map<String, String> params = new HashMap<String, String>();
params.put("app_key", appId); params.put("app_key", appId);
params.put("method", "alipay.story.get"); params.put("method", "order.save");
params.put("format", "json"); params.put("format", "json");
params.put("sign_method", "md5"); params.put("sign_method", "md5");
params.put("timestamp", new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date())); params.put("timestamp", new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));
params.put("v", ""); params.put("v", "");
// 业务参数 // 业务参数
// 参数接收见:com.gitee.sop.storyweb.controller.AlipayController2
params.put("id", "1"); params.put("id", "1");
params.put("name", "葫芦娃"); params.put("name", "葫芦娃post");
// bizContent.put("name", "葫芦娃1234567890葫芦娃1234567890"); // 超出长度
System.out.println("----------- 请求信息 -----------"); System.out.println("----------- 请求信息 -----------");
System.out.println("请求参数:" + buildParamQuery(params)); System.out.println("请求参数:" + buildParamQuery(params));
@ -65,4 +82,38 @@ sign String 是 API输入参数签名结果,签名算法参照下面的介绍
System.out.println(responseData); System.out.println(responseData);
} }
@Test
public void testJson() throws Exception {
// 公共请求参数
Map<String, String> params = new HashMap<String, String>();
params.put("app_key", appId);
params.put("method", "order.json");
params.put("format", "json");
params.put("sign_method", "md5");
params.put("timestamp", new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));
params.put("v", "");
// 业务参数
// 参数接收见:com.gitee.sop.storyweb.controller.AlipayController2
params.put("id", "1");
params.put("name", "葫芦娃json..");
System.out.println("----------- 请求信息 -----------");
System.out.println("请求参数:" + buildParamQuery(params));
System.out.println("商户秘钥:" + secret);
String content = TaobaoSignature.getSignContent(params);
System.out.println("待签名内容:" + content);
String sign = TaobaoSignature.doSign(content, secret, "md5");
System.out.println("签名(sign):" + sign);
params.put("sign", sign);
System.out.println("----------- 返回结果 -----------");
String responseData = postJson(url, params);// 发送请求
System.out.println(responseData);
}
} }

Loading…
Cancel
Save