上传微信公众号

master
YangPeng 2024-09-20 10:28:08 +08:00
commit 3a39e06292
26 changed files with 930 additions and 0 deletions

38
.gitignore vendored 100644
View File

@ -0,0 +1,38 @@
target/
!.mvn/wrapper/maven-wrapper.jar
!**/src/main/**/target/
!**/src/test/**/target/
### IntelliJ IDEA ###
.idea/modules.xml
.idea/jarRepositories.xml
.idea/compiler.xml
.idea/libraries/
*.iws
*.iml
*.ipr
### Eclipse ###
.apt_generated
.classpath
.factorypath
.project
.settings
.springBeans
.sts4-cache
### NetBeans ###
/nbproject/private/
/nbbuild/
/dist/
/nbdist/
/.nb-gradle/
build/
!**/src/main/**/build/
!**/src/test/**/build/
### VS Code ###
.vscode/
### Mac OS ###
.DS_Store

8
.idea/.gitignore vendored 100644
View File

@ -0,0 +1,8 @@
# 默认忽略的文件
/shelf/
/workspace.xml
# 基于编辑器的 HTTP 客户端请求
/httpRequests/
# Datasource local storage ignored files
/dataSources/
/dataSources.local.xml

View File

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="Encoding">
<file url="file://$PROJECT_DIR$/src/main/java" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/src/main/resources" charset="UTF-8" />
</component>
</project>

View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="GitToolBoxBlameSettings">
<option name="version" value="2" />
</component>
</project>

View File

@ -0,0 +1,12 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="MaterialThemeProjectNewConfig">
<option name="metadata">
<MTProjectMetadataState>
<option name="migrated" value="true" />
<option name="pristineConfig" value="false" />
<option name="userId" value="543722b8:191dbece87f:-7ffe" />
</MTProjectMetadataState>
</option>
</component>
</project>

14
.idea/misc.xml 100644
View File

@ -0,0 +1,14 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ExternalStorageConfigurationManager" enabled="true" />
<component name="MavenProjectsManager">
<option name="originalFiles">
<list>
<option value="$PROJECT_DIR$/pom.xml" />
</list>
</option>
</component>
<component name="ProjectRootManager" version="2" languageLevel="JDK_17" default="true" project-jdk-name="17" project-jdk-type="JavaSDK">
<output url="file://$PROJECT_DIR$/out" />
</component>
</project>

6
.idea/vcs.xml 100644
View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="$PROJECT_DIR$" vcs="Git" />
</component>
</project>

68
pom.xml 100644
View File

@ -0,0 +1,68 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.muyu</groupId>
<artifactId>cloud-common</artifactId>
<version>1.0-SNAPSHOT</version>
<parent>
<groupId>com.muyu</groupId>
<artifactId>cloud-modules</artifactId>
<version>3.6.3</version>
</parent>
<properties>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<dependency>
<groupId>com.squareup.okhttp3</groupId>
<artifactId>okhttp</artifactId>
<version>4.9.3</version>
</dependency>
<dependency>
<groupId>com.thoughtworks.xstream</groupId>
<artifactId>xstream</artifactId>
<version>1.4.20</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.34</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
<version>3.3.2</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>3.3.2</version>
</dependency>
<!-- DOM4J是 dom4j.org 出品的一个开源XML解析包-->
<dependency>
<groupId>org.dom4j</groupId>
<artifactId>dom4j</artifactId>
<version>2.1.3</version>
</dependency>
<dependency>
<groupId>com.alibaba.fastjson2</groupId>
<artifactId>fastjson2</artifactId>
<version>2.0.43</version>
</dependency>
<!-- <dependency>-->
<!-- <groupId>com.muyu</groupId>-->
<!-- <artifactId>cloud-common-redis</artifactId>-->
<!-- </dependency>-->
</dependencies>
</project>

View File

@ -0,0 +1,18 @@
package com.muyu;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
/**
* @className: WxApplication
* @author: Yang 🦅
* @date: 2024/9/16 20:39
* @Version: 1.0
* @description:
*/
@SpringBootApplication
public class WxApplication {
public static void main(String[] args) {
SpringApplication.run(WxApplication.class);
}
}

View File

@ -0,0 +1,28 @@
package com.muyu.button;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* @className: AbstractButton
* @author: Yang 🦅
* @date: 2024/9/18 20:57
* @Version: 1.0
* @description:
*/
public abstract class AbstractButton {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public AbstractButton(String name) {
this.name = name;
}
}

