diff --git a/nxhs-service/src/main/java/cc/yunxi/config/WebMvcConfig.java b/nxhs-service/src/main/java/cc/yunxi/config/WebMvcConfig.java index 4e2b618..db8a86a 100644 --- a/nxhs-service/src/main/java/cc/yunxi/config/WebMvcConfig.java +++ b/nxhs-service/src/main/java/cc/yunxi/config/WebMvcConfig.java @@ -49,6 +49,8 @@ public class WebMvcConfig implements WebMvcConfigurer { "/static/**", "/upload/**", "/qrcode/**.txt", + "/wx-message/**",//微信消息推送验证 + "/wx-message/**",//微信消息推送验证 "/doc.html" ); diff --git a/nxhs-service/src/main/java/cc/yunxi/config/props/WxHsyProperties.java b/nxhs-service/src/main/java/cc/yunxi/config/props/WxHsyProperties.java index 65722b8..1cfb686 100644 --- a/nxhs-service/src/main/java/cc/yunxi/config/props/WxHsyProperties.java +++ b/nxhs-service/src/main/java/cc/yunxi/config/props/WxHsyProperties.java @@ -14,4 +14,9 @@ import org.springframework.stereotype.Component; public class WxHsyProperties { private String appId; private String appSecret; + private String tokenUrl; + private String sendMessageUrl; + private String EncodingAESKey; + private String token; + } diff --git a/nxhs-service/src/main/java/cc/yunxi/config/props/WxShProperties.java b/nxhs-service/src/main/java/cc/yunxi/config/props/WxShProperties.java index 6409a3e..5700a92 100644 --- a/nxhs-service/src/main/java/cc/yunxi/config/props/WxShProperties.java +++ b/nxhs-service/src/main/java/cc/yunxi/config/props/WxShProperties.java @@ -14,4 +14,8 @@ import org.springframework.stereotype.Component; public class WxShProperties { private String appId; private String appSecret; + private String tokenUrl; + private String sendMessageUrl; + private String EncodingAESKey; + private String token; } diff --git a/nxhs-service/src/main/java/cc/yunxi/controller/WxMessageController.java b/nxhs-service/src/main/java/cc/yunxi/controller/WxMessageController.java new file mode 100644 index 0000000..2a1c51d --- /dev/null +++ b/nxhs-service/src/main/java/cc/yunxi/controller/WxMessageController.java @@ -0,0 +1,153 @@ +package cc.yunxi.controller; + + +import cc.yunxi.common.domain.CommonResult; +import cc.yunxi.domain.vo.vxmessage.AccessToken; +import cc.yunxi.domain.vo.vxmessage.MessageTemplate; +import cc.yunxi.domain.vo.vxmessage.OrderNew; +import cc.yunxi.domain.vo.vxmessage.ResultVo; +import cc.yunxi.utils.VerifyUtil; +import cc.yunxi.utils.WeChatMessageUtil; +import cc.yunxi.utils.WeChatUtil; +import cn.hutool.json.JSONObject; +import cn.hutool.json.JSONUtil; +import io.swagger.annotations.Api; +import lombok.extern.slf4j.Slf4j; +import org.springframework.web.bind.annotation.*; + +import javax.annotation.Resource; + +@Api(tags = "对接微信推送认证") +@RestController +@RequestMapping("/wx-message") +@Slf4j +public class WxMessageController { + + + @Resource + private WeChatMessageUtil weChatMessageUtil; + + @Resource + private VerifyUtil verifyUtil; + + /** + * 预约端对接微信推送消息验证 + * + * @param signature 签名 + * @param timestamp 时间戳 + * @param nonce 随机数 + * @param echostr 随机字符串 + * @return + */ + @GetMapping("/clientReceive") + public String clientVerify(@RequestParam String signature, + @RequestParam String timestamp, + @RequestParam String nonce, + @RequestParam String echostr) { + log.info("微信推送验证"); + boolean isOfficial = verifyUtil.checkSignature("client", signature, timestamp, nonce); + if (isOfficial) return echostr; + else { + log.error("微信推送验证失败!非官方推送,{},{},{},{}", signature, timestamp, nonce, echostr); + return "error"; + } + } + + /** + * 回收端对接微信推送消息验证 + * + * @param signature 签名 + * @param timestamp 时间戳 + * @param nonce 随机数 + * @param echostr 随机字符串 + * @return + */ + @GetMapping("/recyclerReceive") + public String recyclerVerify(@RequestParam String signature, + @RequestParam String timestamp, + @RequestParam String nonce, + @RequestParam String echostr) { + log.info("微信推送验证"); + boolean isOfficial = verifyUtil.checkSignature("recycler", signature, timestamp, nonce); + if (isOfficial) return echostr; + else { + log.error("微信推送验证失败!非官方推送,{},{},{},{}", signature, timestamp, nonce, echostr); + return "error"; + } + } + + //散户端端接收微信平台消息 + @PostMapping("/clientReceive") + public String clientReceive(@RequestParam("timestamp") String timestamp, + @RequestParam("nonce") String nonce, + @RequestParam("openid") String openid, + @RequestParam("encrypt_type") String encryptType, + @RequestParam("msg_signature") String msgSignature, + @RequestBody String encryptedData) { + JSONObject _encryptedData = JSONUtil.parseObj(encryptedData); + String encrypt = _encryptedData.getStr("Encrypt"); + log.info("微信推送验证"); + boolean isOfficial = verifyUtil.checkSignature("client", timestamp, nonce, encrypt,msgSignature); + if (!isOfficial) { + log.error("接收推送消息失败!非官方推送,{},{},{},{}", timestamp, nonce, encrypt, msgSignature); + return "error"; + } + String message = weChatMessageUtil.decryptMessage(encrypt, "recycler"); + log.info("收到微信推送消息,{}", message); + + return "success"; + } + + //回收端接收微信平台消息 + @PostMapping("/recyclerReceive") + public String recyclerReceive(@RequestParam("timestamp") String timestamp, + @RequestParam("nonce") String nonce, + @RequestParam("openid") String openid, + @RequestParam("encrypt_type") String encryptType, + @RequestParam("msg_signature") String msgSignature, + @RequestBody String encryptedData) { + JSONObject _encryptedData = JSONUtil.parseObj(encryptedData); + String encrypt = _encryptedData.getStr("Encrypt"); + log.info("微信推送验证"); + boolean isOfficial = verifyUtil.checkSignature("recycler", timestamp, nonce, encrypt,msgSignature); + if (!isOfficial) { + log.error("接收推送消息失败!非官方推送,{},{},{},{}", timestamp, nonce, encrypt, msgSignature); + return "error"; + } + + String message = weChatMessageUtil.decryptMessage(encrypt, "recycler"); + log.info("收到微信推送消息,{}", message); + return "success"; + } + + @PostMapping("/getToken") + public CommonResult getToken(@RequestBody String message) { + AccessToken client = weChatMessageUtil.getAccessToken("client"); + return CommonResult.success(client); + } + + @PostMapping("/send") + public CommonResult sendMessage(@RequestBody String message) { + AccessToken client = weChatMessageUtil.getAccessToken("client"); + + OrderNew orderNew = new OrderNew(); + orderNew.setCharacter_string22("11");//订单号 + orderNew.setTime1("2024/06/04 16:30~17:30");//预约时间 + orderNew.setName15("上门回收");//服务名称 + orderNew.setThing9("地址");//预约地址 + orderNew.setPhone_number43("13183060802");//联系电话 + MessageTemplate template = new MessageTemplate(); + template.setTemplate_id(OrderNew.TEMPLATE_ID); + template.setData(orderNew); + template.setTouser("oYkV866Jjz197Iya3kJQwdypNPq8"); + template.setMiniprogram_state("trial"); + template.setPage("/pages/orderDetail/orderDetail"); + template.setLang("zh_CN"); + + ResultVo result = weChatMessageUtil.sendMessage(template, "client"); + + return CommonResult.success(result); + } + + +} diff --git a/nxhs-service/src/main/java/cc/yunxi/domain/vo/vxmessage/AccessToken.java b/nxhs-service/src/main/java/cc/yunxi/domain/vo/vxmessage/AccessToken.java new file mode 100644 index 0000000..b716ec3 --- /dev/null +++ b/nxhs-service/src/main/java/cc/yunxi/domain/vo/vxmessage/AccessToken.java @@ -0,0 +1,33 @@ +package cc.yunxi.domain.vo.vxmessage; + +import cn.hutool.http.HttpUtil; +import cn.hutool.json.JSONObject; +import cn.hutool.json.JSONUtil; +import com.fasterxml.jackson.annotation.JsonIgnore; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Component; + +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.atomic.AtomicLong; + + +@Slf4j +@Data +@NoArgsConstructor +@ApiModel("对接微信开发平台,获取发送推送消息的access_token") +public class AccessToken { + + /*---------------- 返回体 ---------------------*/ + @ApiModelProperty("token内容") + private String access_token; + @ApiModelProperty("token过期时间 转换为毫秒") + private AtomicLong expires_in = new AtomicLong(System.currentTimeMillis() * 1000); + + +} diff --git a/nxhs-service/src/main/java/cc/yunxi/domain/vo/vxmessage/BaseMessage.java b/nxhs-service/src/main/java/cc/yunxi/domain/vo/vxmessage/BaseMessage.java new file mode 100644 index 0000000..9f802aa --- /dev/null +++ b/nxhs-service/src/main/java/cc/yunxi/domain/vo/vxmessage/BaseMessage.java @@ -0,0 +1,10 @@ +package cc.yunxi.domain.vo.vxmessage; + +import io.swagger.annotations.ApiModel; +import lombok.Data; + +@Data +@ApiModel(description = "订单信息基类") +public class BaseMessage { + +} diff --git a/nxhs-service/src/main/java/cc/yunxi/domain/vo/vxmessage/FinishOrder.java b/nxhs-service/src/main/java/cc/yunxi/domain/vo/vxmessage/FinishOrder.java new file mode 100644 index 0000000..6ecfbe4 --- /dev/null +++ b/nxhs-service/src/main/java/cc/yunxi/domain/vo/vxmessage/FinishOrder.java @@ -0,0 +1,21 @@ +package cc.yunxi.domain.vo.vxmessage; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +@Data +@ApiModel("订单完成") +public class FinishOrder extends BaseMessage{ + + @ApiModelProperty("订单号") + private String character_string5; + @ApiModelProperty("订单状态") + private String thing3; + @ApiModelProperty("实付金额") + private String amount22; + @ApiModelProperty("服务方") + private String thing10; + @ApiModelProperty("完成时间") + private String date9; +} diff --git a/nxhs-service/src/main/java/cc/yunxi/domain/vo/vxmessage/MessageTemplate.java b/nxhs-service/src/main/java/cc/yunxi/domain/vo/vxmessage/MessageTemplate.java new file mode 100644 index 0000000..42dbbc7 --- /dev/null +++ b/nxhs-service/src/main/java/cc/yunxi/domain/vo/vxmessage/MessageTemplate.java @@ -0,0 +1,23 @@ +package cc.yunxi.domain.vo.vxmessage; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +@Data +@ApiModel(description = "微信推送消息") +public class MessageTemplate { + @ApiModelProperty("消息模版") + private String template_id; + @ApiModelProperty("跳转页面") + private String page; + @ApiModelProperty("接受者") + private String touser; + @ApiModelProperty("模板内容") + private BaseMessage data; + @ApiModelProperty("跳转小程序类型:developer为开发版;trial为体验版;formal为正式版;默认为正式版") + private String miniprogram_state; + + @ApiModelProperty("语言:支持zh_CN(简体中文)、en_US(英文)、zh_HK(繁体中文)、zh_TW(繁体中文),默认为zh_CN") + private String lang = "zh_CN"; +} diff --git a/nxhs-service/src/main/java/cc/yunxi/domain/vo/vxmessage/OrderCancel.java b/nxhs-service/src/main/java/cc/yunxi/domain/vo/vxmessage/OrderCancel.java new file mode 100644 index 0000000..a3acc53 --- /dev/null +++ b/nxhs-service/src/main/java/cc/yunxi/domain/vo/vxmessage/OrderCancel.java @@ -0,0 +1,20 @@ +package cc.yunxi.domain.vo.vxmessage; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +@Data +@ApiModel("订单取消") +public class OrderCancel extends BaseMessage { + @ApiModelProperty("订单编号") + private String character_string3; + @ApiModelProperty("订单状态") + private String phrase4; + @ApiModelProperty("服务地址") + private String thing29; + @ApiModelProperty("预约时间") + private String date10; + @ApiModelProperty("操作人") + private String thing25; +} diff --git a/nxhs-service/src/main/java/cc/yunxi/domain/vo/vxmessage/OrderNew.java b/nxhs-service/src/main/java/cc/yunxi/domain/vo/vxmessage/OrderNew.java new file mode 100644 index 0000000..1ffaa7d --- /dev/null +++ b/nxhs-service/src/main/java/cc/yunxi/domain/vo/vxmessage/OrderNew.java @@ -0,0 +1,25 @@ +package cc.yunxi.domain.vo.vxmessage; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +@Data +@ApiModel(description = "新订单预约成功通知消息模版") +public class OrderNew extends BaseMessage{ + + @JsonIgnore//不在data属性中 + @ApiModelProperty("消息模版ID") + public static final String TEMPLATE_ID = "TfLbZu3DxLSp4TnuTVCsDTY2U4zSV2M7MNTrGHaUL5s"; + @ApiModelProperty("订单编号") + private String character_string22; + @ApiModelProperty("预约时间") + private String time1; + @ApiModelProperty("服务名称") + private String name15; + @ApiModelProperty("预约地址") + private String thing9; + @ApiModelProperty("联系电话") + private String phone_number43; +} diff --git a/nxhs-service/src/main/java/cc/yunxi/domain/vo/vxmessage/OrderTaken.java b/nxhs-service/src/main/java/cc/yunxi/domain/vo/vxmessage/OrderTaken.java new file mode 100644 index 0000000..93f9666 --- /dev/null +++ b/nxhs-service/src/main/java/cc/yunxi/domain/vo/vxmessage/OrderTaken.java @@ -0,0 +1,21 @@ +package cc.yunxi.domain.vo.vxmessage; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import io.swagger.annotations.ApiOperation; +import lombok.Data; + +@ApiModel("已接单消息模板") +@Data +public class OrderTaken extends BaseMessage{ + @ApiModelProperty("订单号") + private String character_string5; + @ApiModelProperty("订单状态") + private String thing4; + @ApiModelProperty("联系人") + private String thing18; + @ApiModelProperty("联系电话") + private String phone_number14; + @ApiModelProperty("服务时间") + private String thing6; +} diff --git a/nxhs-service/src/main/java/cc/yunxi/domain/vo/vxmessage/ResultVo.java b/nxhs-service/src/main/java/cc/yunxi/domain/vo/vxmessage/ResultVo.java new file mode 100644 index 0000000..0d97327 --- /dev/null +++ b/nxhs-service/src/main/java/cc/yunxi/domain/vo/vxmessage/ResultVo.java @@ -0,0 +1,14 @@ +package cc.yunxi.domain.vo.vxmessage; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +@ApiModel("发送订阅消息的结果") +@Data +public class ResultVo { + @ApiModelProperty("错误代码") + private int errcode; + @ApiModelProperty("错误描述") + private String errmsg; +} diff --git a/nxhs-service/src/main/java/cc/yunxi/utils/VerifyUtil.java b/nxhs-service/src/main/java/cc/yunxi/utils/VerifyUtil.java new file mode 100644 index 0000000..fef2a2b --- /dev/null +++ b/nxhs-service/src/main/java/cc/yunxi/utils/VerifyUtil.java @@ -0,0 +1,133 @@ +package cc.yunxi.utils; + +import cc.yunxi.config.props.WxHsyProperties; +import cc.yunxi.config.props.WxShProperties; +import lombok.Data; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Component; + +import javax.annotation.Resource; +import java.nio.charset.StandardCharsets; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.util.Arrays; + +@Component +@Slf4j +public class VerifyUtil { + + + @Resource + private WxShProperties clientProperties; + @Resource + private WxHsyProperties recyclerProperties; + + /** + * 微信推送验证 明文模式 + * 推送的URL链接:https://www.qq.com/revice?signature=f464b24fc39322e44b38aa78f5edd27bd1441696&echostr=4375120948345356249×tamp=1714036504&nonce=1514711492 + * 将token、timestamp、nonce三个参数进行字典序排序,排序后结果为:["1514711492","1714036504","AAAAA"]。 + * 将三个参数字符串拼接成一个字符串:"15147114921714036504AAAAA" + * 进行sha1计算签名:f464b24fc39322e44b38aa78f5edd27bd1441696 + * 与URL链接中的signature参数进行对比,相等说明请求来自微信服务器,合法。 + * 构造回包返回微信,回包消息体内容为URL链接中的echostr参数4375120948345356249。 + * + * @param endpoint 来源终端 client 预约端 recycler 回收员端 + * @param signature 签名 + * @param timestamp 时间戳 + * @param nonce 随机数 + * @return 是否来着微信服务器 + */ + public boolean checkSignature(String endpoint, String signature, String timestamp, String nonce) { + String token; + if ("client".equals(endpoint)) { + token = clientProperties.getToken(); + } else if ("recycler".equals(endpoint)) { + token = recyclerProperties.getToken(); + } else { + log.error("endpoint错误,微信认证失败:{}", endpoint); + return false; + } + String[] arr = new String[]{token, timestamp, nonce}; + Arrays.sort(arr); + + // 拼接排序后的字符串 + StringBuilder content = new StringBuilder(); + for (String s : arr) { + content.append(s); + } + + try { + MessageDigest instance = MessageDigest.getInstance("SHA-1"); + instance.update(content.toString().getBytes(StandardCharsets.UTF_8)); + + // 完成哈希计算 + byte[] hash = instance.digest(); + + // 转换为十六进制字符串 + StringBuilder hexString = new StringBuilder(); + for (byte b : hash) { + String hex = Integer.toHexString(0xff & b); + if (hex.length() == 1) hexString.append('0'); + hexString.append(hex); + } + // 比较签名 + return signature.equals(hexString.toString()); + } catch (NoSuchAlgorithmException e) { + // SHA-1 应该总是可用的,但以防万一 + e.printStackTrace(); + } + // 签名不匹配或其他错误,返回 false + return false; + } + + /** + * 安全模式接收消息 验证签名 + * + * @param endpoint 来源终端 client 预约端 recycler 回收员端 + * @param timestamp 时间戳 + * @param nonce 随机数 + * @param Encrypt 包体内字段 + * @param msgSignature 签名 + * @return 是否来着微信服务器 + */ + public boolean checkSignature(String endpoint, String timestamp, String nonce, String Encrypt, String msgSignature) { + String token; + if ("client".equals(endpoint)) { + token = clientProperties.getToken(); + } else if ("recycler".equals(endpoint)) { + token = recyclerProperties.getToken(); + } else { + log.error("endpoint错误,微信认证失败:{}", endpoint); + return false; + } + String[] arr = new String[]{token, timestamp, nonce, Encrypt}; + Arrays.sort(arr); + // 拼接排序后的字符串 + StringBuilder content = new StringBuilder(); + for (String s : arr) { + content.append(s); + } + try { + MessageDigest instance = MessageDigest.getInstance("SHA-1"); + instance.update(content.toString().getBytes(StandardCharsets.UTF_8)); + + // 完成哈希计算 + byte[] hash = instance.digest(); + + // 转换为十六进制字符串 + StringBuilder hexString = new StringBuilder(); + for (byte b : hash) { + String hex = Integer.toHexString(0xff & b); + if (hex.length() == 1) hexString.append('0'); + hexString.append(hex); + } + // 比较签名 + return msgSignature.equals(hexString.toString()); + } catch (NoSuchAlgorithmException e) { + // SHA-1 应该总是可用的,但以防万一 + e.printStackTrace(); + } + // 签名不匹配或其他错误,返回 false + return false; + } +} diff --git a/nxhs-service/src/main/java/cc/yunxi/utils/WeChatMessageUtil.java b/nxhs-service/src/main/java/cc/yunxi/utils/WeChatMessageUtil.java new file mode 100644 index 0000000..8bd4c8b --- /dev/null +++ b/nxhs-service/src/main/java/cc/yunxi/utils/WeChatMessageUtil.java @@ -0,0 +1,143 @@ +package cc.yunxi.utils; + +import cc.yunxi.config.props.WxHsyProperties; +import cc.yunxi.config.props.WxShProperties; +import cc.yunxi.domain.vo.vxmessage.AccessToken; +import cc.yunxi.domain.vo.vxmessage.MessageTemplate; +import cc.yunxi.domain.vo.vxmessage.ResultVo; +import cn.hutool.http.HttpUtil; +import cn.hutool.json.JSONUtil; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Component; + +import javax.annotation.Resource; +import javax.crypto.Cipher; +import javax.crypto.spec.IvParameterSpec; +import javax.crypto.spec.SecretKeySpec; +import java.util.Arrays; +import java.util.Base64; +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.atomic.AtomicLong; + + +@Slf4j +@Component +public class WeChatMessageUtil { + @Resource + private WxShProperties clientProperties; + @Resource + private WxHsyProperties recyclerProperties; + + private static final Map accessTokenMap = new ConcurrentHashMap<>(); + + //推送消息给微信用户 + public ResultVo sendMessage(MessageTemplate messageVo, String endpointType) { + ResultVo vo = new ResultVo(); + try { + if ("client".equals(endpointType)) { + String url = clientProperties.getSendMessageUrl(); + AccessToken accessToken = getAccessToken(endpointType); + String token = accessToken.getAccess_token(); + String post = HttpUtil.post(url + "?access_token=" + token, JSONUtil.toJsonStr(messageVo)); + vo = JSONUtil.toBean(post, ResultVo.class); + } else if ("recycler".equals(endpointType)) { + String url = recyclerProperties.getSendMessageUrl(); + AccessToken accessToken = getAccessToken(endpointType); + String token = accessToken.getAccess_token(); + String post = HttpUtil.post(url + "?access_token=" + token, JSONUtil.toJsonStr(messageVo)); + vo = JSONUtil.toBean(post, ResultVo.class); + } else { + vo.setErrcode(400); + vo.setErrmsg("请求未发送,参数错误!"); + } + } catch (RuntimeException e) { + e.printStackTrace(); + vo.setErrcode(500); + vo.setErrmsg(e.getMessage()); + } + return vo; + } + + + /** + * @param endpointType 小程序类型 client recycler + * @return access_token + */ + public AccessToken getAccessToken(String endpointType) { + AccessToken token = accessTokenMap.get(endpointType); + boolean expired = isExpired(token); + if (expired) {//过期重新获取 + // 获取access_token + String result; + Map param = new HashMap<>(); + param.put("grant_type", "client_credential");//固定值 + if ("client".equals(endpointType)) {//预约端 + param.put("appid", clientProperties.getAppId()); + param.put("secret", clientProperties.getAppSecret()); + result = HttpUtil.get(clientProperties.getTokenUrl(), param); + } else if ("recycler".equals(endpointType)) {//回收端 + param.put("appid", recyclerProperties.getAppId()); + param.put("secret", recyclerProperties.getAppSecret()); + result = HttpUtil.get(recyclerProperties.getTokenUrl(), param); + } else { + return null; + } + AccessToken access_token = JSONUtil.toBean(result, AccessToken.class); + access_token.setExpires_in(new AtomicLong(10 * 1000 + access_token.getExpires_in().get() * 1000)); + accessTokenMap.put(endpointType, access_token); + } + log.info("当前微信access_token为:{}", token); + return token; + } + + /** + * 是否过期 + * + * @param token + * @return + */ + private boolean isExpired(AccessToken token) { + if (null != token && + token.getExpires_in().get() > System.currentTimeMillis() + 10 * 1000) { + return false; + } + return true; + } + + + //消息解码 + public String decryptMessage(String encryptedMsg, String endpointType) { + try { + String encodingAESKey; + if ("client".equals(endpointType)) { + encodingAESKey = clientProperties.getEncodingAESKey(); + } else if ("recycler".equals(endpointType)) { + encodingAESKey = recyclerProperties.getEncodingAESKey(); + } else { + log.error("无法获取解密秘钥:{}", endpointType); + return "error"; + } + byte[] aesKey = Base64.getDecoder().decode(encodingAESKey + "="); + byte[] iv = Arrays.copyOfRange(aesKey, 0, 16); + + byte[] encryptedBytes = Base64.getDecoder().decode(encryptedMsg); + + Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); + SecretKeySpec keySpec = new SecretKeySpec(aesKey, "AES"); + IvParameterSpec ivSpec = new IvParameterSpec(iv); + cipher.init(Cipher.DECRYPT_MODE, keySpec, ivSpec); + + byte[] decryptedBytes = cipher.doFinal(encryptedBytes); + + // PKCS#7 unpadding + int pad = decryptedBytes[decryptedBytes.length - 1]; + return new String(Arrays.copyOfRange(decryptedBytes, 16, decryptedBytes.length - pad)); + } catch (Exception e) { + e.printStackTrace(); + return null; + } + } + +} diff --git a/nxhs-service/src/main/resources/application.yml b/nxhs-service/src/main/resources/application.yml index 0391167..5b3e558 100644 --- a/nxhs-service/src/main/resources/application.yml +++ b/nxhs-service/src/main/resources/application.yml @@ -102,6 +102,8 @@ nxhs: - /api/file/download - /api/index/products - /api/device/** + - /api/qrcode/**.txt + - /api/wx-message/** adminKey: 8bd2aa89033ead51c505e44994e42189 # 后台接口访问Key upload: basePath: /upload/ @@ -115,7 +117,16 @@ nxhs: client: # 散户端 appid: wx630bc4f43990c80c appsecret: 37028048aad5e1877c2b2aeacdfdc01b - recycler: # 回收员端 + token: AAAAA #消息推送对接令牌 + tokenUrl: https://api.weixin.qq.com/cgi-bin/token #获取微信平台token地址 + sendMessageUrl: https://api.weixin.qq.com/cgi-bin/message/subscribe/send #调用消息推送接口地址 + EncodingAESKey: g30DNk4Rf9RW0MLwCk0FfcfIsmiPnQ1NN7yLT8pcKNJ #消息解码秘钥 + recycler: # 回收员端 appid: wxf82bcc798891a29d appsecret: f37fb0ab2b5f691d8507acced60a58fb + token: AAAAA #消息推送对接令牌 + tokenUrl: https://api.weixin.qq.com/cgi-bin/token #获取微信平台token地址 + sendMessageUrl: https://api.weixin.qq.com/cgi-bin/message/subscribe/send #调用消息推送接口地址 + EncodingAESKey: #消息解码秘钥 + # 微信支付 # keytool -genkeypair -alias nxhs -keyalg RSA -keypass nxhs2024 -keystore nxhs.jks -storepass nxhs2024 \ No newline at end of file