此文章目的是为了验证测试号接口配置中服务器地址的有效性。
推荐码云上的开源微信开发 Java SDK weixin-java-tools,引入自己项目就可以用了,很多功能都有,十分方便。
文件目录
认证接口配置
@Api("认证接口配置")
@Slf4j
@AllArgsConstructor
@RestController
@RequestMapping("/wx/portal/{appid}")
public class WxPortalController {
private final WxMpService wxService;
private final WxMpMessageRouter messageRouter;
@ApiOperation(value = "接收微信服务器的认证消息")
@GetMapping(produces = "text/plain;charset=utf-8")
public String authGet(@PathVariable String appid,
@RequestParam(name = "signature", required = false) String signature,
@RequestParam(name = "timestamp", required = false) String timestamp,
@RequestParam(name = "nonce", required = false) String nonce,
@RequestParam(name = "echostr", required = false) String echostr) {
log.info("\n接收到来自微信服务器的认证消息:[{}, {}, {}, {}]", signature,
timestamp, nonce, echostr);
if (StringUtils.isAnyBlank(signature, timestamp, nonce, echostr)) {
throw new IllegalArgumentException("请求参数非法,请核实!");
}
if (!this.wxService.switchover(appid)) {
throw new IllegalArgumentException(String.format("未找到对应appid=[%s]的配置,请核实!", appid));
}
if (wxService.checkSignature(timestamp, nonce, signature)) {
return echostr;
}
return "非法请求";
}
@ApiOperation(value = "接收微信请求")
@PostMapping(produces = "application/xml; charset=UTF-8")
public String post(@PathVariable String appid,
@RequestBody String requestBody,
@RequestParam("signature") String signature,
@RequestParam("timestamp") String timestamp,
@RequestParam("nonce") String nonce,
@RequestParam("openid") String openid,
@RequestParam(name = "encrypt_type", required = false) String encType,
@RequestParam(name = "msg_signature", required = false) String msgSignature) {
log.info("\n接收微信请求:[openid=[{}], [signature=[{}], encType=[{}], msgSignature=[{}],"
+ " timestamp=[{}], nonce=[{}], requestBody=[\n{}\n] ",
openid, signature, encType, msgSignature, timestamp, nonce, requestBody);
if (!this.wxService.switchover(appid)) {
throw new IllegalArgumentException(String.format("未找到对应appid=[%s]的配置,请核实!", appid));
}
if (!wxService.checkSignature(timestamp, nonce, signature)) {
throw new IllegalArgumentException("非法请求,可能属于伪造的请求!");
}
String out = null;
if (encType == null) {
// 明文传输的消息
WxMpXmlMessage inMessage = WxMpXmlMessage.fromXml(requestBody);
WxMpXmlOutMessage outMessage = this.route(inMessage);
if (outMessage == null) {
return "";
}
out = outMessage.toXml();
} else if ("aes".equalsIgnoreCase(encType)) {
// aes加密的消息
WxMpXmlMessage inMessage = WxMpXmlMessage.fromEncryptedXml(requestBody, wxService.getWxMpConfigStorage(),
timestamp, nonce, msgSignature);
log.debug("\n消息解密后内容为:\n{} ", inMessage.toString());
WxMpXmlOutMessage outMessage = this.route(inMessage);
if (outMessage == null) {
return "";
}
out = outMessage.toEncryptedXml(wxService.getWxMpConfigStorage());
}
log.debug("\n组装回复信息:{}", out);
return out;
}
private WxMpXmlOutMessage route(WxMpXmlMessage message) {
try {
return this.messageRouter.route(message);
} catch (Exception e) {
log.error("路由消息时出现异常!", e);
}
return null;
}
}
新增配置文件application.yml
wx:
mp:
useRedis: true
redisConfig:
host: 127.0.0.1
port: 6379
#password:
timeout: 2000
configs:
- appId: #测试号appid
secret: #测试号appsecret
token: #测试号接口配置信息填写的token
aesKey: 111
# 开发环境配置
server:
# 服务器的HTTP端口,默认为8080
port: 80
logging:
level:
org.springframework.web: info
com.wckj.binarywang.demo.wx.pay: debug
com.wckj.binarywang.wxpay: debug
读取配置信息
@Data
@ConfigurationProperties(prefix = "wx.mp")
public class WxMpProperties {
/**
* 是否使用redis存储access token
*/
private boolean useRedis;
/**
* redis 配置
*/
private RedisConfig redisConfig;
@Data
public static class RedisConfig {
/**
* redis服务器 主机地址
*/
private String host;
/**
* redis服务器 端口号
*/
private Integer port;
/**
* redis服务器 密码
*/
private String password;
/**
* redis 服务连接超时时间
*/
private Integer timeout;
}
/**
* 多个公众号配置信息
*/
private List<MpConfig> configs;
@Data
public static class MpConfig {
/**
* 设置微信公众号的appid
*/
private String appId;
/**
* 设置微信公众号的app secret
*/
private String secret;
/**
* 设置微信公众号的token
*/
private String token;
/**
* 设置微信公众号的EncodingAESKey
*/
private String aesKey;
}
@Override
public String toString() {
return JsonUtils.toJson(this);
}
}
@AllArgsConstructor
@Configuration
@EnableConfigurationProperties(WxMpProperties.class)
public class WxMpConfiguration {
private final LogHandler logHandler;
private final NullHandler nullHandler;
private final KfSessionHandler kfSessionHandler;
private final StoreCheckNotifyHandler storeCheckNotifyHandler;
private final LocationHandler locationHandler;
private final MenuHandler menuHandler;
private final MsgHandler msgHandler;
private final UnsubscribeHandler unsubscribeHandler;
private final SubscribeHandler subscribeHandler;
private final ScanHandler scanHandler;
private final WxMpProperties properties;
@Bean
public WxMpService wxMpService() {
// 代码里 getConfigs()处报错的同学,请注意仔细阅读项目说明,你的IDE需要引入lombok插件!!!!
final List<WxMpProperties.MpConfig> configs = this.properties.getConfigs();
if (configs == null) {
throw new RuntimeException("大哥,拜托先看下项目首页的说明(readme文件),添加下相关配置,注意别配错了!");
}
WxMpService service = new WxMpServiceImpl();
service.setMultiConfigStorages(configs
.stream().map(a -> {
WxMpDefaultConfigImpl configStorage;
if (this.properties.isUseRedis()) {
final WxMpProperties.RedisConfig redisConfig = this.properties.getRedisConfig();
JedisPoolConfig poolConfig = new JedisPoolConfig();
JedisPool jedisPool = new JedisPool(poolConfig, redisConfig.getHost(), redisConfig.getPort(),
redisConfig.getTimeout(), redisConfig.getPassword());
configStorage = new WxMpRedisConfigImpl(new JedisWxRedisOps(jedisPool), a.getAppId());
System.out.println("JedisPool连接成功:"+redisConfig.getHost()+"\t"+redisConfig.getPort());
} else {
configStorage = new WxMpDefaultConfigImpl();
}
configStorage.setAppId(a.getAppId());
configStorage.setSecret(a.getSecret());
configStorage.setToken(a.getToken());
configStorage.setAesKey(a.getAesKey());
return configStorage;
}).collect(Collectors.toMap(WxMpDefaultConfigImpl::getAppId, a -> a, (o, n) -> o)));
return service;
}
@Bean
public WxMpMessageRouter messageRouter(WxMpService wxMpService) {
final WxMpMessageRouter newRouter = new WxMpMessageRouter(wxMpService);
// 记录所有事件的日志 (异步执行)
newRouter.rule().handler(this.logHandler).next();
// 接收客服会话管理事件
newRouter.rule().async(false).msgType(EVENT).event(KF_CREATE_SESSION)
.handler(this.kfSessionHandler).end();
newRouter.rule().async(false).msgType(EVENT).event(KF_CLOSE_SESSION)
.handler(this.kfSessionHandler).end();
newRouter.rule().async(false).msgType(EVENT).event(KF_SWITCH_SESSION)
.handler(this.kfSessionHandler).end();
// 门店审核事件
newRouter.rule().async(false).msgType(EVENT).event(POI_CHECK_NOTIFY).handler(this.storeCheckNotifyHandler).end();
// 自定义菜单事件
newRouter.rule().async(false).msgType(EVENT).event(EventType.CLICK).handler(this.menuHandler).end();
// 点击菜单连接事件
newRouter.rule().async(false).msgType(EVENT).event(EventType.VIEW).handler(this.nullHandler).end();
// 关注事件
newRouter.rule().async(false).msgType(EVENT).event(SUBSCRIBE).handler(this.subscribeHandler).end();
// 取消关注事件
newRouter.rule().async(false).msgType(EVENT).event(UNSUBSCRIBE).handler(this.unsubscribeHandler).end();
// 上报地理位置事件
newRouter.rule().async(false).msgType(EVENT).event(EventType.LOCATION).handler(this.locationHandler).end();
// 接收地理位置消息
newRouter.rule().async(false).msgType(XmlMsgType.LOCATION).handler(this.locationHandler).end();
// 扫码事件
newRouter.rule().async(false).msgType(EVENT).event(EventType.SCAN).handler(this.scanHandler).end();
// 默认
newRouter.rule().async(false).handler(this.msgHandler).end();
return newRouter;
}
}
Application启动类
@SpringBootApplication
public class WxMpDemoApplication {
public static void main(String[] args) {
SpringApplication.run(WxMpDemoApplication.class, args);
}
}
启动项目,前往微信公众平台,将接口配置信息填写完整,appid就是测试号的appid,token可以任意填写但要与后台配置文件的保持一致。