diff --git a/src/main/java/com/rymcu/vertical/wx/mp/controller/WxoAuthController.java b/src/main/java/com/rymcu/vertical/wx/mp/controller/WxoAuthController.java
new file mode 100644
index 0000000..7e77d71
--- /dev/null
+++ b/src/main/java/com/rymcu/vertical/wx/mp/controller/WxoAuthController.java
@@ -0,0 +1,68 @@
+package com.rymcu.vertical.wx.mp.controller;
+
+import com.rymcu.vertical.util.ContextHolderUtils;
+import me.chanjar.weixin.common.api.WxConsts;
+import me.chanjar.weixin.common.bean.WxJsapiSignature;
+import me.chanjar.weixin.common.error.WxErrorException;
+import me.chanjar.weixin.common.util.http.URIUtil;
+import me.chanjar.weixin.mp.api.WxMpService;
+import me.chanjar.weixin.mp.bean.result.WxMpOAuth2AccessToken;
+import me.chanjar.weixin.mp.bean.result.WxMpUser;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.stereotype.Controller;
+import org.springframework.web.bind.annotation.*;
+
+import javax.annotation.Resource;
+
+/**
+ * @author ronger
+ */
+@Controller
+@RequestMapping("/wx/oauth/{appId}")
+public class WxoAuthController {
+
+ @Resource
+ private WxMpService wxMpService;
+
+ @Value("${resource.domain}")
+ private String domain;
+
+ @GetMapping
+ public String wxAuth(@PathVariable String appId, @RequestParam(name = "redirectUrl") String redirectUrl) {
+ wxMpService.switchoverTo(appId);
+ if ("wxa49093339a5a822b".equals(appId)) {
+ domain = "http://oae.nat300.top/vertical";
+ } else {
+ domain += "/vertical-console";
+ }
+ StringBuilder accessTokenUrl = new StringBuilder(domain).append("/wx/oauth/" + appId + "/getAccessToken?redirectUrl=").append(URIUtil.encodeURIComponent(domain + redirectUrl));
+ String oauth2Url = wxMpService.oauth2buildAuthorizationUrl(accessTokenUrl.toString(), WxConsts.OAuth2Scope.SNSAPI_BASE, null);
+ return "redirect:" + oauth2Url;
+ }
+
+ @GetMapping("getAccessToken")
+ public String getAccessToken(@PathVariable String appId, @RequestParam(name = "code") String code, @RequestParam(name = "redirectUrl") String redirectUrl) throws Exception {
+ wxMpService.switchoverTo(appId);
+ WxMpOAuth2AccessToken oAuth2AccessToken = wxMpService.oauth2getAccessToken(code);
+ boolean valid = wxMpService.oauth2validateAccessToken(oAuth2AccessToken);
+ if (!valid) {
+ throw new Exception("无权限");
+ }
+
+ WxMpUser wxMpUser =wxMpService.getUserService().userInfo(oAuth2AccessToken.getOpenId());
+ ContextHolderUtils.getSession2().setAttribute("wxUser", wxMpUser);
+ return "redirect:" + redirectUrl;
+ }
+
+ @GetMapping("validJs")
+ @ResponseBody
+ public WxJsapiSignature validJs(String url) throws WxErrorException {
+ return wxMpService.createJsapiSignature(url);
+ }
+
+ @GetMapping("t")
+ public String reOauth() {
+ return "wx/oauth";
+ }
+}
diff --git a/src/main/java/com/rymcu/vertical/wx/open/config/WxOpenProperties.java b/src/main/java/com/rymcu/vertical/wx/open/config/WxOpenProperties.java
new file mode 100644
index 0000000..4fcce5d
--- /dev/null
+++ b/src/main/java/com/rymcu/vertical/wx/open/config/WxOpenProperties.java
@@ -0,0 +1,42 @@
+package com.rymcu.vertical.wx.open.config;
+
+import lombok.Getter;
+import lombok.Setter;
+import org.apache.commons.lang3.builder.ToStringBuilder;
+import org.apache.commons.lang3.builder.ToStringStyle;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+
+
+/**
+ * @author 007
+ */
+@Getter
+@Setter
+@ConfigurationProperties(prefix = "wx.open")
+public class WxOpenProperties {
+ /**
+ * 设置微信三方平台的appid
+ */
+ private String componentAppId;
+
+ /**
+ * 设置微信三方平台的app secret
+ */
+ private String componentSecret;
+
+ /**
+ * 设置微信三方平台的token
+ */
+ private String componentToken;
+
+ /**
+ * 设置微信三方平台的EncodingAESKey
+ */
+ private String componentAesKey;
+
+ @Override
+ public String toString() {
+ return ToStringBuilder.reflectionToString(this,
+ ToStringStyle.MULTI_LINE_STYLE);
+ }
+}
diff --git a/src/main/java/com/rymcu/vertical/wx/open/controller/WxOpenNotifyController.java b/src/main/java/com/rymcu/vertical/wx/open/controller/WxOpenNotifyController.java
new file mode 100644
index 0000000..3b2e3fe
--- /dev/null
+++ b/src/main/java/com/rymcu/vertical/wx/open/controller/WxOpenNotifyController.java
@@ -0,0 +1,110 @@
+package com.rymcu.vertical.wx.open.controller;
+
+import com.rymcu.vertical.wx.open.handler.WxOpenServiceHandler;
+import me.chanjar.weixin.common.error.WxErrorException;
+import me.chanjar.weixin.mp.bean.kefu.WxMpKefuMessage;
+import me.chanjar.weixin.mp.bean.message.WxMpXmlMessage;
+import me.chanjar.weixin.mp.bean.message.WxMpXmlOutMessage;
+import me.chanjar.weixin.open.bean.message.WxOpenXmlMessage;
+import org.apache.commons.lang3.StringUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.*;
+
+/**
+ * @author 007
+ */
+@RestController
+@RequestMapping("/notify")
+public class WxOpenNotifyController {
+ private final Logger logger = LoggerFactory.getLogger(this.getClass());
+ @Autowired
+ protected WxOpenServiceHandler wxOpenService;
+
+ @RequestMapping("/receive_ticket")
+ public Object receiveTicket(@RequestBody(required = false) String requestBody, @RequestParam("timestamp") String timestamp,
+ @RequestParam("nonce") String nonce, @RequestParam("signature") String signature,
+ @RequestParam(name = "encrypt_type", required = false) String encType,
+ @RequestParam(name = "msg_signature", required = false) String msgSignature) {
+ this.logger.info(
+ "\n接收微信请求:[signature=[{}], encType=[{}], msgSignature=[{}],"
+ + " timestamp=[{}], nonce=[{}], requestBody=[\n{}\n] ",
+ signature, encType, msgSignature, timestamp, nonce, requestBody);
+
+ if (!StringUtils.equalsIgnoreCase("aes", encType)
+ || !wxOpenService.getWxOpenComponentService().checkSignature(timestamp, nonce, signature)) {
+ throw new IllegalArgumentException("非法请求,可能属于伪造的请求!");
+ }
+
+ // aes加密的消息
+ WxOpenXmlMessage inMessage = WxOpenXmlMessage.fromEncryptedXml(requestBody,
+ wxOpenService.getWxOpenConfigStorage(), timestamp, nonce, msgSignature);
+ this.logger.debug("\n消息解密后内容为:\n{} ", inMessage.toString());
+ try {
+ String out = wxOpenService.getWxOpenComponentService().route(inMessage);
+ this.logger.debug("\n组装回复信息:{}", out);
+ } catch (WxErrorException e) {
+ this.logger.error("receive_ticket", e);
+ }
+
+
+ return "success";
+ }
+
+ @RequestMapping("{appId}/callback")
+ public Object callback(@RequestBody(required = false) String requestBody,
+ @PathVariable("appId") String appId,
+ @RequestParam("signature") String signature,
+ @RequestParam("timestamp") String timestamp,
+ @RequestParam("nonce") String nonce,
+ @RequestParam("openid") String openid,
+ @RequestParam("encrypt_type") String encType,
+ @RequestParam("msg_signature") String msgSignature) {
+ this.logger.info(
+ "\n接收微信请求:[appId=[{}], openid=[{}], signature=[{}], encType=[{}], msgSignature=[{}],"
+ + " timestamp=[{}], nonce=[{}], requestBody=[\n{}\n] ",
+ appId, openid, signature, encType, msgSignature, timestamp, nonce, requestBody);
+ if (!StringUtils.equalsIgnoreCase("aes", encType)
+ || !wxOpenService.getWxOpenComponentService().checkSignature(timestamp, nonce, signature)) {
+ throw new IllegalArgumentException("非法请求,可能属于伪造的请求!");
+ }
+
+ String out = "";
+ // aes加密的消息
+ WxMpXmlMessage inMessage = WxOpenXmlMessage.fromEncryptedMpXml(requestBody,
+ wxOpenService.getWxOpenConfigStorage(), timestamp, nonce, msgSignature);
+ this.logger.debug("\n消息解密后内容为:\n{} ", inMessage.toString());
+ // 全网发布测试用例
+ if (StringUtils.equalsAnyIgnoreCase(appId)) {
+ try {
+ if (StringUtils.equals(inMessage.getMsgType(), "text")) {
+ if (StringUtils.equals(inMessage.getContent(), "TESTCOMPONENT_MSG_TYPE_TEXT")) {
+ out = WxOpenXmlMessage.wxMpOutXmlMessageToEncryptedXml(
+ WxMpXmlOutMessage.TEXT().content("TESTCOMPONENT_MSG_TYPE_TEXT_callback")
+ .fromUser(inMessage.getToUser())
+ .toUser(inMessage.getFromUser())
+ .build(),
+ wxOpenService.getWxOpenConfigStorage()
+ );
+ } else if (StringUtils.startsWith(inMessage.getContent(), "QUERY_AUTH_CODE:")) {
+ String msg = inMessage.getContent().replace("QUERY_AUTH_CODE:", "") + "_from_api";
+ WxMpKefuMessage kefuMessage = WxMpKefuMessage.TEXT().content(msg).toUser(inMessage.getFromUser()).build();
+ wxOpenService.getWxOpenComponentService().getWxMpServiceByAppid(appId).getKefuService().sendKefuMessage(kefuMessage);
+ }
+ } else if (StringUtils.equals(inMessage.getMsgType(), "event")) {
+ WxMpKefuMessage kefuMessage = WxMpKefuMessage.TEXT().content(inMessage.getEvent() + "from_callback").toUser(inMessage.getFromUser()).build();
+ wxOpenService.getWxOpenComponentService().getWxMpServiceByAppid(appId).getKefuService().sendKefuMessage(kefuMessage);
+ }
+ } catch (WxErrorException e) {
+ logger.error("callback", e);
+ }
+ }else{
+ WxMpXmlOutMessage outMessage = wxOpenService.getWxOpenMessageRouter().route(inMessage, appId);
+ if(outMessage != null){
+ out = WxOpenXmlMessage.wxMpOutXmlMessageToEncryptedXml(outMessage, wxOpenService.getWxOpenConfigStorage());
+ }
+ }
+ return out;
+ }
+}
diff --git a/src/main/java/com/rymcu/vertical/wx/open/handler/WxOpenServiceHandler.java b/src/main/java/com/rymcu/vertical/wx/open/handler/WxOpenServiceHandler.java
new file mode 100644
index 0000000..e361568
--- /dev/null
+++ b/src/main/java/com/rymcu/vertical/wx/open/handler/WxOpenServiceHandler.java
@@ -0,0 +1,74 @@
+package com.rymcu.vertical.wx.open.handler;
+
+import com.rymcu.vertical.core.service.props.DynProps4FilesService;
+import com.rymcu.vertical.wx.open.config.WxOpenProperties;
+import me.chanjar.weixin.open.api.impl.WxOpenInRedisConfigStorage;
+import me.chanjar.weixin.open.api.impl.WxOpenMessageRouter;
+import me.chanjar.weixin.open.api.impl.WxOpenServiceImpl;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.core.env.Environment;
+import org.springframework.stereotype.Service;
+import redis.clients.jedis.Jedis;
+import redis.clients.jedis.JedisPool;
+import redis.clients.jedis.JedisPoolConfig;
+
+import javax.annotation.PostConstruct;
+import javax.annotation.Resource;
+
+/**
+ * @author ronger
+ */
+@Service
+public class WxOpenServiceHandler extends WxOpenServiceImpl {
+
+ private Logger logger = LoggerFactory.getLogger(getClass());
+
+
+ @Resource
+ private WxOpenProperties wxOpenProperties;
+ @Resource
+ private WxOpenMessageRouter wxOpenMessageRouter;
+ @Resource
+ private DynProps4FilesService dynProps4Files;
+ @Resource
+ private Environment env;
+ private static JedisPool pool;
+
+ @PostConstruct
+ public void init() {
+ WxOpenInRedisConfigStorage inRedisConfigStorage = new WxOpenInRedisConfigStorage(getJedisPool());
+ inRedisConfigStorage.setComponentAppId(wxOpenProperties.getComponentAppId());
+ inRedisConfigStorage.setComponentAppSecret(wxOpenProperties.getComponentSecret());
+ inRedisConfigStorage.setComponentToken(wxOpenProperties.getComponentToken());
+ inRedisConfigStorage.setComponentAesKey(wxOpenProperties.getComponentAesKey());
+ setWxOpenConfigStorage(inRedisConfigStorage);
+ wxOpenMessageRouter = new WxOpenMessageRouter(this);
+ wxOpenMessageRouter.rule().handler((wxMpXmlMessage, map, wxMpService, wxSessionManager) -> {
+ logger.info("\n接收到 {} 公众号请求消息,内容:{}", wxMpService.getWxMpConfigStorage().getAppId(), wxMpXmlMessage);
+ return null;
+ }).next();
+ }
+
+ private JedisPool getJedisPool() {
+ if (pool == null) {
+ synchronized (WxOpenServiceHandler.class) {
+ if (pool == null) {
+ JedisPoolConfig config = new JedisPoolConfig();
+ config.setMaxIdle(dynProps4Files.getInt("REDIS_MAX_IDLE", JedisPoolConfig.DEFAULT_MAX_IDLE));
+ config.setMaxTotal(dynProps4Files.getInt("REDIS_MAX_TOTAL", JedisPoolConfig.DEFAULT_MAX_TOTAL));
+ config.setMaxWaitMillis(dynProps4Files.getLong("REDIS_MAX_WAIT", JedisPoolConfig.DEFAULT_MAX_WAIT_MILLIS));
+ config.setTestOnBorrow(true);
+ pool = new JedisPool(config, env.getProperty("spring.redis.host"),
+ dynProps4Files.getInt("REDIS_PORT", 6379), dynProps4Files.getInt(
+ "REDIS_MAX_WAIT", 1000), dynProps4Files.getProperty("REDIS_PASSWORD", env.getProperty("spring.redis.password")));
+ }
+ }
+ }
+ return pool;
+ }
+
+ public WxOpenMessageRouter getWxOpenMessageRouter() {
+ return wxOpenMessageRouter;
+ }
+}