From 39faf4a6e8023e84234d40989e61b24536b1276c Mon Sep 17 00:00:00 2001 From: zhuyebai Date: Tue, 9 May 2023 14:49:40 +0800 Subject: [PATCH] =?UTF-8?q?=E5=88=9D=E6=AD=A5=E5=AE=9E=E7=8E=B0=E5=BE=AE?= =?UTF-8?q?=E4=BF=A1=E8=87=AA=E5=8A=A8=E5=9B=9E=E5=A4=8D=E6=B6=88=E6=81=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- novel-common/pom.xml | 6 ++ .../novel/controller/WeChatController.java | 40 ++++++++ .../novel/core/except/AesException.java | 62 ++++++++++++ .../com/java2nb/novel/core/utils/SHA1.java | 53 +++++++++++ .../novel/core/utils/WXPublicUtils.java | 30 ++++++ .../com/java2nb/novel/dto/WxMessageDto.java | 95 +++++++++++++++++++ .../java2nb/novel/service/WeChatService.java | 30 ++++++ .../novel/service/impl/WeChatServiceImpl.java | 68 +++++++++++++ 8 files changed, 384 insertions(+) create mode 100644 novel-front/src/main/java/com/java2nb/novel/controller/WeChatController.java create mode 100644 novel-front/src/main/java/com/java2nb/novel/core/except/AesException.java create mode 100644 novel-front/src/main/java/com/java2nb/novel/core/utils/SHA1.java create mode 100644 novel-front/src/main/java/com/java2nb/novel/core/utils/WXPublicUtils.java create mode 100644 novel-front/src/main/java/com/java2nb/novel/dto/WxMessageDto.java create mode 100644 novel-front/src/main/java/com/java2nb/novel/service/WeChatService.java create mode 100644 novel-front/src/main/java/com/java2nb/novel/service/impl/WeChatServiceImpl.java diff --git a/novel-common/pom.xml b/novel-common/pom.xml index e5c800d..64cf700 100644 --- a/novel-common/pom.xml +++ b/novel-common/pom.xml @@ -66,6 +66,12 @@ ${sharding.jdbc.version} + + cn.hutool + hutool-all + 5.8.16 + + diff --git a/novel-front/src/main/java/com/java2nb/novel/controller/WeChatController.java b/novel-front/src/main/java/com/java2nb/novel/controller/WeChatController.java new file mode 100644 index 0000000..efd0d77 --- /dev/null +++ b/novel-front/src/main/java/com/java2nb/novel/controller/WeChatController.java @@ -0,0 +1,40 @@ +package com.java2nb.novel.controller; + +import com.java2nb.novel.core.except.AesException; +import com.java2nb.novel.core.utils.WXPublicUtils; +import com.java2nb.novel.dto.WxMessageDto; +import com.java2nb.novel.service.WeChatService; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +/** + * @author zhu + * @date 2023/5/9 + * @description + */ +@RestController +@RequestMapping("/wechat") +@RequiredArgsConstructor +@Slf4j +public class WeChatController { + + @Autowired + private WeChatService weChatService; + + @GetMapping("/") + @ResponseBody + public String weCheck(HttpServletRequest request) throws AesException { + return weChatService.weCheck(request); + } + + @PostMapping("/") + @ResponseBody + public String getMessage(HttpServletRequest request,@RequestBody WxMessageDto dto) { + return weChatService.getMessage(request, dto); + } +} diff --git a/novel-front/src/main/java/com/java2nb/novel/core/except/AesException.java b/novel-front/src/main/java/com/java2nb/novel/core/except/AesException.java new file mode 100644 index 0000000..fe65755 --- /dev/null +++ b/novel-front/src/main/java/com/java2nb/novel/core/except/AesException.java @@ -0,0 +1,62 @@ +package com.java2nb.novel.core.except; + +/** + * @author zhu + * @date 2023/5/9 + * @description + */ +@SuppressWarnings("serial") +public class AesException extends Exception { + + public final static int OK = 0; + public final static int ValidateSignatureError = -40001; + public final static int ParseXmlError = -40002; + public final static int ComputeSignatureError = -40003; + public final static int IllegalAesKey = -40004; + public final static int ValidateAppidError = -40005; + public final static int EncryptAESError = -40006; + public final static int DecryptAESError = -40007; + public final static int IllegalBuffer = -40008; + public final static int EncodeBase64Error = -40009; + public final static int DecodeBase64Error = -40010; + public final static int GenReturnXmlError = -40011; + + private int code; + + private static String getMessage(int code) { + switch (code) { + case ValidateSignatureError: + return "签名验证错误"; + case ParseXmlError: + return "xml解析失败"; + case ComputeSignatureError: + return "sha加密生成签名失败"; + case IllegalAesKey: + return "SymmetricKey非法"; + case ValidateAppidError: + return "appid校验失败"; + case EncryptAESError: + return "aes加密失败"; + case DecryptAESError: + return "aes解密失败"; + case IllegalBuffer: + return "解密后得到的buffer非法"; + case EncodeBase64Error: + return "base64加密错误"; + case DecodeBase64Error: + return "base64解密错误"; + case GenReturnXmlError: + return "xml生成失败"; + default: + return null; + } + } + public int getCode() { + return code; + } + + public AesException(int code) { + super(getMessage(code)); + this.code = code; + } +} diff --git a/novel-front/src/main/java/com/java2nb/novel/core/utils/SHA1.java b/novel-front/src/main/java/com/java2nb/novel/core/utils/SHA1.java new file mode 100644 index 0000000..4f723fe --- /dev/null +++ b/novel-front/src/main/java/com/java2nb/novel/core/utils/SHA1.java @@ -0,0 +1,53 @@ +package com.java2nb.novel.core.utils; + +import com.java2nb.novel.core.except.AesException; + +import java.security.MessageDigest; +import java.util.Arrays; + +/** + * @author zhu + * @date 2023/5/9 + * @description + */ +public class SHA1 { + /** + * 用SHA1算法验证Token + * + * @param token 票据 + * @param timestamp 时间戳 + * @param nonce 随机字符串 + * @return 安全签名 + * @throws AesException + */ + public static String getSHA1(String token, String timestamp, String nonce) throws AesException { + try { + String[] array = new String[]{token, timestamp, nonce}; + StringBuffer sb = new StringBuffer(); + // 字符串排序 + Arrays.sort(array); + for (int i = 0; i < 3; i++) { + sb.append(array[i]); + } + String str = sb.toString(); + // SHA1签名生成 + MessageDigest md = MessageDigest.getInstance("SHA-1"); + md.update(str.getBytes()); + byte[] digest = md.digest(); + + StringBuffer hexstr = new StringBuffer(); + String shaHex = ""; + for (int i = 0; i < digest.length; i++) { + shaHex = Integer.toHexString(digest[i] & 0xFF); + if (shaHex.length() < 2) { + hexstr.append(0); + } + hexstr.append(shaHex); + } + return hexstr.toString(); + } catch (Exception e) { + e.printStackTrace(); + throw new AesException(AesException.ComputeSignatureError); + } + } +} diff --git a/novel-front/src/main/java/com/java2nb/novel/core/utils/WXPublicUtils.java b/novel-front/src/main/java/com/java2nb/novel/core/utils/WXPublicUtils.java new file mode 100644 index 0000000..9550ee6 --- /dev/null +++ b/novel-front/src/main/java/com/java2nb/novel/core/utils/WXPublicUtils.java @@ -0,0 +1,30 @@ +package com.java2nb.novel.core.utils; + +import com.java2nb.novel.core.except.AesException; + +/** + * @author zhu + * @date 2023/5/9 + * @description + */ +public class WXPublicUtils { + + /** + * 验证Token + * @param msgSignature 签名串,对应URL参数的signature + * @param timeStamp 时间戳,对应URL参数的timestamp + * @param nonce 随机串,对应URL参数的nonce + * + * @return 是否为安全签名 + * @throws AesException 执行失败,请查看该异常的错误码和具体的错误信息 + */ + public static boolean verifyUrl(String msgSignature, String timeStamp, String nonce) + throws AesException { + // 这里的 WXPublicConstants.TOKEN 填写你自己设置的Token就可以了 + String signature = SHA1.getSHA1("zyb707099", timeStamp, nonce); + if (!signature.equals(msgSignature)) { + throw new AesException(AesException.ValidateSignatureError); + } + return true; + } +} diff --git a/novel-front/src/main/java/com/java2nb/novel/dto/WxMessageDto.java b/novel-front/src/main/java/com/java2nb/novel/dto/WxMessageDto.java new file mode 100644 index 0000000..acc4dcf --- /dev/null +++ b/novel-front/src/main/java/com/java2nb/novel/dto/WxMessageDto.java @@ -0,0 +1,95 @@ +package com.java2nb.novel.dto; + +import lombok.Data; + +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlElement; +import javax.xml.bind.annotation.XmlRootElement; + +/** + * @author zhu + * @date 2023/5/9 + * @description + */ +@Data +@XmlRootElement(name = "xml") +@XmlAccessorType(XmlAccessType.FIELD) +public class WxMessageDto { + /** + * 开发者微信号 + */ + @XmlElement(name = "ToUserName") + private String toUserName; + + /** + * 发送方帐号(一个OpenID) + */ + @XmlElement(name = "FromUserName") + private String fromUserName; + + /** + * 消息创建时间 (整型) + */ + @XmlElement(name = "CreateTime") + private String createTime; + + /** + * 消息类型,文本为text + */ + @XmlElement(name = "MsgType") + private String msgType; + + /** + * 文本消息内容 + */ + @XmlElement(name = "Content") + private String content; + + /** + * 消息id,64位整型 + */ + @XmlElement(name = "MsgId") + private String msgId; + + /** + * 消息的数据id + */ + @XmlElement(name = "MsgDataId") + private String msgDataId; + + /** + * 多图文时第几篇文章,从1开始(消息如果来自文章时才有) + */ + @XmlElement(name = "Idx") + private String idx; + + /** + * 图片链接(由系统生成) + */ + @XmlElement(name = "PicUrl") + private String picUrl; + + /** + * 图片消息媒体id,可以调用获取临时素材接口拉取数据。 + */ + @XmlElement(name = "MediaId") + private String mediaId; + + /** + * 语音格式:amr + */ + @XmlElement(name = "Format") + private String format; + /** + * 语音识别结果,UTF8编码 + */ + @XmlElement(name = "Recognition") + private String recognition; + + /** + * 视频消息缩略图的媒体id,可以调用多媒体文件下载接口拉取数据。 + */ + @XmlElement(name = "ThumbMediaId") + private String thumbMediaId; +} diff --git a/novel-front/src/main/java/com/java2nb/novel/service/WeChatService.java b/novel-front/src/main/java/com/java2nb/novel/service/WeChatService.java new file mode 100644 index 0000000..0a03b2b --- /dev/null +++ b/novel-front/src/main/java/com/java2nb/novel/service/WeChatService.java @@ -0,0 +1,30 @@ +package com.java2nb.novel.service; + +import com.java2nb.novel.core.except.AesException; +import com.java2nb.novel.dto.WxMessageDto; +import org.springframework.web.bind.annotation.RequestBody; + +import javax.servlet.http.HttpServletRequest; + +/** + * @author zhu + * @date 2023/5/9 + * @description + */ +public interface WeChatService { + /** + * + * @param request + * @return + * @throws AesException + */ + String weCheck(HttpServletRequest request) throws AesException; + + /** + * + * @param request + * @param dto + * @return + */ + String getMessage(HttpServletRequest request, WxMessageDto dto); +} diff --git a/novel-front/src/main/java/com/java2nb/novel/service/impl/WeChatServiceImpl.java b/novel-front/src/main/java/com/java2nb/novel/service/impl/WeChatServiceImpl.java new file mode 100644 index 0000000..e578e7c --- /dev/null +++ b/novel-front/src/main/java/com/java2nb/novel/service/impl/WeChatServiceImpl.java @@ -0,0 +1,68 @@ +package com.java2nb.novel.service.impl; + +import cn.hutool.core.util.XmlUtil; +import com.java2nb.novel.core.except.AesException; +import com.java2nb.novel.core.utils.WXPublicUtils; +import com.java2nb.novel.dto.WxMessageDto; +import com.java2nb.novel.service.WeChatService; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; +import org.w3c.dom.Document; + +import javax.servlet.http.HttpServletRequest; + +/** + * @author zhu + * @date 2023/5/9 + * @description + */ +@Service +@Slf4j +public class WeChatServiceImpl implements WeChatService { + @Override + public String weCheck(HttpServletRequest request) throws AesException { + String msgSignature = request.getParameter("signature"); + String msgTimestamp = request.getParameter("timestamp"); + String msgNonce = request.getParameter("nonce"); + String echostr = request.getParameter("echostr"); + if (WXPublicUtils.verifyUrl(msgSignature, msgTimestamp, msgNonce)) { + return echostr; + } + return null; + } + + @Override + public String getMessage(HttpServletRequest request, WxMessageDto dto) { + String msgSignature = request.getParameter("signature"); + String msgTimestamp = request.getParameter("timestamp"); + String msgNonce = request.getParameter("nonce"); + String echostr = request.getParameter("echostr"); + StringBuilder sb = new StringBuilder("\n" + + " \n" + + " \n" + + " "+dto.getCreateTime()+"\n"); + switch (dto.getMsgType()) { + case "text": + //处理文本消息 + disposeText(sb, dto.getContent()); + break; + default: + break; + } + sb.append(""); + return sb.toString(); + } + + private void disposeText(StringBuilder sb, String content) { + //查询是否存在该返回信息逻辑 + //无逻辑,返回菜单 + String stringBuilder = "你发的信息小仙无法识别嗷!\n" + + "请回复以下数字:\n" + + "1. 北京\n" + + "2. 上海\n" + + "3. 武汉\n" + + "4. 小说"; + sb.append("\n"); + sb.append("\n"); + } +}