View File

@ -0,0 +1,18 @@
package com.muyu.button;
import lombok.Data;
import java.util.List;
/**
* @className: Button
* @author: Yang 🦅
* @date: 2024/9/18 20:53
* @Version: 1.0
* @description:
*/
@Data
public class Button {
private List<AbstractButton> button;
}

View File

@ -0,0 +1,32 @@
package com.muyu.button;
/**
* @className: ClickButton
* @author: Yang 🦅
* @date: 2024/9/18 21:00
* @Version: 1.0
* @description:
*/
public class ClickButton extends AbstractButton{
public ClickButton(String name) {
super(name);
this.type="click";
}
private String type;
private String key;
public String getType() {
return type;
}
public String getKey() {
return key;
}
public void setKey(String key) {
this.key = key;
}
}

View File

@ -0,0 +1,30 @@
package com.muyu.button;
/**
* @className: PhotoOrAlbum
* @author: Yang 🦅
* @date: 2024/9/18 21:18
* @Version: 1.0
* @description:
*/
public class PhotoOrAlbumButton extends AbstractButton{
public PhotoOrAlbumButton(String name) {
super(name);
this.type="pic_photo_or_album";
}
private String type;
private String key;
public String getType() {
return type;
}
public String getKey() {
return key;
}
public void setKey(String key) {
this.key = key;
}
}

View File

@ -0,0 +1,25 @@
package com.muyu.button;
import java.util.List;
/**
* @className: SubButton
* @author: Yang 🦅
* @date: 2024/9/18 21:24
* @Version: 1.0
* @description:
*/
public class SubButton extends AbstractButton{
public SubButton(String name) {
super(name);
}
private List<AbstractButton> sub_button;
public List<AbstractButton> getSub_button() {
return sub_button;
}
public void setSub_button(List<AbstractButton> sub_button) {
this.sub_button = sub_button;
}
}

View File

@ -0,0 +1,29 @@
package com.muyu.button;
import java.util.ArrayList;
/**
* @className: TestButton
* @author: Yang 🦅
* @date: 2024/9/18 22:20
* @Version: 1.0
* @description:
*/
public class TestButton {
public static void main(String[] args) {
//创建一级菜单
Button button = new Button();
ArrayList<AbstractButton> buttons = new ArrayList<>();
//一级菜单中的第一个按钮
ClickButton clickButton = new ClickButton("授权");
clickButton.setKey("1");
//一级菜单中的第二个按钮
button.setButton(buttons);
}
}

View File

@ -0,0 +1,30 @@
package com.muyu.button;
/**
* @className: ViewButton
* @author: Yang 🦅
* @date: 2024/9/18 21:14
* @Version: 1.0
* @description:
*/
public class ViewButton extends AbstractButton{
public ViewButton(String name,String url) {
super(name);
this.type="view";
this.url = url;
}
private String type;
private String url;
public String getType() {
return type;
}
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
}

View File

