赞
踩
注册地址:https://mp.weixin.qq.com/cgi-bin/registermidpage?action=index&lang=zh_CN
注册流程参考:https://kf.qq.com/touch/faq/150804UVr222150804quq6B7.html?platform=15
用于服务配置
微信开发者文档:https://developers.weixin.qq.com/doc/offiaccount/Getting_Started/Overview.html
WxJava开源地址:https://gitee.com/binary/weixin-java-tools?_from=gitee_search
<dependency>
<groupId>com.github.binarywang</groupId>
<artifactId>weixin-java-mp</artifactId>
<version>4.5.0</version>
</dependency>
# 微信公众号配置
wx:
mp:
appId: wx1111111
appSecret: 111111111111
# 配置消息回调地址接入公众号时需要的token
token: add2222
aesKey: xxxx
server: https://www.baidu.com/
package com.qiangesoft.wechat.config; import lombok.Data; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.stereotype.Component; /** * 微信公众号相关配置属性 * * @author qiangesoft * @date 2023-09-07 */ @Data @Component @ConfigurationProperties(WxMpProperties.PREFIX) public class WxMpProperties { public static final String PREFIX = "wx.mp"; /** * 公众号开发信息:appId */ private String appId; /** * 公众号开发信息:appSecret */ private String appSecret; /** * 服务器配置:token */ private String token; /** * 服务器配置:消息加解密密钥EncodingAESKey */ private String aesKey; /** * 服务器配置:微信服务器地址 */ private String server; }
package com.qiangesoft.wechat.config; import com.qiangesoft.wechat.core.handler.LogHandler; import com.qiangesoft.wechat.core.handler.event.SubscribeHandler; import com.qiangesoft.wechat.core.handler.event.UnsubscribeHandler; import com.qiangesoft.wechat.core.handler.message.ImageHandler; import com.qiangesoft.wechat.core.handler.message.LocationHandler; import com.qiangesoft.wechat.core.handler.message.MessageHandler; import com.qiangesoft.wechat.core.handler.message.VoiceHandler; import com.qiangesoft.wechat.core.interceptor.MessageInterceptor; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import me.chanjar.weixin.common.api.WxConsts; import me.chanjar.weixin.mp.api.WxMpMessageRouter; import me.chanjar.weixin.mp.api.WxMpService; import me.chanjar.weixin.mp.api.impl.WxMpServiceImpl; import me.chanjar.weixin.mp.config.WxMpConfigStorage; import me.chanjar.weixin.mp.config.impl.WxMpDefaultConfigImpl; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; /** * 微信公众号配置 * * @author qiangesoft * @date 2023-09-07 */ @Slf4j @Configuration @RequiredArgsConstructor public class WxMpConfiguration { /** * 微信公众号配置信息 */ private final WxMpProperties wxMpProperties; /** * 日志处理器 */ private final LogHandler logHandler; /** * 订阅事件处理器 */ private final SubscribeHandler subscribeHandler; /** * 取消订阅事件处理器 */ private final UnsubscribeHandler unsubscribeHandler; /** * 语音消息处理器 */ private final VoiceHandler voiceHandler; /** * 文本消息处理器 */ private final MessageHandler messageHandler; /** * 图片消息处理器 */ private final ImageHandler imageHandler; /** * 位置消息处理器 */ private final LocationHandler locationHandler; /** * 消息拦截器 */ private final MessageInterceptor messageInterceptor; /** * 声明实例 * * @return */ @Bean public WxMpService wxMpService() { WxMpService wxMpService = new WxMpServiceImpl(); wxMpService.setWxMpConfigStorage(wxMpConfigStorage()); return wxMpService; } /** * 配置存储方式 * * @return */ @Bean public WxMpConfigStorage wxMpConfigStorage() { WxMpDefaultConfigImpl configStorage = new WxMpDefaultConfigImpl(); // 公众号appId configStorage.setAppId(wxMpProperties.getAppId()); // 公众号appSecret configStorage.setSecret(wxMpProperties.getAppSecret()); // 公众号Token configStorage.setToken(wxMpProperties.getToken()); // 公众号EncodingAESKey configStorage.setAesKey(wxMpProperties.getAesKey()); return configStorage; } /** * 配置公众号的事件路由 * <p> * 1.用户发消息 * 2.关注事件 * 3.取消关注事件 * ...... */ @Bean public WxMpMessageRouter messageRouter(WxMpService wxMpService) { WxMpMessageRouter newRouter = new WxMpMessageRouter(wxMpService); // 记录所有消息事件的日志 newRouter.rule().async(false).handler(this.logHandler).next(); // 文本消息 newRouter.rule().async(false).msgType(WxConsts.XmlMsgType.TEXT).interceptor(this.messageInterceptor).handler(this.messageHandler).end(); // 语音消息 newRouter.rule().async(false).msgType(WxConsts.XmlMsgType.VOICE).handler(this.voiceHandler).end(); // 图片消息 newRouter.rule().async(false).msgType(WxConsts.XmlMsgType.IMAGE).handler(this.imageHandler).end(); // 位置消息 newRouter.rule().async(false).msgType(WxConsts.XmlMsgType.LOCATION).handler(this.locationHandler).end(); // 关注事件 newRouter.rule().async(false).msgType(WxConsts.XmlMsgType.EVENT).event(WxConsts.EventType.SUBSCRIBE).handler(this.subscribeHandler).end(); // 取消关注事件 newRouter.rule().async(false).msgType(WxConsts.XmlMsgType.EVENT).event(WxConsts.EventType.UNSUBSCRIBE).handler(this.unsubscribeHandler).end(); return newRouter; } }
下载验证文件放于项目目录
package com.qiangesoft.wechat.controller; import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; import lombok.extern.slf4j.Slf4j; import org.springframework.core.io.ClassPathResource; import org.springframework.http.HttpHeaders; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.URLEncoder; import java.nio.charset.StandardCharsets; /** * 微信接入控制器 * ps:(JS接口安全域名、网页授权域名需要获取MP_verify_eerrereeee.txt) * <a href="https://mp.weixin.qq.com/cgi-bin/settingpage?t=setting/function&action=function&token=854866943&lang=zh_CN">...</a> * * @author qiangesoft * @date 2023-09-11 */ @Slf4j @Api(tags = "微信接入") @RestController @RequestMapping("/wechat") public class WxAccessController { /** * 获取安全凭证 * ps:例如[http://主机:端口/wechat/MP_verify_eerrereeee.txt] * * @param fileName * @param response */ @ApiOperation(value = "安全凭证") @GetMapping("/{fileName}") public void security(@PathVariable String fileName, HttpServletResponse response) { InputStream is = null; OutputStream os = null; try { // 获取文件 ClassPathResource classPathResource = new ClassPathResource(fileName); String filename = classPathResource.getFilename(); is = classPathResource.getInputStream(); byte[] bytes = is.readAllBytes(); response.setCharacterEncoding("UTF-8"); response.addHeader(HttpHeaders.CONTENT_DISPOSITION, "attachment;filename=" + URLEncoder.encode(filename, StandardCharsets.UTF_8)); response.addHeader(HttpHeaders.CONTENT_LENGTH, bytes.length + ""); response.setContentType("application/octet-stream"); os = response.getOutputStream(); os.write(bytes); } catch (IOException e) { e.printStackTrace(); } finally { if (os != null) { try { os.close(); } catch (IOException e) { e.printStackTrace(); } } if (is != null) { try { is.close(); } catch (IOException e) { e.printStackTrace(); } } } } }
package com.qiangesoft.wechat.controller; import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; import lombok.extern.slf4j.Slf4j; import me.chanjar.weixin.mp.api.WxMpMessageRouter; import me.chanjar.weixin.mp.api.WxMpService; import me.chanjar.weixin.mp.bean.message.WxMpXmlMessage; import me.chanjar.weixin.mp.bean.message.WxMpXmlOutMessage; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.*; /** * 微信消息控制器 * ps:请求验证、接收消息被动回复等 * * @author qiangesoft * @date 2023-09-07 */ @Slf4j @Api(tags = "微信消息") @RestController @RequestMapping("/wechat/message") public class WxMessageController { @Autowired private WxMpService wxMpService; @Autowired private WxMpMessageRouter wxMpMessageRouter; @ApiOperation(value = "验证接口") @GetMapping public String validate(@RequestParam String signature, @RequestParam String timestamp, @RequestParam String nonce, @RequestParam String echostr) { // 校验消息是否来自微信 if (!wxMpService.checkSignature(timestamp, nonce, signature)) { throw new IllegalArgumentException("非法请求,可能属于伪造的请求!"); } return echostr; } @ApiOperation(value = "接收消息") @PostMapping(produces = "application/xml; charset=UTF-8") public String handleMessage(@RequestBody String requestBody, @RequestParam String signature, @RequestParam String timestamp, @RequestParam String nonce, @RequestParam String openid, @RequestParam(name = "encrypt_type", required = false) String encType, @RequestParam(name = "msg_signature", required = false) String msgSignature) { // 校验消息是否来自微信 if (!wxMpService.checkSignature(timestamp, nonce, signature)) { throw new IllegalArgumentException("非法请求,可能属于伪造的请求!"); } // 消息处理【被动消息回复】 String message = null; if (encType == null) { // 明文传输 WxMpXmlMessage inMessage = WxMpXmlMessage.fromXml(requestBody); WxMpXmlOutMessage outMessage = wxMpMessageRouter.route(inMessage); if (outMessage == null) { return null; } message = outMessage.toXml(); } else if ("aes".equalsIgnoreCase(encType)) { // aes加密 WxMpXmlMessage inMessage = WxMpXmlMessage.fromEncryptedXml(requestBody, wxMpService.getWxMpConfigStorage(), timestamp, nonce, msgSignature); WxMpXmlOutMessage outMessage = wxMpMessageRouter.route(inMessage); if (outMessage == null) { return null; } message = outMessage.toEncryptedXml(wxMpService.getWxMpConfigStorage()); } return message; } }
package com.qiangesoft.wechat.controller; import com.qiangesoft.wechat.config.WxMpProperties; import com.qiangesoft.wechat.dto.UserDTO; import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; import lombok.extern.slf4j.Slf4j; import me.chanjar.weixin.common.api.WxConsts; import me.chanjar.weixin.common.bean.WxOAuth2UserInfo; import me.chanjar.weixin.common.bean.oauth2.WxOAuth2AccessToken; import me.chanjar.weixin.common.error.WxErrorException; import me.chanjar.weixin.common.service.WxOAuth2Service; import me.chanjar.weixin.mp.api.WxMpService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.*; /** * 微信用户控制器 * ps:获取用户信息、授权网页用户信息等 * * @author qiangesoft * @date 2023-09-11 */ @Slf4j @Api(tags = "微信用户") @Controller @RequestMapping("/wechat/user") public class WxUserController { @Autowired private WxMpProperties wxMpProperties; @Autowired private WxMpService wxMpService; @ApiOperation(value = "个人信息") @GetMapping("/mine") public String mine() { WxOAuth2Service oAuth2Service = wxMpService.getOAuth2Service(); // 构建授权url String authorizationUrl = oAuth2Service.buildAuthorizationUrl(wxMpProperties.getServer() + "wechat/user/callback", WxConsts.OAuth2Scope.SNSAPI_USERINFO, null); return "redirect:" + authorizationUrl; } @ApiOperation(value = "个人信息网页授权回调") @GetMapping("/callback") public String callback(String code, Model model) throws WxErrorException { WxOAuth2UserInfo userInfo = this.getUserInfo(code); String openid = userInfo.getOpenid(); // todo 通过openid拿取业务系统详细用户信息 IWxUserService UserDTO userDTO = new UserDTO(); userDTO.setOpenId(openid); userDTO.setNickName(userInfo.getNickname()); model.addAttribute("user", userDTO); return "mine"; } /** * 获取微信用户信息 * * @param code * @return * @throws WxErrorException */ private WxOAuth2UserInfo getUserInfo(String code) throws WxErrorException { WxOAuth2Service oAuth2Service = wxMpService.getOAuth2Service(); // 利用code获取accessToken WxOAuth2AccessToken accessToken = oAuth2Service.getAccessToken(code); // 利用accessToken获取用户信息 WxOAuth2UserInfo userInfo = oAuth2Service.getUserInfo(accessToken, null); return userInfo; } }
package com.qiangesoft.wechat.core.interceptor; import me.chanjar.weixin.common.api.WxConsts; import me.chanjar.weixin.common.error.WxErrorException; import me.chanjar.weixin.common.session.WxSessionManager; import me.chanjar.weixin.mp.api.WxMpMessageInterceptor; import me.chanjar.weixin.mp.api.WxMpService; import me.chanjar.weixin.mp.bean.message.WxMpXmlMessage; import org.springframework.stereotype.Component; import java.util.Map; /** * 对微信公众号消息进行预处理、过滤等操作,根据具体业务需求决定是否允许继续执行后面的路由处理方法 * <p> * 1.如果要中止消息的继续处理,需要返回 false * 2.在执行完当前拦截器操作后,允许消息的继续处理,返回 true * * @author qiangesoft * @date 2023-09-07 */ @Component public class MessageInterceptor implements WxMpMessageInterceptor { @Override public boolean intercept(WxMpXmlMessage wxMessage, Map<String, Object> context, WxMpService wxMpService, WxSessionManager sessionManager) throws WxErrorException { String msgType = wxMessage.getMsgType(); String content = wxMessage.getContent(); // 文本处理 if (msgType.equals(WxConsts.XmlMsgType.TEXT)) { if (content.contains("混蛋")) { String newContent = content.replace("混蛋", "***"); wxMessage.setContent(newContent); } return true; } // todo 音频、视频处理 return true; } }
package com.qiangesoft.wechat.core.handler.event; import com.qiangesoft.wechat.core.builder.TextOutMessageBuilder; import com.qiangesoft.wechat.entity.WxUser; import com.qiangesoft.wechat.service.IWxUserService; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import me.chanjar.weixin.common.api.WxConsts; import me.chanjar.weixin.common.error.WxErrorException; import me.chanjar.weixin.common.session.WxSessionManager; import me.chanjar.weixin.mp.api.WxMpMessageHandler; import me.chanjar.weixin.mp.api.WxMpService; import me.chanjar.weixin.mp.bean.message.WxMpXmlMessage; import me.chanjar.weixin.mp.bean.message.WxMpXmlOutMessage; import org.springframework.stereotype.Component; import java.util.Map; /** * 微信公众号用户关注处理器 * * @author qiangesoft * @date 2023-09-07 */ @Slf4j @Component @RequiredArgsConstructor public class SubscribeHandler implements WxMpMessageHandler { private final IWxUserService wxUserService; @Override public WxMpXmlOutMessage handle(WxMpXmlMessage wxMessage, Map<String, Object> context, WxMpService wxMpService, WxSessionManager sessionManager) throws WxErrorException { if (!wxMessage.getMsgType().equals(WxConsts.EventType.SUBSCRIBE)) { return null; } String openId = wxMessage.getFromUser(); // 微信关注用户 WxUser wxUser = wxUserService.getByOpenId(openId); if (wxUser == null) { wxUser = new WxUser(); wxUser.setOpenId(openId); wxUser.setAttentionFlag(true); wxUserService.save(wxUser); } else { wxUser.setAttentionFlag(true); wxUserService.updateById(wxUser); } String outContent = "您好,欢迎关注xxx!"; return new TextOutMessageBuilder().build(outContent, wxMessage, wxMpService); } }
package com.qiangesoft.wechat.core.handler.event; import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; import com.qiangesoft.wechat.core.builder.TextOutMessageBuilder; import com.qiangesoft.wechat.entity.WxUser; import com.qiangesoft.wechat.service.IWxUserService; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import me.chanjar.weixin.common.api.WxConsts; import me.chanjar.weixin.common.error.WxErrorException; import me.chanjar.weixin.common.session.WxSessionManager; import me.chanjar.weixin.mp.api.WxMpMessageHandler; import me.chanjar.weixin.mp.api.WxMpService; import me.chanjar.weixin.mp.bean.message.WxMpXmlMessage; import me.chanjar.weixin.mp.bean.message.WxMpXmlOutMessage; import org.springframework.stereotype.Component; import java.util.Map; /** * 微信公众号用户取消关注处理器 * * @author qiangesoft * @date 2023-09-07 */ @Slf4j @Component @RequiredArgsConstructor public class UnsubscribeHandler implements WxMpMessageHandler { private final IWxUserService wxUserService; @Override public WxMpXmlOutMessage handle(WxMpXmlMessage wxMessage, Map<String, Object> context, WxMpService wxMpService, WxSessionManager sessionManager) throws WxErrorException { if (!wxMessage.getMsgType().equals(WxConsts.EventType.UNSUBSCRIBE)) { return null; } String openId = wxMessage.getFromUser(); // 取消关注 LambdaUpdateWrapper<WxUser> updateWrapper = new LambdaUpdateWrapper<>(); updateWrapper.eq(WxUser::getOpenId, openId) .set(WxUser::getAttentionFlag, false); wxUserService.update(updateWrapper); String outContent = "已取消关注xxx,再见!"; return new TextOutMessageBuilder().build(outContent, wxMessage, wxMpService); } }
package com.qiangesoft.wechat.core.handler.message; import com.qiangesoft.wechat.core.builder.TextOutMessageBuilder; import lombok.extern.slf4j.Slf4j; import me.chanjar.weixin.common.api.WxConsts; import me.chanjar.weixin.common.error.WxErrorException; import me.chanjar.weixin.common.session.WxSessionManager; import me.chanjar.weixin.mp.api.WxMpMessageHandler; import me.chanjar.weixin.mp.api.WxMpService; import me.chanjar.weixin.mp.bean.message.WxMpXmlMessage; import me.chanjar.weixin.mp.bean.message.WxMpXmlOutMessage; import org.springframework.stereotype.Component; import java.util.Map; /** * 文本消息处理器 * * @author qiangesoft * @date 2023-09-07 */ @Slf4j @Component public class MessageHandler implements WxMpMessageHandler { @Override public WxMpXmlOutMessage handle(WxMpXmlMessage wxMessage, Map<String, Object> context, WxMpService wxMpService, WxSessionManager sessionManager) throws WxErrorException { if (!wxMessage.getMsgType().equals(WxConsts.XmlMsgType.TEXT)) { return null; } // 接收的消息内容 String content = wxMessage.getContent(); log.info("Received wechat text message, content [{}] !", content); String outContent = "您好,欢迎访问xxx!"; if (content.contains("你好") || content.contains("您好")) { outContent = "您好"; } return new TextOutMessageBuilder().build(outContent, wxMessage, wxMpService); } }
package com.qiangesoft.wechat.core.handler; import com.alibaba.fastjson2.JSONObject; import lombok.extern.slf4j.Slf4j; import me.chanjar.weixin.common.error.WxErrorException; import me.chanjar.weixin.common.session.WxSessionManager; import me.chanjar.weixin.mp.api.WxMpMessageHandler; import me.chanjar.weixin.mp.api.WxMpService; import me.chanjar.weixin.mp.bean.message.WxMpXmlMessage; import me.chanjar.weixin.mp.bean.message.WxMpXmlOutMessage; import org.springframework.stereotype.Component; import java.util.Map; /** * 微信公众号日志记录处理器 * * @author qiangesoft * @date 2023-09-07 */ @Slf4j @Component public class LogHandler implements WxMpMessageHandler { @Override public WxMpXmlOutMessage handle(WxMpXmlMessage wxMessage, Map<String, Object> context, WxMpService wxMpService, WxSessionManager sessionManager) throws WxErrorException { log.info("Received wechat message, content is [{}]", JSONObject.toJSONString(wxMessage)); return null; } }
地址:https://gitee.com/qiangesoft/boot-business/tree/master/boot-business-wechat
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。