From c25e59a991c882a0d08023fa3bcb95df09f5a1a9 Mon Sep 17 00:00:00 2001 From: nishengli Date: Fri, 15 Sep 2023 16:52:15 +0800 Subject: [PATCH] init --- .gitignore | 4 +- pom.xml | 116 ++++++++ .../tsl3060/open/extend/core/ApiClient.java | 265 ++++++++++++++++++ .../tsl3060/open/extend/core/ApiRequest.java | 107 +++++++ .../tsl3060/open/extend/core/ApiResponse.java | 125 +++++++++ .../com/tsl3060/open/extend/core/Config.java | 113 ++++++++ .../tsl3060/open/extend/core/IApiRequest.java | 11 + .../open/extend/core/INotifyListener.java | 14 + .../extend/core/NotifyAnswerResponse.java | 84 ++++++ .../open/extend/core/NotifyRequest.java | 150 ++++++++++ .../extend/core/exception/ApiException.java | 11 + .../core/exception/BadResourceException.java | 21 ++ .../extend/core/notify/CarbonOrderNotify.java | 110 ++++++++ .../core/notify/CarbonOrderNotifyAnswer.java | 20 ++ .../open/extend/core/notify/IAnswer.java | 4 + .../core/payload/CarbonOrderPayload.java | 113 ++++++++ .../extend/core/payload/RequestPayload.java | 13 + .../extend/core/payload/UserLoginPayload.java | 35 +++ .../payload/UserRegisterRequestPayload.java | 40 +++ .../core/payload/WalletQueryPayload.java | 22 ++ .../extend/core/response/CaptchaResponse.java | 31 ++ .../core/response/CarbonOrderResponse.java | 52 ++++ .../extend/core/response/SmsSendResponse.java | 44 +++ .../core/response/UserLoginResponse.java | 35 +++ .../core/response/UserRegisterResponse.java | 42 +++ .../core/response/WalletQueryResponse.java | 32 +++ .../extend/core/router/INotifyRouter.java | 10 + .../extend/core/router/NotifyMapRouter.java | 30 ++ .../notify/CarbonOrderNotifyRouter.java | 29 ++ .../open/extend/core/secure/ISecure.java | 47 ++++ .../open/extend/core/secure/RSASecure.java | 220 +++++++++++++++ .../open/extend/core/secure/SecureTool.java | 24 ++ src/main/resources/logback.xml | 76 +++++ 33 files changed, 2049 insertions(+), 1 deletion(-) create mode 100644 pom.xml create mode 100644 src/main/java/com/tsl3060/open/extend/core/ApiClient.java create mode 100644 src/main/java/com/tsl3060/open/extend/core/ApiRequest.java create mode 100644 src/main/java/com/tsl3060/open/extend/core/ApiResponse.java create mode 100644 src/main/java/com/tsl3060/open/extend/core/Config.java create mode 100644 src/main/java/com/tsl3060/open/extend/core/IApiRequest.java create mode 100644 src/main/java/com/tsl3060/open/extend/core/INotifyListener.java create mode 100644 src/main/java/com/tsl3060/open/extend/core/NotifyAnswerResponse.java create mode 100644 src/main/java/com/tsl3060/open/extend/core/NotifyRequest.java create mode 100644 src/main/java/com/tsl3060/open/extend/core/exception/ApiException.java create mode 100644 src/main/java/com/tsl3060/open/extend/core/exception/BadResourceException.java create mode 100644 src/main/java/com/tsl3060/open/extend/core/notify/CarbonOrderNotify.java create mode 100644 src/main/java/com/tsl3060/open/extend/core/notify/CarbonOrderNotifyAnswer.java create mode 100644 src/main/java/com/tsl3060/open/extend/core/notify/IAnswer.java create mode 100644 src/main/java/com/tsl3060/open/extend/core/payload/CarbonOrderPayload.java create mode 100644 src/main/java/com/tsl3060/open/extend/core/payload/RequestPayload.java create mode 100644 src/main/java/com/tsl3060/open/extend/core/payload/UserLoginPayload.java create mode 100644 src/main/java/com/tsl3060/open/extend/core/payload/UserRegisterRequestPayload.java create mode 100644 src/main/java/com/tsl3060/open/extend/core/payload/WalletQueryPayload.java create mode 100644 src/main/java/com/tsl3060/open/extend/core/response/CaptchaResponse.java create mode 100644 src/main/java/com/tsl3060/open/extend/core/response/CarbonOrderResponse.java create mode 100644 src/main/java/com/tsl3060/open/extend/core/response/SmsSendResponse.java create mode 100644 src/main/java/com/tsl3060/open/extend/core/response/UserLoginResponse.java create mode 100644 src/main/java/com/tsl3060/open/extend/core/response/UserRegisterResponse.java create mode 100644 src/main/java/com/tsl3060/open/extend/core/response/WalletQueryResponse.java create mode 100644 src/main/java/com/tsl3060/open/extend/core/router/INotifyRouter.java create mode 100644 src/main/java/com/tsl3060/open/extend/core/router/NotifyMapRouter.java create mode 100644 src/main/java/com/tsl3060/open/extend/core/router/notify/CarbonOrderNotifyRouter.java create mode 100644 src/main/java/com/tsl3060/open/extend/core/secure/ISecure.java create mode 100644 src/main/java/com/tsl3060/open/extend/core/secure/RSASecure.java create mode 100644 src/main/java/com/tsl3060/open/extend/core/secure/SecureTool.java create mode 100644 src/main/resources/logback.xml diff --git a/.gitignore b/.gitignore index 82416ef..8eac6d0 100644 --- a/.gitignore +++ b/.gitignore @@ -16,4 +16,6 @@ buildNumber.properties .project # JDT-specific (Eclipse Java Development Tools) .classpath - +.idea +src/test +*.iml diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..8584d66 --- /dev/null +++ b/pom.xml @@ -0,0 +1,116 @@ + + + 4.0.0 + + com.tsl3060.open.extend + OpenExtendDemo + 0.3.6-wanshun + + + 8 + 8 + + UTF-8 + 5.8.19 + + 1.4.7 + + + + + + cn.hutool + hutool-log + + + + + ch.qos.logback + logback-core + ${logback.version} + + + ch.qos.logback + logback-classic + ${logback.version} + + + org.bouncycastle + bcprov-jdk15to18 + 1.69 + + + org.junit.jupiter + junit-jupiter + RELEASE + test + + + com.squareup.okhttp3 + okhttp + 4.10.0 + + + com.alibaba.fastjson2 + fastjson2 + 2.0.33 + + + + + + + + + + + cn.hutool + hutool-bom + ${hutool.version} + pom + + import + + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + 3.11.0 + + 8 + 8 + UTF-8 + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/main/java/com/tsl3060/open/extend/core/ApiClient.java b/src/main/java/com/tsl3060/open/extend/core/ApiClient.java new file mode 100644 index 0000000..6535823 --- /dev/null +++ b/src/main/java/com/tsl3060/open/extend/core/ApiClient.java @@ -0,0 +1,265 @@ +package com.tsl3060.open.extend.core; + +import cn.hutool.core.date.DateTime; +import cn.hutool.core.util.StrUtil; +import cn.hutool.core.util.XmlUtil; +import cn.hutool.log.Log; +import cn.hutool.log.LogFactory; +import com.alibaba.fastjson2.JSON; +import com.alibaba.fastjson2.JSONObject; +import com.tsl3060.open.extend.core.exception.ApiException; +import com.tsl3060.open.extend.core.exception.BadResourceException; +import com.tsl3060.open.extend.core.notify.IAnswer; +import com.tsl3060.open.extend.core.router.INotifyRouter; +import com.tsl3060.open.extend.core.router.NotifyMapRouter; +import com.tsl3060.open.extend.core.secure.ISecure; +import com.tsl3060.open.extend.core.secure.SecureTool; +import okhttp3.*; +import org.w3c.dom.Document; + +import java.io.IOException; +import java.text.DateFormat; +import java.text.SimpleDateFormat; +import java.util.*; + +public class ApiClient { + + private static final int ERROR_OK = 1000; + + private static final int SUB_OK = 2000; + + /** + * 请求内容格式 + */ + private final MediaType contentType = MediaType.parse("application/json;charset=utf-8"); + + /** + * 返回内容格式 + */ + private final MediaType acceptType = MediaType.parse("application/json"); + + + private SecureTool secureTool; + + private Config config; + + + private final Log log = LogFactory.get(); + + private SecureTool getSecureTool() { + if (secureTool == null) { + secureTool = new SecureTool(getConfig()); + } + return secureTool; + } + + public Config getConfig() { + return config; + } + + public void setConfig(Config config) { + this.config = config; + } + + public T request(IApiRequest request, Class tClass) throws Exception { + ApiResponse apiResponse = this.request(request); + if (apiResponse == null) { + return null; + } + if (apiResponse.getErrCode() != ERROR_OK) { + //系统级错误 + throw new ApiException(apiResponse.getDescription() + "(" + apiResponse.getErrCode() + ")"); + } + if (apiResponse.getSubErr() != SUB_OK) { + //业务级错误 + throw new ApiException(apiResponse.getDescription() + "(" + apiResponse.getSubErr() + ")"); + } + Object o = apiResponse.getPayload(); + if (o != null) { + return JSON.to(tClass, o); + } + return null; + } + + /** + * 接口请求 + * + * @param request + * @return + */ + public ApiResponse request(IApiRequest request) throws Exception { + ApiRequest apiRequest = pack(request.path(), request); + ApiResponse apiResponse = this.request(apiRequest); + if (apiResponse == null) { + throw new Exception("返回结果为空"); + } + return apiResponse; + } + + /** + * 打包请求 + * + * @param path + * @param data + * @return + */ + private ApiRequest pack(String path, Object data) { + ApiRequest apiRequest = new ApiRequest(); + apiRequest.setPath(path); + apiRequest.setCharset(this.config.getCharset()); + DateFormat dateFormat = new SimpleDateFormat(this.config.getDataFormat()); + apiRequest.setTime(dateFormat.format(new Date())); + apiRequest.setSignType(this.config.getSignType()); + apiRequest.setPayload(data); + apiRequest.setAccessToken(""); + apiRequest.setAppId(this.config.getAppid()); + return apiRequest; + } + + /** + * 执行请求 + * + * @param apiRequest + * @return + */ + public ApiResponse request(ApiRequest apiRequest) { + ISecure iSecure = getSecureTool().getSecure(apiRequest.getSignType()); + String signStr = iSecure.requestSign(apiRequest); + apiRequest.setSign(signStr); + String fBody = JSON.toJSONString(apiRequest); + log.debug(">>> {}", fBody); + RequestBody requestBody = RequestBody.create(fBody, contentType); + /** + * 通讯主机地址 + */ + + String host = this.config.getHost(); + Request request = new Request.Builder() + .url(String.format("%s%s", host, apiRequest.getPath())) + .addHeader("ACCEPT", acceptType.toString()) + .post(requestBody) + .build(); + OkHttpClient.Builder builder = new OkHttpClient.Builder(); + OkHttpClient okHttpClient = builder.build(); + try { + Call call = okHttpClient.newCall(request); + try (Response response = call.execute()) { + ResponseBody responseBody = response.body(); + ApiResponse apiResponse = null; + if (responseBody != null) { + String bStr = responseBody.string(); + log.debug("<<< {}", bStr); + apiResponse = JSON.parseObject(bStr, ApiResponse.class); + } + if (apiResponse == null) { + return null; + } + //验证签名 + if (this.verifyResponse(apiResponse)) { + return apiResponse; + } + return null; + + } + } catch (IOException e) { + log.error(e); + } + return null; + } + + /** + * 验证反馈数据 + * + * @param response + * @return + */ + public boolean verifyResponse(ApiResponse response) { + ISecure iSecure = this.secureTool.getSecure(response.getSignType()); + if (iSecure == null) { + log.debug("没有找到签名类型 %s", response.getSignType()); + return false; + } + return iSecure.verifyResponse(response); + } + + + private INotifyListener notifyListener; + + public INotifyListener getNotifyListener() { + return notifyListener; + } + + public void setNotifyListener(INotifyListener notifyListener) { + this.notifyListener = notifyListener; + } + + + private NotifyMapRouter notifyMapRouter; + + /** + * 验证回调数据 + * + * @return + */ + public String notifyRun(String raw, String contentType, String accept) throws BadResourceException { + if (notifyMapRouter == null) { + notifyMapRouter = new NotifyMapRouter(this.notifyListener); + } + if (StrUtil.isEmpty(raw)) { + throw new BadResourceException("通知内容为空"); + } + + NotifyRequest notifyRequest; + if (contentType.contains("application/json")) { + notifyRequest = JSON.parseObject(raw, NotifyRequest.class); + } else if (contentType.contains("application/xml")) { + //XML格式 + Document document = XmlUtil.parseXml(raw); + //TODO 对XML解析 + throw new BadResourceException("暂不支持的格式"); + } else { + throw new BadResourceException("不支持的数据格式"); + } + //验证通知 + ISecure iSecure = getSecureTool().getSecure(notifyRequest.getSignType()); + if (!iSecure.verifyNotify(notifyRequest)) { + throw new BadResourceException("验签未通过"); + } + INotifyRouter notifyRouter = this.notifyMapRouter.getRouter(notifyRequest.getModule()); + if (notifyRouter == null) { + throw new BadResourceException("未知的通知"); + } + + //组装 + NotifyAnswerResponse notifyAnswerResponse = new NotifyAnswerResponse(); + notifyAnswerResponse.setAnswerId(notifyRequest.getNotifyId()); + notifyAnswerResponse.setAppId(this.config.getAppid()); + notifyAnswerResponse.setTime(DateTime.now().toString(this.config.getDataFormat())); + notifyAnswerResponse.setSignType(this.config.getSignType()); + notifyAnswerResponse.setCharset(this.config.getCharset()); + //通知 + try { + IAnswer o = notifyRouter.makeBody(notifyRequest); + notifyAnswerResponse.setPayload(o); + notifyAnswerResponse.setResult("ok"); + } catch (Exception e) { + e.printStackTrace(); + notifyAnswerResponse.setResult("fail"); + } + //生成签名 + ISecure makeSecure = getSecureTool().getSecure(notifyAnswerResponse.getSignType()); + String signStr = makeSecure.answerSign(notifyAnswerResponse); + notifyAnswerResponse.setSign(signStr); + //解析完成 + if (accept.contains("application/json")) { + + return JSON.toJSONString(notifyAnswerResponse); + } else if (accept.contains("application/xml")) { + //返回XML格式 + return ""; + } else { + //未知的格式 + return ""; + } + } +} diff --git a/src/main/java/com/tsl3060/open/extend/core/ApiRequest.java b/src/main/java/com/tsl3060/open/extend/core/ApiRequest.java new file mode 100644 index 0000000..bef8d6f --- /dev/null +++ b/src/main/java/com/tsl3060/open/extend/core/ApiRequest.java @@ -0,0 +1,107 @@ +package com.tsl3060.open.extend.core; + +import com.alibaba.fastjson2.annotation.JSONField; + +public class ApiRequest { + @JSONField(name = "app_id") + private String appId; + + /** + * 请求路径 + */ + private String path; + + /** + * 访问TOKEN + */ + @JSONField(name = "access_token") + private String accessToken; + + /** + * 签名数据 + */ + private String sign; + + /** + * 签名类型 + */ + @JSONField(name = "sign_type") + private String signType; + /** + * 数据编码 + */ + private String charset; + /** + * 时间 + */ + private String time; + /** + * 载体 + */ + private Object payload; + + + public String getAppId() { + return appId; + } + + public void setAppId(String appId) { + this.appId = appId; + } + + public String getPath() { + return path; + } + + public void setPath(String path) { + this.path = path; + } + + public String getAccessToken() { + return accessToken; + } + + public void setAccessToken(String accessToken) { + this.accessToken = accessToken; + } + + public String getSign() { + return sign; + } + + public void setSign(String sign) { + this.sign = sign; + } + + public String getSignType() { + return signType; + } + + public void setSignType(String signType) { + this.signType = signType; + } + + public String getCharset() { + return charset; + } + + public void setCharset(String charset) { + this.charset = charset; + } + + public String getTime() { + return time; + } + + public void setTime(String time) { + this.time = time; + } + + public Object getPayload() { + return payload; + } + + public void setPayload(Object payload) { + this.payload = payload; + } +} diff --git a/src/main/java/com/tsl3060/open/extend/core/ApiResponse.java b/src/main/java/com/tsl3060/open/extend/core/ApiResponse.java new file mode 100644 index 0000000..04c7b1f --- /dev/null +++ b/src/main/java/com/tsl3060/open/extend/core/ApiResponse.java @@ -0,0 +1,125 @@ +package com.tsl3060.open.extend.core; + +import com.alibaba.fastjson2.annotation.JSONField; + +public class ApiResponse { + private String time = ""; + private String sign = ""; + private String charset = "UTF-8"; + @JSONField(name = "response_id") + private String responseId = ""; + @JSONField(name = "err_code") + private int errCode; + @JSONField(name = "err_msg") + private String errMsg = ""; + @JSONField(name = "sub_err") + private int subErr; + @JSONField(name = "sub_msg") + private String subMsg = ""; + + @JSONField(name = "sign_type") + private String signType = ""; + + private Object payload; + + @JSONField(name = "open_id") + private String openId = ""; + + private String description=""; + public String getOpenId() { + return openId; + } + + public void setOpenId(String openId) { + this.openId = openId; + } + + public String getTime() { + return time; + } + + + public void setTime(String time) { + this.time = time; + } + + public String getSign() { + return sign; + } + + public void setSign(String sign) { + this.sign = sign; + } + + public String getCharset() { + return charset; + } + + public void setCharset(String charset) { + this.charset = charset; + } + + public String getResponseId() { + return responseId; + } + + public void setResponseId(String responseId) { + this.responseId = responseId; + } + + public int getErrCode() { + return errCode; + } + + public void setErrCode(int errCode) { + this.errCode = errCode; + } + + public String getErrMsg() { + return errMsg; + } + + public void setErrMsg(String errMsg) { + this.errMsg = errMsg; + } + + public int getSubErr() { + return subErr; + } + + public void setSubErr(int subErr) { + this.subErr = subErr; + } + + public String getSubMsg() { + return subMsg; + } + + public void setSubMsg(String subMsg) { + this.subMsg = subMsg; + } + + public String getSignType() { + return signType; + } + + public void setSignType(String signType) { + this.signType = signType; + } + + public Object getPayload() { + return payload; + } + + public void setPayload(Object payload) { + this.payload = payload; + } + + public String getDescription() { + return description; + } + + public void setDescription(String description) { + this.description = description; + } +} diff --git a/src/main/java/com/tsl3060/open/extend/core/Config.java b/src/main/java/com/tsl3060/open/extend/core/Config.java new file mode 100644 index 0000000..48663a6 --- /dev/null +++ b/src/main/java/com/tsl3060/open/extend/core/Config.java @@ -0,0 +1,113 @@ +package com.tsl3060.open.extend.core; + +import com.tsl3060.open.extend.core.secure.RSASecure; + +public class Config { + private String privateKey = ""; + private String publicKey = ""; + /** + * API 平台公钥 + */ + private String apiPublicKey = ""; + + private String appid = ""; + + private String signType = RSASecure.NAME; + + private String charset = "utf-8"; + + private String host = "https://open.tsl3060.com"; + private String dataFormat = "yyyy-MM-dd HH:mm:ss"; + + + public String getPrivateKey() { + return privateKey; + } + + public void setPrivateKey(String privateKey) { + this.privateKey = privateKey; + if (this.privateKey != null) { + this.privateKey = this.privateKey + .replace("\r", "") + .replace("\n", "") + .replace("-----BEGIN PRIVATE KEY-----", "") + .replace("-----END PRIVATE KEY-----", ""); + } + } + + public String getPublicKey() { + return publicKey; + } + + public void setPublicKey(String publicKey) { + this.publicKey = publicKey; + if (this.publicKey != null) { + this.publicKey = this.publicKey + .replace("\r", "") + .replace("\n", "") + .replace("-----BEGIN PUBLIC KEY-----", "") + .replace("-----END PUBLIC KEY-----", ""); + } + } + + public String getApiPublicKey() { + return apiPublicKey; + } + + public void setApiPublicKey(String apiPublicKey) { + this.apiPublicKey = apiPublicKey; + if (this.apiPublicKey != null) { + this.apiPublicKey = this.apiPublicKey + .replace("\r", "") + .replace("\n", "") + .replace("-----BEGIN PUBLIC KEY-----", "") + .replace("-----END PUBLIC KEY-----", ""); + + } + } + + public String getAppid() { + return appid; + } + + public void setAppid(String appid) { + this.appid = appid; + } + + public String getSignType() { + return signType; + } + + public void setSignType(String signType) { + this.signType = signType; + } + + public String getCharset() { + return charset; + } + + public void setCharset(String charset) { + this.charset = charset; + } + + public String getHost() { + return host; + } + + public void setHost(String host) { + this.host = host; + } + + public String getDataFormat() { + return dataFormat; + } + + public void setDataFormat(String dataFormat) { + this.dataFormat = dataFormat; + } + + + public void useSandBox() { + this.setHost("https://opendev.tsl3060.com"); + } +} diff --git a/src/main/java/com/tsl3060/open/extend/core/IApiRequest.java b/src/main/java/com/tsl3060/open/extend/core/IApiRequest.java new file mode 100644 index 0000000..e580ccf --- /dev/null +++ b/src/main/java/com/tsl3060/open/extend/core/IApiRequest.java @@ -0,0 +1,11 @@ +package com.tsl3060.open.extend.core; + +public interface IApiRequest { + /** + * 请求路径 + * + * @return + */ + String path(); + +} diff --git a/src/main/java/com/tsl3060/open/extend/core/INotifyListener.java b/src/main/java/com/tsl3060/open/extend/core/INotifyListener.java new file mode 100644 index 0000000..f6d3ac7 --- /dev/null +++ b/src/main/java/com/tsl3060/open/extend/core/INotifyListener.java @@ -0,0 +1,14 @@ +package com.tsl3060.open.extend.core; + +import com.tsl3060.open.extend.core.notify.CarbonOrderNotify; +import com.tsl3060.open.extend.core.notify.CarbonOrderNotifyAnswer; + +public interface INotifyListener { + /** + * 低碳积分通知 + * + * @param notify + * @return + */ + CarbonOrderNotifyAnswer carbon(CarbonOrderNotify notify); +} diff --git a/src/main/java/com/tsl3060/open/extend/core/NotifyAnswerResponse.java b/src/main/java/com/tsl3060/open/extend/core/NotifyAnswerResponse.java new file mode 100644 index 0000000..2408ff3 --- /dev/null +++ b/src/main/java/com/tsl3060/open/extend/core/NotifyAnswerResponse.java @@ -0,0 +1,84 @@ +package com.tsl3060.open.extend.core; + +import com.alibaba.fastjson2.annotation.JSONField; + +public class NotifyAnswerResponse { + @JSONField(name = "answer_id") + private String answerId; + @JSONField(name = "app_id") + private String appId; + private String time; + private Object payload; + + private String sign; + @JSONField(name = "sign_type") + private String signType; + + private String charset; + + private String result; + + public String getResult() { + return result; + } + + public void setResult(String result) { + this.result = result; + } + + public String getCharset() { + return charset; + } + + public void setCharset(String charset) { + this.charset = charset; + } + + public String getAnswerId() { + return answerId; + } + + public void setAnswerId(String answerId) { + this.answerId = answerId; + } + + public String getAppId() { + return appId; + } + + public void setAppId(String appId) { + this.appId = appId; + } + + public String getTime() { + return time; + } + + public void setTime(String time) { + this.time = time; + } + + public Object getPayload() { + return payload; + } + + public void setPayload(Object payload) { + this.payload = payload; + } + + public String getSign() { + return sign; + } + + public void setSign(String sign) { + this.sign = sign; + } + + public String getSignType() { + return signType; + } + + public void setSignType(String signType) { + this.signType = signType; + } +} diff --git a/src/main/java/com/tsl3060/open/extend/core/NotifyRequest.java b/src/main/java/com/tsl3060/open/extend/core/NotifyRequest.java new file mode 100644 index 0000000..65f737f --- /dev/null +++ b/src/main/java/com/tsl3060/open/extend/core/NotifyRequest.java @@ -0,0 +1,150 @@ +package com.tsl3060.open.extend.core; + +import com.alibaba.fastjson2.annotation.JSONField; + +public class NotifyRequest { + @JSONField(name = "notify_id") + private String notifyId; + @JSONField(name = "app_id") + private String appId; + private String module = ""; + @JSONField(name = "source_id") + private String sourceId = ""; + @JSONField(name = "err_code") + private int errCode; + @JSONField(name = "err_msg") + private String errMsg; + @JSONField(name = "sub_err") + private int subErr; + @JSONField(name = "sub_msg") + private String subMsg; + @JSONField(name = "sign_type") + private String signType; + private Object payload; + @JSONField(name = "open_id") + private String openId; + private String description; + private String charset = "UTF-8"; + private String time; + private String sign; + + public String getNotifyId() { + return notifyId; + } + + public void setNotifyId(String notifyId) { + this.notifyId = notifyId; + } + + public String getAppId() { + return appId; + } + + public void setAppId(String appId) { + this.appId = appId; + } + + public String getModule() { + return module; + } + + public void setModule(String module) { + this.module = module; + } + + public String getSourceId() { + return sourceId; + } + + public void setSourceId(String sourceId) { + this.sourceId = sourceId; + } + + public int getErrCode() { + return errCode; + } + + public void setErrCode(int errCode) { + this.errCode = errCode; + } + + public String getErrMsg() { + return errMsg; + } + + public void setErrMsg(String errMsg) { + this.errMsg = errMsg; + } + + public int getSubErr() { + return subErr; + } + + public void setSubErr(int subErr) { + this.subErr = subErr; + } + + public String getSubMsg() { + return subMsg; + } + + public void setSubMsg(String subMsg) { + this.subMsg = subMsg; + } + + public String getSignType() { + return signType; + } + + public void setSignType(String signType) { + this.signType = signType; + } + + public Object getPayload() { + return payload; + } + + public void setPayload(Object payload) { + this.payload = payload; + } + + public String getOpenId() { + return openId; + } + + public void setOpenId(String openId) { + this.openId = openId; + } + + public String getDescription() { + return description; + } + + public void setDescription(String description) { + this.description = description; + } + + public String getCharset() { + return charset; + } + + public void setCharset(String charset) { + this.charset = charset; + } + + public String getTime() { + return time; + } + + public void setTime(String time) { + this.time = time; + } + + public String getSign() { + return sign; + } + + public void setSign(String sign) { + this.sign = sign; + } +} diff --git a/src/main/java/com/tsl3060/open/extend/core/exception/ApiException.java b/src/main/java/com/tsl3060/open/extend/core/exception/ApiException.java new file mode 100644 index 0000000..fad7cc4 --- /dev/null +++ b/src/main/java/com/tsl3060/open/extend/core/exception/ApiException.java @@ -0,0 +1,11 @@ +package com.tsl3060.open.extend.core.exception; + +public class ApiException extends Exception { + + public ApiException() { + } + + public ApiException(String message) { + super(message); + } +} diff --git a/src/main/java/com/tsl3060/open/extend/core/exception/BadResourceException.java b/src/main/java/com/tsl3060/open/extend/core/exception/BadResourceException.java new file mode 100644 index 0000000..95d7dbf --- /dev/null +++ b/src/main/java/com/tsl3060/open/extend/core/exception/BadResourceException.java @@ -0,0 +1,21 @@ +package com.tsl3060.open.extend.core.exception; + +import java.io.IOException; + +public class BadResourceException extends IOException { + public BadResourceException() { + super(); + } + + public BadResourceException(String message) { + super(message); + } + + public BadResourceException(String message, Throwable cause) { + super(message, cause); + } + + public BadResourceException(Throwable cause) { + super(cause); + } +} diff --git a/src/main/java/com/tsl3060/open/extend/core/notify/CarbonOrderNotify.java b/src/main/java/com/tsl3060/open/extend/core/notify/CarbonOrderNotify.java new file mode 100644 index 0000000..4c315c2 --- /dev/null +++ b/src/main/java/com/tsl3060/open/extend/core/notify/CarbonOrderNotify.java @@ -0,0 +1,110 @@ +package com.tsl3060.open.extend.core.notify; + +import com.alibaba.fastjson2.annotation.JSONField; + +public class CarbonOrderNotify { + /** + * 用户OpenID + */ + private String openid; + /** + * 订单号 + */ + @JSONField(name = "order_no") + private String orderNo; + /** + * 低碳订单号 + */ + @JSONField(name = "carbon_no") + private String carbonNo; + + /** + * 用户低碳积分 + */ + @JSONField(name = "carbon") + private double carbon; + + /** + * 本次订单新增积分 + */ + @JSONField(name = "amount") + private double amount; + /** + * 订单时间 + */ + @JSONField(name = "order_time") + private String orderTime; + /** + * 低碳积分订单完成时间 + */ + @JSONField(name = "complete_time") + private String completeTime; + /** + * 订单类型 + */ + private String type; + + public String getOpenid() { + return openid; + } + + public void setOpenid(String openid) { + this.openid = openid; + } + + public String getOrderNo() { + return orderNo; + } + + public void setOrderNo(String orderNo) { + this.orderNo = orderNo; + } + + public String getCarbonNo() { + return carbonNo; + } + + public void setCarbonNo(String carbonNo) { + this.carbonNo = carbonNo; + } + + public double getCarbon() { + return carbon; + } + + public void setCarbon(double carbon) { + this.carbon = carbon; + } + + public double getAmount() { + return amount; + } + + public void setAmount(double amount) { + this.amount = amount; + } + + public String getOrderTime() { + return orderTime; + } + + public void setOrderTime(String orderTime) { + this.orderTime = orderTime; + } + + public String getCompleteTime() { + return completeTime; + } + + public void setCompleteTime(String completeTime) { + this.completeTime = completeTime; + } + + public String getType() { + return type; + } + + public void setType(String type) { + this.type = type; + } +} diff --git a/src/main/java/com/tsl3060/open/extend/core/notify/CarbonOrderNotifyAnswer.java b/src/main/java/com/tsl3060/open/extend/core/notify/CarbonOrderNotifyAnswer.java new file mode 100644 index 0000000..be7d0ba --- /dev/null +++ b/src/main/java/com/tsl3060/open/extend/core/notify/CarbonOrderNotifyAnswer.java @@ -0,0 +1,20 @@ +package com.tsl3060.open.extend.core.notify; + +public class CarbonOrderNotifyAnswer implements IAnswer { + private boolean answer; + + public CarbonOrderNotifyAnswer() { + } + + public CarbonOrderNotifyAnswer(boolean answer) { + this.answer = answer; + } + + public boolean isAnswer() { + return answer; + } + + public void setAnswer(boolean answer) { + this.answer = answer; + } +} diff --git a/src/main/java/com/tsl3060/open/extend/core/notify/IAnswer.java b/src/main/java/com/tsl3060/open/extend/core/notify/IAnswer.java new file mode 100644 index 0000000..a9363d4 --- /dev/null +++ b/src/main/java/com/tsl3060/open/extend/core/notify/IAnswer.java @@ -0,0 +1,4 @@ +package com.tsl3060.open.extend.core.notify; + +public interface IAnswer { +} diff --git a/src/main/java/com/tsl3060/open/extend/core/payload/CarbonOrderPayload.java b/src/main/java/com/tsl3060/open/extend/core/payload/CarbonOrderPayload.java new file mode 100644 index 0000000..6b093bf --- /dev/null +++ b/src/main/java/com/tsl3060/open/extend/core/payload/CarbonOrderPayload.java @@ -0,0 +1,113 @@ +package com.tsl3060.open.extend.core.payload; + +import com.alibaba.fastjson2.annotation.JSONField; +import com.tsl3060.open.extend.core.IApiRequest; + +public class CarbonOrderPayload extends RequestPayload implements IApiRequest { + @Override + public String path() { + return "/v1/wanshun/wallet/carbon"; + } + + private String openid; + @JSONField(name = "order_no") + private String orderNo; + + private double mileage; + @JSONField(name = "order_time") + private String orderTime; + @JSONField(name = "order_state") + private String orderState; + @JSONField(name = "vehicle_model") + private String vehicleModel = ""; + @JSONField(name = "new_energy") + private boolean newEnergy = false; + @JSONField(name = "complete_time") + private String completeTime = ""; + + @JSONField(name = "order_pay") + private double orderPay = 0; + private String behavior = ""; + + + public String getOpenid() { + return openid; + } + + public void setOpenid(String openid) { + this.openid = openid; + } + + public String getOrderNo() { + return orderNo; + } + + public void setOrderNo(String orderNo) { + this.orderNo = orderNo; + } + + public double getMileage() { + return mileage; + } + + public void setMileage(double mileage) { + this.mileage = mileage; + } + + public String getOrderTime() { + return orderTime; + } + + public void setOrderTime(String orderTime) { + this.orderTime = orderTime; + } + + public boolean isNewEnergy() { + return newEnergy; + } + + public void setNewEnergy(boolean newEnergy) { + this.newEnergy = newEnergy; + } + + public String getOrderState() { + return orderState; + } + + public void setOrderState(String orderState) { + this.orderState = orderState; + } + + public String getVehicleModel() { + return vehicleModel; + } + + public void setVehicleModel(String vehicleModel) { + this.vehicleModel = vehicleModel; + } + + + public String getCompleteTime() { + return completeTime; + } + + public void setCompleteTime(String completeTime) { + this.completeTime = completeTime; + } + + public double getOrderPay() { + return orderPay; + } + + public void setOrderPay(double orderPay) { + this.orderPay = orderPay; + } + + public String getBehavior() { + return behavior; + } + + public void setBehavior(String behavior) { + this.behavior = behavior; + } +} diff --git a/src/main/java/com/tsl3060/open/extend/core/payload/RequestPayload.java b/src/main/java/com/tsl3060/open/extend/core/payload/RequestPayload.java new file mode 100644 index 0000000..2640a91 --- /dev/null +++ b/src/main/java/com/tsl3060/open/extend/core/payload/RequestPayload.java @@ -0,0 +1,13 @@ +package com.tsl3060.open.extend.core.payload; + +public abstract class RequestPayload { + private String source; + + public String getSource() { + return source; + } + + public void setSource(String source) { + this.source = source; + } +} diff --git a/src/main/java/com/tsl3060/open/extend/core/payload/UserLoginPayload.java b/src/main/java/com/tsl3060/open/extend/core/payload/UserLoginPayload.java new file mode 100644 index 0000000..9138551 --- /dev/null +++ b/src/main/java/com/tsl3060/open/extend/core/payload/UserLoginPayload.java @@ -0,0 +1,35 @@ +package com.tsl3060.open.extend.core.payload; + +import com.alibaba.fastjson2.annotation.JSONField; +import com.tsl3060.open.extend.core.IApiRequest; + +/** + * 用户登录载体 + */ +public class UserLoginPayload extends RequestPayload implements IApiRequest { + private String device; + + private String openid; + + + public String getDevice() { + return device; + } + + public void setDevice(String device) { + this.device = device; + } + + public String getOpenid() { + return openid; + } + + public void setOpenid(String openid) { + this.openid = openid; + } + + @Override + public String path() { + return "/v1/wanshun/account/login"; + } +} diff --git a/src/main/java/com/tsl3060/open/extend/core/payload/UserRegisterRequestPayload.java b/src/main/java/com/tsl3060/open/extend/core/payload/UserRegisterRequestPayload.java new file mode 100644 index 0000000..883d48f --- /dev/null +++ b/src/main/java/com/tsl3060/open/extend/core/payload/UserRegisterRequestPayload.java @@ -0,0 +1,40 @@ +package com.tsl3060.open.extend.core.payload; + +import com.tsl3060.open.extend.core.IApiRequest; +import com.tsl3060.open.extend.core.payload.RequestPayload; + +public class UserRegisterRequestPayload extends RequestPayload implements IApiRequest { + @Override + public String path() { + return "/v1/wanshun/account/register"; + } + + private String phone; + private String uid; + private String type; + + + public String getPhone() { + return phone; + } + + public void setPhone(String phone) { + this.phone = phone; + } + + public String getUid() { + return uid; + } + + public void setUid(String uid) { + this.uid = uid; + } + + public String getType() { + return type; + } + + public void setType(String type) { + this.type = type; + } +} diff --git a/src/main/java/com/tsl3060/open/extend/core/payload/WalletQueryPayload.java b/src/main/java/com/tsl3060/open/extend/core/payload/WalletQueryPayload.java new file mode 100644 index 0000000..cf9aa53 --- /dev/null +++ b/src/main/java/com/tsl3060/open/extend/core/payload/WalletQueryPayload.java @@ -0,0 +1,22 @@ +package com.tsl3060.open.extend.core.payload; + +import com.tsl3060.open.extend.core.IApiRequest; + +public class WalletQueryPayload extends RequestPayload implements IApiRequest { + + private String openid; + + + public String getOpenid() { + return openid; + } + + public void setOpenid(String openid) { + this.openid = openid; + } + + @Override + public String path() { + return "/v1/wanshun/wallet/query"; + } +} diff --git a/src/main/java/com/tsl3060/open/extend/core/response/CaptchaResponse.java b/src/main/java/com/tsl3060/open/extend/core/response/CaptchaResponse.java new file mode 100644 index 0000000..211a53c --- /dev/null +++ b/src/main/java/com/tsl3060/open/extend/core/response/CaptchaResponse.java @@ -0,0 +1,31 @@ +package com.tsl3060.open.extend.core.response; + +/** + * 验证码载体 + */ +public class CaptchaResponse { + /** + * 验证码内容 + */ + private String image; + /** + * 验证码ID + */ + private String id; + + public String getImage() { + return image; + } + + public void setImage(String image) { + this.image = image; + } + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } +} diff --git a/src/main/java/com/tsl3060/open/extend/core/response/CarbonOrderResponse.java b/src/main/java/com/tsl3060/open/extend/core/response/CarbonOrderResponse.java new file mode 100644 index 0000000..86ecc18 --- /dev/null +++ b/src/main/java/com/tsl3060/open/extend/core/response/CarbonOrderResponse.java @@ -0,0 +1,52 @@ +package com.tsl3060.open.extend.core.response; + +import com.alibaba.fastjson2.annotation.JSONField; + +public class CarbonOrderResponse { + private String openid; + private String carbon; + private double total; + private String record; + @JSONField(name = "record_time") + private String recordTime; + + public String getOpenid() { + return openid; + } + + public void setOpenid(String openid) { + this.openid = openid; + } + + public String getCarbon() { + return carbon; + } + + public void setCarbon(String carbon) { + this.carbon = carbon; + } + + public double getTotal() { + return total; + } + + public void setTotal(double total) { + this.total = total; + } + + public String getRecord() { + return record; + } + + public void setRecord(String record) { + this.record = record; + } + + public String getRecordTime() { + return recordTime; + } + + public void setRecordTime(String recordTime) { + this.recordTime = recordTime; + } +} diff --git a/src/main/java/com/tsl3060/open/extend/core/response/SmsSendResponse.java b/src/main/java/com/tsl3060/open/extend/core/response/SmsSendResponse.java new file mode 100644 index 0000000..7f91fb4 --- /dev/null +++ b/src/main/java/com/tsl3060/open/extend/core/response/SmsSendResponse.java @@ -0,0 +1,44 @@ +package com.tsl3060.open.extend.core.response; + +import com.alibaba.fastjson2.annotation.JSONField; + +public class SmsSendResponse { + private String phone; + private String send; + private int time; + @JSONField(name = "send_time") + private String sendTime; + + public String getPhone() { + return phone; + } + + public void setPhone(String phone) { + this.phone = phone; + } + + public String getSend() { + return send; + } + + public void setSend(String send) { + this.send = send; + } + + public int getTime() { + return time; + } + + public void setTime(int time) { + this.time = time; + } + + public String getSendTime() { + return sendTime; + } + + public void setSendTime(String sendTime) { + this.sendTime = sendTime; + } +} + diff --git a/src/main/java/com/tsl3060/open/extend/core/response/UserLoginResponse.java b/src/main/java/com/tsl3060/open/extend/core/response/UserLoginResponse.java new file mode 100644 index 0000000..6775cf2 --- /dev/null +++ b/src/main/java/com/tsl3060/open/extend/core/response/UserLoginResponse.java @@ -0,0 +1,35 @@ +package com.tsl3060.open.extend.core.response; + +import com.alibaba.fastjson2.annotation.JSONField; + +public class UserLoginResponse { + @JSONField(name = "expire_at") + private String expireAt; + private String openid; + private String token; + + + public String getOpenid() { + return openid; + } + + public void setOpenid(String openid) { + this.openid = openid; + } + + public String getToken() { + return token; + } + + public void setToken(String token) { + this.token = token; + } + + public String getExpireAt() { + return expireAt; + } + + public void setExpireAt(String expireAt) { + this.expireAt = expireAt; + } +} diff --git a/src/main/java/com/tsl3060/open/extend/core/response/UserRegisterResponse.java b/src/main/java/com/tsl3060/open/extend/core/response/UserRegisterResponse.java new file mode 100644 index 0000000..b387a53 --- /dev/null +++ b/src/main/java/com/tsl3060/open/extend/core/response/UserRegisterResponse.java @@ -0,0 +1,42 @@ +package com.tsl3060.open.extend.core.response; + +public class UserRegisterResponse { + private String nickname; + private String avatar; + private String openid; + + private String appid; + + public String getNickname() { + return nickname; + } + + public void setNickname(String nickname) { + this.nickname = nickname; + } + + public String getAvatar() { + return avatar; + } + + public void setAvatar(String avatar) { + this.avatar = avatar; + } + + public String getOpenid() { + return openid; + } + + public void setOpenid(String openid) { + this.openid = openid; + } + + + public String getAppid() { + return appid; + } + + public void setAppid(String appid) { + this.appid = appid; + } +} diff --git a/src/main/java/com/tsl3060/open/extend/core/response/WalletQueryResponse.java b/src/main/java/com/tsl3060/open/extend/core/response/WalletQueryResponse.java new file mode 100644 index 0000000..3f5024c --- /dev/null +++ b/src/main/java/com/tsl3060/open/extend/core/response/WalletQueryResponse.java @@ -0,0 +1,32 @@ +package com.tsl3060.open.extend.core.response; + +public class WalletQueryResponse { + private String openid; + private String carbon; + + private String query; + + public String getOpenid() { + return openid; + } + + public void setOpenid(String openid) { + this.openid = openid; + } + + public String getCarbon() { + return carbon; + } + + public void setCarbon(String carbon) { + this.carbon = carbon; + } + + public String getQuery() { + return query; + } + + public void setQuery(String query) { + this.query = query; + } +} diff --git a/src/main/java/com/tsl3060/open/extend/core/router/INotifyRouter.java b/src/main/java/com/tsl3060/open/extend/core/router/INotifyRouter.java new file mode 100644 index 0000000..159c7d6 --- /dev/null +++ b/src/main/java/com/tsl3060/open/extend/core/router/INotifyRouter.java @@ -0,0 +1,10 @@ +package com.tsl3060.open.extend.core.router; + +import com.tsl3060.open.extend.core.NotifyRequest; +import com.tsl3060.open.extend.core.notify.IAnswer; + +public interface INotifyRouter { + String path(); + + IAnswer makeBody(NotifyRequest notifyRequest) throws Exception; +} diff --git a/src/main/java/com/tsl3060/open/extend/core/router/NotifyMapRouter.java b/src/main/java/com/tsl3060/open/extend/core/router/NotifyMapRouter.java new file mode 100644 index 0000000..e1ccd38 --- /dev/null +++ b/src/main/java/com/tsl3060/open/extend/core/router/NotifyMapRouter.java @@ -0,0 +1,30 @@ +package com.tsl3060.open.extend.core.router; + +import cn.hutool.core.util.ClassUtil; +import cn.hutool.core.util.ReflectUtil; +import com.tsl3060.open.extend.core.INotifyListener; + +import java.util.HashMap; +import java.util.Map; +import java.util.Set; + +public class NotifyMapRouter { + private Map map = new HashMap<>(); + + public NotifyMapRouter(INotifyListener notifyListener) { + Set> classes = ClassUtil.scanPackage("com.tsl3060.open.extend.core.router.notify"); + + for (Class c : classes) { + INotifyRouter notifyRouter = (INotifyRouter) ReflectUtil.newInstance(c,notifyListener); + map.put(notifyRouter.path(), notifyRouter); + } + + } + + + public INotifyRouter getRouter(String module) { + return map.get(module); + } + + +} diff --git a/src/main/java/com/tsl3060/open/extend/core/router/notify/CarbonOrderNotifyRouter.java b/src/main/java/com/tsl3060/open/extend/core/router/notify/CarbonOrderNotifyRouter.java new file mode 100644 index 0000000..0edc5d2 --- /dev/null +++ b/src/main/java/com/tsl3060/open/extend/core/router/notify/CarbonOrderNotifyRouter.java @@ -0,0 +1,29 @@ +package com.tsl3060.open.extend.core.router.notify; + +import com.alibaba.fastjson2.JSON; +import com.tsl3060.open.extend.core.INotifyListener; +import com.tsl3060.open.extend.core.NotifyRequest; +import com.tsl3060.open.extend.core.notify.CarbonOrderNotify; +import com.tsl3060.open.extend.core.notify.IAnswer; +import com.tsl3060.open.extend.core.router.INotifyRouter; + +public class CarbonOrderNotifyRouter implements INotifyRouter { + + + private final INotifyListener notifyListener; + + public CarbonOrderNotifyRouter(INotifyListener notifyListener) { + this.notifyListener = notifyListener; + } + + @Override + public String path() { + return "/v1/wanshun/notify/carbon"; + } + + @Override + public IAnswer makeBody(NotifyRequest notifyRequest) throws Exception { + CarbonOrderNotify carbonOrderNotify = JSON.to(CarbonOrderNotify.class, notifyRequest.getPayload()); + return this.notifyListener.carbon(carbonOrderNotify); + } +} diff --git a/src/main/java/com/tsl3060/open/extend/core/secure/ISecure.java b/src/main/java/com/tsl3060/open/extend/core/secure/ISecure.java new file mode 100644 index 0000000..396f9ac --- /dev/null +++ b/src/main/java/com/tsl3060/open/extend/core/secure/ISecure.java @@ -0,0 +1,47 @@ +package com.tsl3060.open.extend.core.secure; + +import com.tsl3060.open.extend.core.*; + +public interface ISecure { + /** + * 参数设置 + * + * @param config + */ + void setConfig(Config config); + + /** + * 安全模块名称 + * + * @return 名称 + */ + String name(); + + /** + * 对请求进行签名 + * + * @param apiRequest 请求 + * @return 签名字符串 + */ + String requestSign(ApiRequest apiRequest); + + /** + * 同步返回验签 + * + * @param apiResponse 同步返回 + * @return 是否正确 + */ + boolean verifyResponse(ApiResponse apiResponse); + + /** + * 异步通知验签 + * + * @param notifyRequest 异步通知 + * @return 是否正确 + */ + boolean verifyNotify(NotifyRequest notifyRequest); + + + String answerSign(NotifyAnswerResponse response); + +} diff --git a/src/main/java/com/tsl3060/open/extend/core/secure/RSASecure.java b/src/main/java/com/tsl3060/open/extend/core/secure/RSASecure.java new file mode 100644 index 0000000..8a4365e --- /dev/null +++ b/src/main/java/com/tsl3060/open/extend/core/secure/RSASecure.java @@ -0,0 +1,220 @@ +package com.tsl3060.open.extend.core.secure; + +import cn.hutool.core.collection.CollectionUtil; +import cn.hutool.core.util.HexUtil; +import cn.hutool.log.Log; +import cn.hutool.log.LogFactory; +import com.alibaba.fastjson2.JSONObject; +import com.tsl3060.open.extend.core.*; + +import java.io.UnsupportedEncodingException; +import java.math.BigDecimal; +import java.nio.charset.Charset; +import java.security.*; +import java.security.spec.InvalidKeySpecException; +import java.security.spec.PKCS8EncodedKeySpec; +import java.security.spec.X509EncodedKeySpec; +import java.util.*; +import java.util.stream.Collectors; + +public class RSASecure implements ISecure { + + private final Log log = LogFactory.get(); + private static final String SIGNATURE_ALGORITHM = "SHA256withRSA"; + private static final String DIGEST_ALGORITHM = "SHA-256"; + public static final String NAME = "RSA"; + private Config config; + + @Override + public void setConfig(Config config) { + this.config = config; + } + + @Override + public String name() { + return NAME; + } + + private String object2LinkStr(Object ob) { + List payloadStr = new ArrayList<>(); + if (ob != null) { + JSONObject j = JSONObject.from(ob); + //排序 + Set _keys = j.keySet(); + List keys = _keys.stream().sorted(Comparator.naturalOrder()).collect(Collectors.toList()); + + for (String key : keys) { + if (j.get(key) != null) { + Object v = j.get(key); + String vx; + if (v instanceof Double) { + vx = String.format("%.2f", v); + } else if (v instanceof Float) { + vx = String.format("%.2f", v); + } else if (v instanceof Boolean) { + vx = String.format("%s", v); + } else if (v instanceof BigDecimal) { + vx = String.format("%.2f", v); + } else { + vx = String.valueOf(v); + } + payloadStr.add(String.format("%s=%s", key, vx)); + } + } + } + return CollectionUtil.join(payloadStr, "&"); + } + + public String sign(String content, String charset) { + + log.debug("req: {}", content); + try { + byte[] bytesContent = content.getBytes(charset); + MessageDigest messageDigest = MessageDigest.getInstance(DIGEST_ALGORITHM); + byte[] digestData = messageDigest.digest(bytesContent); + //签名工具 + byte[] pKeyData = Base64.getDecoder().decode(this.config.getPrivateKey()); + PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(pKeyData); + KeyFactory keyFactory = KeyFactory.getInstance(NAME); + PrivateKey pkey = keyFactory.generatePrivate(spec); + + Signature signature = Signature.getInstance(SIGNATURE_ALGORITHM); + signature.initSign(pkey); + signature.update(digestData); + byte[] s = signature.sign(); + return HexUtil.encodeHexStr(s); + + } catch (UnsupportedEncodingException | NoSuchAlgorithmException | InvalidKeySpecException | + SignatureException | + InvalidKeyException e) { + throw new RuntimeException(e); + } + } + + /** + * 对请求生成签名 + * + * @param request 请求 + * @return + */ + @Override + public String requestSign(ApiRequest request) { + Object ob = request.getPayload(); + String waitStr = this.object2LinkStr(ob); + String content = String.format("%s&%s&%s&%s&%s&%s&%s", + request.getPath(), + request.getAppId(), + request.getAccessToken(), + request.getSignType(), + request.getCharset(), + request.getTime(), + waitStr + ); + log.debug("req: {}", content); + + return this.sign(content, request.getCharset()); + + } + + + private boolean responseServerVerify(byte[] content, String sign) { + try { + MessageDigest messageDigest = MessageDigest.getInstance(DIGEST_ALGORITHM); + byte[] digestData = messageDigest.digest(content); + byte[] pubKeyData = Base64.getDecoder().decode(this.config.getApiPublicKey()); + X509EncodedKeySpec spec = new X509EncodedKeySpec(pubKeyData); + KeyFactory keyFactory = KeyFactory.getInstance(NAME); + PublicKey pubKey = keyFactory.generatePublic(spec); + Signature signature = Signature.getInstance(SIGNATURE_ALGORITHM); + signature.initVerify(pubKey); + signature.update(digestData); + + byte[] bodySign = HexUtil.decodeHex(sign); + return signature.verify(bodySign); + } catch (NoSuchAlgorithmException | InvalidKeySpecException | SignatureException | InvalidKeyException e) { + log.error(e); + return false; + } + } + + @Override + public boolean verifyResponse(ApiResponse response) { + String waitStr = this.object2LinkStr(response.getPayload()); + String formatStr = String.format( + "%s&%s&%s&%s&%s&%s&%s&%s&%s&%s&%s", + response.getResponseId(), + response.getErrCode(), + response.getErrMsg(), + response.getSubErr(), + response.getSubMsg(), + response.getTime(), + response.getOpenId(), + response.getSignType(), + response.getCharset(), + response.getDescription(), + waitStr + ); + log.debug("反馈内容 {}", formatStr); + log.debug("签名内容 {}", response.getSign()); + Charset bodyCharset = Charset.forName(response.getCharset()); + byte[] bytesContent = formatStr.getBytes(bodyCharset); + return this.responseServerVerify(bytesContent, response.getSign()); + + } + + /** + * 验证通知 + * + * @param response 异步通知 + * @return + */ + @Override + public boolean verifyNotify(NotifyRequest response) { + String waitStr = this.object2LinkStr(response.getPayload()); + String formatStr = String.format( + "%s&%s&%s&%s&%s&%s&%s&%s&%s&%s&%s&%s&%s", + response.getNotifyId(), + response.getSourceId(), + response.getAppId(), + response.getErrCode(), + response.getErrMsg(), + response.getSubErr(), + response.getSubMsg(), + response.getTime(), + response.getOpenId(), + response.getSignType(), + response.getCharset(), + response.getDescription(), + waitStr + ); + log.debug("通知内容 {}", formatStr); + log.debug("通知签名内容 {}", response.getSign()); + Charset bodyCharset = Charset.forName(response.getCharset()); + byte[] bytesContent = formatStr.getBytes(bodyCharset); + + return this.responseServerVerify(bytesContent, response.getSign()); + + } + + /** + * 通知反馈签名 + * + * @param response + * @return + */ + @Override + public String answerSign(NotifyAnswerResponse response) { + Object ob = response.getPayload(); + String waitStr = this.object2LinkStr(ob); + String content = String.format("%s&%s&%s&%s&%s&%s&%s", + response.getAnswerId(), + response.getAppId(), + response.getResult(), + response.getSignType(), + response.getCharset(), + response.getTime(), + waitStr + ); + return this.sign(content, response.getCharset()); + } +} diff --git a/src/main/java/com/tsl3060/open/extend/core/secure/SecureTool.java b/src/main/java/com/tsl3060/open/extend/core/secure/SecureTool.java new file mode 100644 index 0000000..c353e0e --- /dev/null +++ b/src/main/java/com/tsl3060/open/extend/core/secure/SecureTool.java @@ -0,0 +1,24 @@ +package com.tsl3060.open.extend.core.secure; + +import com.tsl3060.open.extend.core.Config; + +import java.util.HashMap; + +/** + * + */ +public class SecureTool { + + private final HashMap secureHashMap = new HashMap<>(); + + public SecureTool(Config config) { + RSASecure rsaSecure = new RSASecure(); + rsaSecure.setConfig(config); + secureHashMap.put(rsaSecure.name(), rsaSecure); + } + + public ISecure getSecure(String name) { + return secureHashMap.get(name); + } + +} diff --git a/src/main/resources/logback.xml b/src/main/resources/logback.xml new file mode 100644 index 0000000..2400527 --- /dev/null +++ b/src/main/resources/logback.xml @@ -0,0 +1,76 @@ + + + + + + + + + + + + + + + + + + %d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger -%msg%n + + + + + + %d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger -%msg%n + + + + + + ${logPath}/info/%d.log + + ${maxHistory} + + + + + + + + + ERROR + + + %d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger -%msg%n + + + ${logPath}/error/%d.log + + 3 + + + + + + + + + + + + + + + + + \ No newline at end of file