@ -0,0 +1,202 @@
package com.muyu.controller;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.muyu.domain.Message;
import com.muyu.service.WxService;
import com.muyu.util.OkHttpUtils;
import com.muyu.util.TokenUtil;
import com.muyu.util.WechatUtil;
import com.thoughtworks.xstream.XStream;
import io.micrometer.common.util.StringUtils;
import jakarta.servlet.ServletInputStream;
import jakarta.servlet.http.HttpServletRequest;
import okhttp3.*;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import java.io.IOException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.*;
/**
* @className: GetWxController
* @author: Yang 🦅
* @date: 2024/9/16 20:38
* @Version: 1.0
* @description:
*/
@RestController
public class WxController {
// @Autowired
// private RedisService redisService;
@Autowired
private WxService wxService;
/**
* ,
* @param signature
* @param timestamp
* @param nonce
* @param echostr
* @return
*/
@GetMapping("/")
public String check(@RequestParam("signature") String signature,@RequestParam("timestamp") String timestamp ,
@RequestParam("nonce") String nonce, @RequestParam("echostr") String echostr){
String token ="zzhdzt";
List<String> list = Arrays.asList(token, timestamp, nonce);
Collections.sort(list);
StringBuilder stringBuilder = new StringBuilder();
for (String s : list) {
stringBuilder.append(s);
}
try {
MessageDigest instance = MessageDigest.getInstance("sha1");
//使用sha1进行加密,获得byte数组
byte[] digest = instance.digest(stringBuilder.toString().getBytes());
StringBuilder sum = new StringBuilder();
for (byte b : digest) {
sum.append(Integer.toHexString((b>>4)&15));
sum.append(Integer.toHexString(b&15));
}
if (!StringUtils.isEmpty(signature) && signature.equals(sum.toString())){
return echostr;
}
} catch (NoSuchAlgorithmException e) {
throw new RuntimeException(e);
}
return null;
}
/**
*
* @param request
* @return
*/
@PostMapping("/")
public String receiveMessage(HttpServletRequest request){
String message = wxService.handleMessage(request);
return message;
}
@GetMapping("/getSignUpUserInfo")
public String getSignUpUserInfo(HttpServletRequest request){
String code = request.getParameter("code");
return wxService.getSignUpUserInfo(code);
}
@GetMapping("/send")
public void sendWxMsg(@RequestParam("toUser") String toUser,@RequestParam("content") String content) {
WechatUtil.send(toUser,content);
}
// @GetMapping("/getWxUserList")
// public List getUserList(){
// String accessToken = TokenUtil.getAccessToken();
// String url="https://api.weixin.qq.com/cgi-bin/user/get?access_token="+accessToken;
// String request = OkHttpUtils.sendGetRequest(url);
//
// try {
// ObjectMapper mapper = new ObjectMapper();
// JsonNode rootNode = mapper.readTree(request);
//
// int total = rootNode.get("total").asInt();
// int count = rootNode.get("count").asInt();
// JsonNode itemsNode = rootNode.get("data");
// JsonNode openIdNode = itemsNode.get("openid");
// List<String> openids = new ArrayList<>();
//
// // 遍历 JSON 数组中的每个元素
// for (JsonNode item : openIdNode) {
// // 添加到集合中
// openids.add(item.asText());
// }
//
// String nextOpenid = openids.isEmpty() ? null : openids.get(openids.size() - 1);
//
// // 构建最终的JSON对象
// Map<String, Object> map = new HashMap<>();
// map.put("total", total);
// map.put("count", count);
// Map<String, List<String>> data = new HashMap<>();
// data.put("openid", openids);
// map.put("data", data);
// map.put("next_openid", nextOpenid);
//
// // 根据openid获取用户信息
// for (String openid : openids) {
// String getUserUrl = "https://api.weixin.qq.com/cgi-bin/user/info?access_token=" + accessToken + "&openid=" + openid + "&lang=zh_CN";
// String userInfoRequest = OkHttpUtils.sendGetRequest(getUserUrl);
//
// // 解析用户信息
// JsonNode userInfoNode = mapper.readTree(userInfoRequest);
// System.out.println("用户信息: " + userInfoNode);
// }
// } catch (IOException e) {
// // 处理解析错误
// e.printStackTrace();
// }
// return null;
// }
// @PostMapping("/test")
// public void test(){
// String accessToken = TokenUtil.getAccessToken();
// System.out.println(accessToken);
// }
// String message="<xml><ToUserName><![CDATA[gh_746d58eabb43]]></ToUserName><FromUserName><![CDATA[oUjOm6hQ2WPWn1d8YO2-ovVJpfdE]]></FromUserName><CreateTime>12345678</CreateTime><MsgType><![CDATA[text]]></MsgType> <Content><![CDATA[你好]]></Content></xml>";
// @PostMapping("/")
// public String receiveMessage(HttpServletRequest request){
//
//
//
// try {
// ServletInputStream inputStream = null;
// inputStream = request.getInputStream();
//
// byte[] bytes = new byte[1024];
// int len = 0;
// while ((len=inputStream.read(bytes))!=-1){
// System.out.println(new String(bytes, 0, len));
// }
//
// } catch (IOException e) {
// throw new RuntimeException(e);
// }
//
// return "";
// }
}

View File

@ -0,0 +1,28 @@
package com.muyu.domain;
import lombok.Data;
/**
* @className: AccessToken
* @author: Yang 🦅
* @date: 2024/9/17 16:49
* @Version: 1.0
* @description:
*/
@Data
public class AccessToken {
private String access_token;
private Long expires_in;
public void setExpiresTime(Long expiresIn) {
this.expires_in = System.currentTimeMillis()+expiresIn*1000;
}
// public boolean isExpired(Long expiresIn){
// long now = System.currentTimeMillis();
// return now>expiresIn;
// }
}

View File

