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; + } +}