pom.xml
<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.company</groupId> <artifactId>javademo</artifactId> <version>0.0.1-SNAPSHOT</version> <packaging>jar</packaging> <name>javademo</name> <url>http://maven.apache.org</url> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <maven.compiler.source>1.8</maven.compiler.source> <maven.compiler.target>1.8</maven.compiler.target> </properties> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>1.5.9.RELEASE</version> <relativePath /> </parent> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>com.google.guava</groupId> <artifactId>guava</artifactId> <version>23.0</version> </dependency> <dependency> <groupId>com.alibaba</groupId> <artifactId>fastjson</artifactId> <version>1.2.54</version> </dependency> <dependency> <groupId>org.apache.httpcomponents</groupId> <artifactId>httpclient</artifactId> </dependency> <dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-lang3</artifactId> <version>3.7</version> </dependency> <dependency> <groupId>commons-io</groupId> <artifactId>commons-io</artifactId> <version>2.4</version> </dependency> <dependency> <groupId>dom4j</groupId> <artifactId>dom4j</artifactId> </dependency> <dependency> <groupId>jaxen</groupId> <artifactId>jaxen</artifactId> </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <scope>test</scope> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> <executions> <execution> <goals> <goal>repackage</goal> </goals> </execution> </executions> </plugin> </plugins> </build> </project>
application.properties
server.port=80
logging.path=./logs/
WeiXinUtils.java
package com.company.javademo; import java.util.ArrayList; import java.util.List; import java.util.concurrent.TimeUnit; import org.apache.commons.lang3.StringUtils; import org.apache.http.HttpEntity; import org.apache.http.client.methods.CloseableHttpResponse; import org.apache.http.client.methods.HttpGet; import org.apache.http.client.methods.HttpPost; import org.apache.http.entity.StringEntity; import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClients; import org.apache.http.util.EntityUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.alibaba.fastjson.JSONObject; import com.google.common.cache.Cache; import com.google.common.cache.CacheBuilder; import com.google.common.collect.Lists; /** * 依赖以下jar包 * <dependency> * <groupId>com.google.guava</groupId> * <artifactId>guava</artifactId> * <version>23.0</version> * </dependency> * <dependency> * <groupId>com.alibaba</groupId> * <artifactId>fastjson</artifactId> * <version>1.2.54</version> * </dependency> * <dependency> * <groupId>org.apache.httpcomponents</groupId> * <artifactId>httpclient</artifactId> * <version>4.5.6</version> * </dependency> */ /** * 微信操作帮助类 */ public class WeiXinUtils { static Logger logger = LoggerFactory.getLogger(WeiXinUtils.class); /** * 微信用户基本信息 */ static class UserInfo { public Integer subscribe; public String openid; public String nickname; public Integer sex; public String language; public String city; public String province; public String country; public String headimgurl; public Long subscribe_time; public String unionid; public String remark; public Integer groupid; public List<Integer> tagid_list; public String subscribe_scene; public Integer qr_scene; public String qr_scene_str; } /** * 批量获取用户基本信息 * * @param access_token * @param openids * @return */ public static List<UserInfo> batchgetUsers(String access_token, List<String> openids) { class UserOpenId { public String openid; public String lang = "zh_CN"; public UserOpenId(String openid) { this.openid = openid; this.lang = "zh_CN"; } } class UserInfoBatchGetRequest { public List<UserOpenId> user_list; public UserInfoBatchGetRequest(List<UserOpenId> user_list) { this.user_list = user_list; } } class UserInfoBatchGetResponse { public List<UserInfo> user_info_list; } List<UserOpenId> userOpenIds = null; List<List<String>> parts = Lists.partition(openids, 100); List<UserInfo> userInfos = new ArrayList<UserInfo>(); for (List<String> part : parts) { userOpenIds = new ArrayList<UserOpenId>(); for (String openid : part) { userOpenIds.add(new UserOpenId(openid)); } String url = "https://api.weixin.qq.com/cgi-bin/user/info/batchget?access_token=" + access_token; UserInfoBatchGetRequest request = new UserInfoBatchGetRequest(userOpenIds); String res = httpPost(url, JSONObject.toJSONString(request)); UserInfoBatchGetResponse response = JSONObject.parseObject(res, UserInfoBatchGetResponse.class); userInfos.addAll(response.user_info_list); } return userInfos; } /** * 获取用户列表 * * @param access_token * @return */ public static List<String> userGet(String access_token) { String url = "https://api.weixin.qq.com/cgi-bin/user/get?access_token=" + access_token + "&next_openid="; class UserGetResponseData { public List<String> openid; } class UserGetResponse { public int total; public int count; public UserGetResponseData data; public String next_openid; } List<String> openids = new ArrayList<String>(); int total = 0; String next_openid = ""; do { url = url + next_openid; String res = httpGet(url); UserGetResponse response = JSONObject.parseObject(res, UserGetResponse.class); openids.addAll(response.data.openid); total = response.total; next_openid = response.next_openid; } while (openids.size() < total); return openids; } /** * 永久素材总数 */ static class GetMaterialcountResponse { public int voice_count; public int video_count; public int image_count; public int news_count; } /** * 获取素材总数 * * @param access_token * @return */ public static GetMaterialcountResponse getMaterialcount(String access_token) { String url = "https://api.weixin.qq.com/cgi-bin/material/get_materialcount?access_token="; String res = httpGet(url); GetMaterialcountResponse response = JSONObject.parseObject(res, GetMaterialcountResponse.class); return response; } public static class MaterialNewsItem { public String title; public String thumb_media_id; public int show_cover_pic; public String author; public String digest; public String content; public String url; public String content_source_url; } public static class MaterialNewsContent { public List<MaterialNewsItem> news_item; } public static class MaterialNews { public String media_id; public MaterialNewsContent content; public Long update_time; } public static class Material { public String media_id; public String name; public Long update_time; public String url; } /** * 批量获取图文素材 * * @param access_token * @return */ public static List<MaterialNews> batchgetMaterialNews(String access_token) { class Request { public String type; public int offset; public int count; public Request(String type, int offset, int count) { this.type = type; this.offset = offset; this.count = count; } } class Response { public int total_count; public int item_count; public List<MaterialNews> item; } int start = 0; int size = 20; int total = 0; List<MaterialNews> materialNewsList = new ArrayList<MaterialNews>(); do { String url = "https://api.weixin.qq.com/cgi-bin/material/batchget_material?access_token=" + access_token; Request request = new Request("news", start, size); String res = httpPost(url, JSONObject.toJSONString(request)); Response response = JSONObject.parseObject(res, Response.class); total = response.total_count; materialNewsList.addAll(response.item); start = start + size; } while (start < total); return materialNewsList; } /** * 批量获取其它素材 * * @param access_token * @return */ public static List<Material> batchgetMaterial(String access_token, String type) { class Request { public String type; public int offset; public int count; public Request(String type, int offset, int count) { this.type = type; this.offset = offset; this.count = count; } } class Response { public int total_count; public int item_count; public List<Material> item; } int start = 0; int size = 20; int total = 0; List<Material> materialList = new ArrayList<Material>(); do { String url = "https://api.weixin.qq.com/cgi-bin/material/batchget_material?access_token=" + access_token; Request request = new Request(type, start, size); String res = httpPost(url, JSONObject.toJSONString(request)); Response response = JSONObject.parseObject(res, Response.class); total = response.total_count; materialList.addAll(response.item); start = start + size; } while (start < total); return materialList; } // guava缓存 static Cache<String, String> guavaCache = CacheBuilder.newBuilder().maximumSize(100) .expireAfterWrite(30, TimeUnit.MINUTES).concurrencyLevel(10).recordStats().build(); /** * 获取accessToken * * @param appid * @param appsecret * @return */ public static String getAccessToken(String appid, String appsecret) { String cacheKey = appid + "_access_token"; String access_token = guavaCache.getIfPresent(cacheKey); if (StringUtils.isEmpty(access_token)) { String url = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=" + appid + "&secret=" + appsecret; class Response { public String access_token; public int expires_in; } String res = httpGet(url); Response response = JSONObject.parseObject(res, Response.class); access_token = response.access_token; guavaCache.put(cacheKey, access_token); } return access_token; } /** * 获取授权url 如果用户同意授权,页面将跳转至 redirect_uri/?code=CODE&state=STATE * * @param appid * @param redirectUrl * @param state * @return */ public static String getAuthorize(String appid, String redirectUrl, String state) { String url = "https://open.weixin.qq.com/connect/oauth2/authorize?appid=" + appid + "&redirect_uri=" + redirectUrl + "&response_type=code&scope=snsapi_userinfo&state=" + state + "#wechat_redirect"; return url; } /** * 网页授权accesstoken */ static class AuthorizationAccessToken { public String access_token; public int expires_in; public String refresh_token; public String openid; public String scope; } /** * 通过网页code换取授权accesstoken * * @param appid * @param appsecret * @param code * @return */ public static AuthorizationAccessToken getAccessTokenByCode(String appid, String appsecret, String code) { String url = "https://api.weixin.qq.com/sns/oauth2/access_token?appid=" + appid + "&secret=" + appsecret + "&code=" + code + "&grant_type=authorization_code"; String res = httpGet(url); AuthorizationAccessToken response = JSONObject.parseObject(res, AuthorizationAccessToken.class); return response; } /** * 网页授权码用户返回信息 */ static class AuthorizationUserInfo { public String openid; public String nickname; public String sex; public String province; public String city; public String country; public String headimgurl; // 若用户更换头像,原有头像URL将失效 public List<String> privilege; public String unionid; // 绑定到微信开放平台帐号后,才会出现该字段 } /** * 通过授权码token获取用户信息 * * @param authorization_access_token * @param openid * @return */ public static AuthorizationUserInfo getAuthorizationUserInfo(String authorization_access_token, String openid) { String url = "https://api.weixin.qq.com/sns/userinfo?access_token=" + authorization_access_token + "&openid=" + openid + "&lang=zh_CN"; String res = httpGet(url); AuthorizationUserInfo response = JSONObject.parseObject(res, AuthorizationUserInfo.class); return response; } public static String httpPost(String url, String jsonBody) { CloseableHttpClient httpClient = HttpClients.createDefault(); HttpPost httpPost = new HttpPost(url); httpPost.addHeader("Content-Type", "application/json"); StringEntity reqEntity = new StringEntity(jsonBody, "utf-8"); httpPost.setEntity(reqEntity); CloseableHttpResponse response = null; String result = null; try { logger.info("post url :" + url + ", body :" + jsonBody); response = httpClient.execute(httpPost); logger.info("post response status :" + response.getStatusLine()); HttpEntity resEntity = response.getEntity(); result = EntityUtils.toString(resEntity, "UTF-8"); logger.info("post response body :" + result); } catch (Exception e) { e.printStackTrace(); } return result; } public static String httpGet(String url) { CloseableHttpClient httpClient = HttpClients.createDefault(); HttpGet httpGet = new HttpGet(url); CloseableHttpResponse response = null; String result = null; try { logger.info("get url :" + url); response = httpClient.execute(httpGet); logger.info("get response status :" + response.getStatusLine()); HttpEntity resEntity = response.getEntity(); result = EntityUtils.toString(resEntity, "UTF-8"); logger.info("get response body :" + result); } catch (Exception e) { e.printStackTrace(); } return result; } }
WeiXinCallbackController.java
package com.company.javademo; import java.io.IOException; import java.util.Arrays; import java.util.List; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.apache.commons.codec.digest.DigestUtils; import org.apache.commons.io.IOUtils; import org.dom4j.Document; import org.dom4j.DocumentException; import org.dom4j.DocumentHelper; import org.dom4j.Node; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.ResponseBody; @Controller @RequestMapping("/weixin") public class WeiXinCallbackController { @GetMapping(value = "/callback") @ResponseBody public String checkSignature(String signature, String timestamp, String nonce, String echostr) { String token = "mytoken"; List<String> list = Arrays.asList(token, timestamp, nonce); list.sort(null); String tmpStr = DigestUtils.sha1Hex(String.join("", list)); if (tmpStr.equals(signature)) { return echostr; } return null; } @PostMapping("/callback") @ResponseBody public String sendWxMessage(HttpServletRequest request , HttpServletResponse response) throws IOException, DocumentException { String xml = IOUtils.toString(request.getInputStream()); // SAXReader saxReader = new SAXReader(); // Document document = saxReader.read(request.getInputStream()); Document document = DocumentHelper.parseText(xml); Node nodeMsgType = document.selectSingleNode("//MsgType"); String msgType = nodeMsgType.getText(); //处理文本消息 if ("text".equals(msgType)) { String fromUser = document.selectSingleNode("//FromUserName").getText(); String toUser = document.selectSingleNode("//ToUserName").getText(); String content = document.selectSingleNode("//Content").getText(); String createTime = document.selectSingleNode("//CreateTime").getText(); String msgId = document.selectSingleNode("//MsgId").getText(); String answerMsg="<xml>"+ " <ToUserName><![CDATA["+fromUser+"]]></ToUserName>"+ " <FromUserName><![CDATA["+toUser+"]]></FromUserName>" + " <CreateTime>"+createTime+"</CreateTime>" + " <MsgType><![CDATA["+msgType+"]]></MsgType>" + " <Content><![CDATA["+content+"]]></Content>" + " </xml>"; return answerMsg; } else { System.out.println("非文本字段暂时不做处理"); return "success"; } } }
App.java
package com.company.javademo; import java.util.List; import org.springframework.boot.Banner; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import com.alibaba.fastjson.JSONObject; import com.company.javademo.WeiXinUtils.Material; import com.company.javademo.WeiXinUtils.MaterialNews; /** * Hello world! * */ @SpringBootApplication public class App { public static void main(String[] args) { SpringApplication app = new SpringApplication(App.class); // 关闭banner app.setBannerMode(Banner.Mode.OFF); app.run(args); String appid = "appid"; String appsecret = "appsecret"; String access_token = WeiXinUtils.getAccessToken(appid, appsecret); List<MaterialNews> materialNewsList = WeiXinUtils.batchgetMaterialNews(access_token); System.out.println(JSONObject.toJSONString(materialNewsList)); List<Material> materialList = WeiXinUtils.batchgetMaterial(access_token,"image"); System.out.println(JSONObject.toJSONString(materialList)); } }