@ -0,0 +1,33 @@
package com.muyu.domain;
import com.thoughtworks.xstream.annotations.XStreamAlias;
import lombok.Data;
/**
* @className: Message
* @author: Yang 🦅
* @date: 2024/9/17 16:49
* @Version: 1.0
* @description:
*/
@Data
@XStreamAlias("xml")
public class Message {
@XStreamAlias("ToUserName")
private String toUserName;
@XStreamAlias("FromUserName")
private String fromUserName;
@XStreamAlias("CreateTime")
private Long createTime;
@XStreamAlias("MsgType")
private String msgType;
@XStreamAlias("Content")
private String content;
}

View File

@ -0,0 +1,26 @@
package com.muyu.domain;
import lombok.Data;
/**
* @className: SubscribedUser
* @author: Yang 🦅
* @date: 2024/9/18 15:11
* @Version: 1.0
* @description:
*/
@Data
public class SubscribedUser {
private String openid;
private String nickname;
private Integer sex;
private String province;
private String city;
private String country;
private String headimgurl;
private Long subscribedTime;
// private String privilege;
// private String unionid;
}

View File

@ -0,0 +1,125 @@
package com.muyu.service.Impl;
import com.alibaba.fastjson2.JSONObject;
import com.muyu.domain.Message;
import com.muyu.domain.SubscribedUser;
import com.muyu.service.WxService;
import com.muyu.util.OkHttpUtils;
import com.muyu.util.TokenUtil;
import com.muyu.util.WechatUtil;
import com.thoughtworks.xstream.XStream;
import jakarta.servlet.ServletInputStream;
import jakarta.servlet.http.HttpServletRequest;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import java.io.IOException;
import java.util.HashMap;
import java.util.List;
/**
* @className: WxServiceImpl
* @author: Yang 🦅
* @date: 2024/9/18 12:05
* @Version: 1.0
* @description:
*/
@Service
public class WxServiceImpl implements WxService {
/**
*
* @param request
* @return
*/
@Override
public String handleMessage(HttpServletRequest request) {
ServletInputStream inputStream = null;
HashMap<String, String> map = new HashMap<>();
try {
inputStream = request.getInputStream();
SAXReader saxReader = new SAXReader();
Document document = saxReader.read(inputStream);
Element rootElement = document.getRootElement();
List<Element> elements = rootElement.elements();
for (Element element : elements) {
map.put(element.getName(),element.getStringValue());
}
} catch (IOException e) {
throw new RuntimeException(e);
} catch (DocumentException e) {
throw new RuntimeException(e);
}
System.out.println("接受消息:"+map);
String replyMessage = getReplyMessage(map);
System.out.println("返回消息:"+replyMessage);
return replyMessage;
}
@Override
public String getSignUpUserInfo(String code) {
String appId = TokenUtil.APP_ID;
String appSecret = TokenUtil.APP_SECRET;
String url= String.format("https://api.weixin.qq.com/sns/oauth2/access_token?appid=%s&secret=%s&code=%s&grant_type=authorization_code",appId,appSecret,code);
String request = OkHttpUtils.sendGetRequest(url);
String accessToken = JSONObject.parseObject(request).getString("access_token");
String openId = JSONObject.parseObject(request).getString("openid");
String userInfoUrl= String.format("https://api.weixin.qq.com/sns/userinfo?access_token=%s&openid=%s&lang=zh_CN",accessToken,openId);
String userInfo = OkHttpUtils.sendGetRequest(userInfoUrl);
SubscribedUser subscribedUser = JSONObject.parseObject(userInfo, SubscribedUser.class);
System.out.println(subscribedUser);
//TODO
// 添加数据库
WechatUtil.send(openId,"授权成功");
return "恭喜你,信息被盗取了";
}
/**
*
* @param map
* @return
*/
private String getReplyMessage(HashMap<String, String> map) {
Message message = new Message();
message.setToUserName(map.get("FromUserName"));
message.setFromUserName(map.get("ToUserName"));
// message.setMsgType(map.get("MsgType"));
message.setMsgType("text");
message.setCreateTime(System.currentTimeMillis()/1000);
String appId = TokenUtil.APP_ID;
String appSecret = TokenUtil.APP_SECRET;
String redirectDomain = TokenUtil.REDIRECT_DOMAIN;
//获取用户授权的url
String url = String.format("https://open.weixin.qq.com/connect/oauth2/authorize?appid=%s&redirect_uri=%s&response_type=code&scope=%s&state=STATE#wechat_redirect",appId,
"http://"+redirectDomain+"/getSignUpUserInfo","snsapi_userinfo") ;
message.setContent("点击给公众号授权链接 <a href=\""+url+"\">授权</a>");
XStream xStream =new XStream();
xStream.processAnnotations(Message.class);
String xml = xStream.toXML(message);
return xml;
}
}

