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");
+ }
+}