View File

@ -0,0 +1,16 @@
package com.muyu.service;
import jakarta.servlet.http.HttpServletRequest;
/**
* @className: WxService
* @author: Yang 🦅
* @date: 2024/9/18 12:05
* @Version: 1.0
* @description:
*/
public interface WxService {
String handleMessage(HttpServletRequest request);
String getSignUpUserInfo(String code);
}

View File

@ -0,0 +1,36 @@
package com.muyu.util;
import okhttp3.*;
import java.io.IOException;
public class OkHttpUtils {
private static final OkHttpClient client = new OkHttpClient();
public static String sendGetRequest(String urlString) {
Request request = new Request.Builder()
.url(urlString)
.build();
try (Response response = client.newCall(request).execute()) {
return response.body().string();
} catch (IOException e) {
e.printStackTrace();
return null;
}
}
public static String sendPostRequest(String urlString, String params) {
RequestBody requestBody = RequestBody.create(params, MediaType.parse("application/json; charset=utf-8"));
Request request = new Request.Builder()
.url(urlString)
.post(requestBody)
.build();
try (Response response = client.newCall(request).execute()) {
return response.body().string();
} catch (IOException e) {
e.printStackTrace();
return null;
}
}
}

View File

@ -0,0 +1,44 @@
package com.muyu.util;
import com.alibaba.fastjson2.JSON;
import com.muyu.domain.AccessToken;
import org.springframework.stereotype.Component;
/**
* @className: TokenUtil
* @author: Yang 🦅
* @date: 2024/9/17 20:26
* @Version: 1.0
* @description:
*/
@Component
public class TokenUtil {
public final static String APP_ID = "wxa10c6f55da3c0553";
public final static String APP_SECRET ="72d285981f3f51274e79dcda5e498a81";
//域名
public final static String REDIRECT_DOMAIN ="3kknv4.natappfree.cc";
private static AccessToken accessToken = new AccessToken();
private static void getToken(){
String url = String.format("https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=%s&secret=%s",APP_ID,APP_SECRET);
String request = OkHttpUtils.sendGetRequest(url);
AccessToken wechatToken = JSON.parseObject(request, AccessToken.class);
//redisService.setCacheObject("WECHAT_TOKEN",wechatToken.getAccessToken(),wechatToken.getExpiresIn(), TimeUnit.SECONDS);
if (wechatToken != null) {
accessToken.setExpiresTime(wechatToken.getExpires_in());
accessToken.setAccess_token(wechatToken.getAccess_token());
}
}
public static String getAccessToken(){
getToken();
return accessToken.getAccess_token();
}
}

View File

@ -0,0 +1,39 @@
package com.muyu.util;
import io.micrometer.common.util.StringUtils;
import okhttp3.*;
import java.io.IOException;
public class WechatUtil {
public static void send(String toUser,String content) {
String accessToken = TokenUtil.getAccessToken();
if (!StringUtils.isBlank(accessToken)) {
String url = "https://api.weixin.qq.com/cgi-bin/message/custom/send?access_token=" + accessToken;
String jsonStr = "{\"touser\":\"" + toUser + "\",\"msgtype\":\"text\",\"text\":{\"content\":\"" + content + "\"}}";
OkHttpClient client = new OkHttpClient();
// 构建请求
RequestBody requestBody = RequestBody.create(jsonStr, MediaType.get("application/json; charset=utf-8"));
Request request = new Request.Builder()
.url(url)
.post(requestBody)
.addHeader("Content-Type", "application/json; charset=utf-8")
.build();
// 发送请求
try (Response response = client.newCall(request).execute()) {
if (!response.isSuccessful()) {
throw new IOException("Unexpected code " + response);
}
// 输出响应
System.out.println(response.body().string());
}catch (IOException e) {
e.printStackTrace();
}
}
}
}

View File

@ -0,0 +1,12 @@
# Tomcat
server:
port: 8088
##用户授权的重定向域名
#redirect:
# domain:123
##公众号appId和secret
#wx:
# appId:123
# secret:123