Commit c671b01b0a038ac836cb71be323178d61461917d
1 parent
275a1b10
mp:增加公众号的校验签名回调
Showing
6 changed files
with
105 additions
and
16 deletions
yudao-module-mp/yudao-module-mp-biz/src/main/java/cn/iocoder/yudao/module/mp/controller/admin/open/MpOpenController.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.mp.controller.admin.open; | |
| 2 | + | |
| 3 | +import cn.hutool.core.util.StrUtil; | |
| 4 | +import cn.iocoder.yudao.module.mp.controller.admin.open.vo.MpOpenCheckSignatureReqVO; | |
| 5 | +import cn.iocoder.yudao.module.mp.framework.mp.core.MpServiceFactory; | |
| 6 | +import io.swagger.annotations.Api; | |
| 7 | +import io.swagger.annotations.ApiOperation; | |
| 8 | +import lombok.extern.slf4j.Slf4j; | |
| 9 | +import me.chanjar.weixin.mp.api.WxMpService; | |
| 10 | +import org.springframework.validation.annotation.Validated; | |
| 11 | +import org.springframework.web.bind.annotation.*; | |
| 12 | + | |
| 13 | +import javax.annotation.Resource; | |
| 14 | + | |
| 15 | +@Api(tags = "管理后台 - 公众号回调") | |
| 16 | +@RestController | |
| 17 | +@RequestMapping("/mp/open") | |
| 18 | +@Validated | |
| 19 | +@Slf4j | |
| 20 | +public class MpOpenController { | |
| 21 | + | |
| 22 | + @Resource | |
| 23 | + private MpServiceFactory mpServiceFactory; | |
| 24 | + | |
| 25 | + @ApiOperation("校验签名") // 参见 https://developers.weixin.qq.com/doc/offiaccount/Basic_Information/Access_Overview.html 文档 | |
| 26 | + @GetMapping(value = "/{appId}", produces = "text/plain;charset=utf-8") | |
| 27 | + public String checkSignature(@PathVariable("appId") String appId, | |
| 28 | + MpOpenCheckSignatureReqVO reqVO) { | |
| 29 | + log.info("[checkSignature][appId({}) 接收到来自微信服务器的认证消息({})]", appId, reqVO); | |
| 30 | + // 校验请求签名 | |
| 31 | + WxMpService wxMpService = mpServiceFactory.getRequiredMpService(appId); | |
| 32 | + // 校验通过 | |
| 33 | + if (wxMpService.checkSignature(reqVO.getTimestamp(), reqVO.getNonce(), reqVO.getSignature())) { | |
| 34 | + return reqVO.getEchostr(); | |
| 35 | + } | |
| 36 | + // 校验不通过 | |
| 37 | + return "非法请求"; | |
| 38 | + } | |
| 39 | + | |
| 40 | +} | ... | ... |
yudao-module-mp/yudao-module-mp-biz/src/main/java/cn/iocoder/yudao/module/mp/controller/admin/open/vo/MpOpenCheckSignatureReqVO.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.mp.controller.admin.open.vo; | |
| 2 | + | |
| 3 | + | |
| 4 | +import io.swagger.annotations.ApiModel; | |
| 5 | +import io.swagger.annotations.ApiModelProperty; | |
| 6 | +import lombok.Data; | |
| 7 | +import lombok.EqualsAndHashCode; | |
| 8 | +import lombok.ToString; | |
| 9 | +import org.springframework.web.bind.annotation.RequestParam; | |
| 10 | + | |
| 11 | +import javax.validation.constraints.NotEmpty; | |
| 12 | + | |
| 13 | +@ApiModel("管理后台 - 公众号校验签名 Request VO") | |
| 14 | +@Data | |
| 15 | +public class MpOpenCheckSignatureReqVO { | |
| 16 | + | |
| 17 | + @ApiModelProperty(value = "微信加密签名", required = true, example = "490eb57f448b87bd5f20ccef58aa4de46aa1908e") | |
| 18 | + @NotEmpty(message = "微信加密签名不能为空") | |
| 19 | + private String signature; | |
| 20 | + | |
| 21 | + @ApiModelProperty(value = "时间戳", required = true, example = "1672587863") | |
| 22 | + @NotEmpty(message = "时间戳不能为空") | |
| 23 | + private String timestamp; | |
| 24 | + | |
| 25 | + @ApiModelProperty(value = "随机数", required = true, example = "1827365808") | |
| 26 | + @NotEmpty(message = "随机数不能为空") | |
| 27 | + private String nonce; | |
| 28 | + | |
| 29 | + @ApiModelProperty(value = "随机字符串", required = true, example = "2721154047828672511") | |
| 30 | + @NotEmpty(message = "随机字符串不能为空") | |
| 31 | + @SuppressWarnings("SpellCheckingInspection") | |
| 32 | + private String echostr; | |
| 33 | + | |
| 34 | +} | ... | ... |
yudao-module-mp/yudao-module-mp-biz/src/main/java/cn/iocoder/yudao/module/mp/controller/package-info.java
0 → 100644
yudao-module-mp/yudao-module-mp-biz/src/main/java/cn/iocoder/yudao/module/mp/framework/mp/core/DefaultMpServiceFactory.java
| ... | ... | @@ -94,65 +94,65 @@ public class DefaultMpServiceFactory implements MpServiceFactory { |
| 94 | 94 | // 第二步,创建 WxMpService 对象 |
| 95 | 95 | WxMpService service = new WxMpServiceImpl(); |
| 96 | 96 | service.setWxMpConfigStorage(configStorage); |
| 97 | - return null; | |
| 97 | + return service; | |
| 98 | 98 | } |
| 99 | 99 | |
| 100 | 100 | private WxMpMessageRouter buildMpMessageRouter(WxMpService mpService) { |
| 101 | - final WxMpMessageRouter newRouter = new WxMpMessageRouter(mpService); | |
| 101 | + WxMpMessageRouter router = new WxMpMessageRouter(mpService); | |
| 102 | 102 | // 记录所有事件的日志(异步执行) |
| 103 | - newRouter.rule().handler(logHandler).next(); | |
| 103 | + router.rule().handler(logHandler).next(); | |
| 104 | 104 | |
| 105 | 105 | // 接收客服会话管理事件 |
| 106 | - newRouter.rule().async(false).msgType(WxConsts.XmlMsgType.EVENT) | |
| 106 | + router.rule().async(false).msgType(WxConsts.XmlMsgType.EVENT) | |
| 107 | 107 | .event(WxMpEventConstants.CustomerService.KF_CREATE_SESSION) |
| 108 | 108 | .handler(kfSessionHandler).end(); |
| 109 | - newRouter.rule().async(false).msgType(WxConsts.XmlMsgType.EVENT) | |
| 109 | + router.rule().async(false).msgType(WxConsts.XmlMsgType.EVENT) | |
| 110 | 110 | .event(WxMpEventConstants.CustomerService.KF_CLOSE_SESSION) |
| 111 | 111 | .handler(kfSessionHandler) |
| 112 | 112 | .end(); |
| 113 | - newRouter.rule().async(false).msgType(WxConsts.XmlMsgType.EVENT) | |
| 113 | + router.rule().async(false).msgType(WxConsts.XmlMsgType.EVENT) | |
| 114 | 114 | .event(WxMpEventConstants.CustomerService.KF_SWITCH_SESSION) |
| 115 | 115 | .handler(kfSessionHandler).end(); |
| 116 | 116 | |
| 117 | 117 | // 门店审核事件 |
| 118 | - newRouter.rule().async(false).msgType(WxConsts.XmlMsgType.EVENT) | |
| 118 | + router.rule().async(false).msgType(WxConsts.XmlMsgType.EVENT) | |
| 119 | 119 | .event(WxMpEventConstants.POI_CHECK_NOTIFY) |
| 120 | 120 | .handler(storeCheckNotifyHandler).end(); |
| 121 | 121 | |
| 122 | 122 | // 自定义菜单事件 |
| 123 | - newRouter.rule().async(false).msgType(WxConsts.XmlMsgType.EVENT) | |
| 123 | + router.rule().async(false).msgType(WxConsts.XmlMsgType.EVENT) | |
| 124 | 124 | .event(WxConsts.MenuButtonType.CLICK).handler(menuHandler).end(); |
| 125 | 125 | |
| 126 | 126 | // 点击菜单连接事件 |
| 127 | - newRouter.rule().async(false).msgType(WxConsts.XmlMsgType.EVENT) | |
| 127 | + router.rule().async(false).msgType(WxConsts.XmlMsgType.EVENT) | |
| 128 | 128 | .event(WxConsts.MenuButtonType.VIEW).handler(nullHandler).end(); |
| 129 | 129 | |
| 130 | 130 | // 关注事件 |
| 131 | - newRouter.rule().async(false).msgType(WxConsts.XmlMsgType.EVENT) | |
| 131 | + router.rule().async(false).msgType(WxConsts.XmlMsgType.EVENT) | |
| 132 | 132 | .event(WxConsts.EventType.SUBSCRIBE).handler(subscribeHandler) |
| 133 | 133 | .end(); |
| 134 | 134 | |
| 135 | 135 | // 取消关注事件 |
| 136 | - newRouter.rule().async(false).msgType(WxConsts.XmlMsgType.EVENT) | |
| 136 | + router.rule().async(false).msgType(WxConsts.XmlMsgType.EVENT) | |
| 137 | 137 | .event(WxConsts.EventType.UNSUBSCRIBE) |
| 138 | 138 | .handler(unsubscribeHandler).end(); |
| 139 | 139 | |
| 140 | 140 | // 上报地理位置事件 |
| 141 | - newRouter.rule().async(false).msgType(WxConsts.XmlMsgType.EVENT) | |
| 141 | + router.rule().async(false).msgType(WxConsts.XmlMsgType.EVENT) | |
| 142 | 142 | .event(WxConsts.EventType.LOCATION).handler(locationHandler) |
| 143 | 143 | .end(); |
| 144 | 144 | |
| 145 | 145 | // 接收地理位置消息 |
| 146 | - newRouter.rule().async(false).msgType(WxConsts.XmlMsgType.LOCATION) | |
| 146 | + router.rule().async(false).msgType(WxConsts.XmlMsgType.LOCATION) | |
| 147 | 147 | .handler(locationHandler).end(); |
| 148 | 148 | |
| 149 | 149 | // 扫码事件 |
| 150 | - newRouter.rule().async(false).msgType(WxConsts.XmlMsgType.EVENT) | |
| 150 | + router.rule().async(false).msgType(WxConsts.XmlMsgType.EVENT) | |
| 151 | 151 | .event(WxConsts.EventType.SCAN).handler(scanHandler).end(); |
| 152 | 152 | |
| 153 | 153 | // 默认 |
| 154 | - newRouter.rule().async(false).handler(msgHandler).end(); | |
| 155 | - return newRouter; | |
| 154 | + router.rule().async(false).handler(msgHandler).end(); | |
| 155 | + return router; | |
| 156 | 156 | } |
| 157 | 157 | |
| 158 | 158 | } | ... | ... |
yudao-module-mp/yudao-module-mp-biz/src/main/java/cn/iocoder/yudao/module/mp/framework/mp/core/MpServiceFactory.java
| 1 | 1 | package cn.iocoder.yudao.module.mp.framework.mp.core; |
| 2 | 2 | |
| 3 | +import cn.hutool.core.lang.Assert; | |
| 3 | 4 | import cn.iocoder.yudao.module.mp.dal.dataobject.account.MpAccountDO; |
| 4 | 5 | import me.chanjar.weixin.mp.api.WxMpMessageRouter; |
| 5 | 6 | import me.chanjar.weixin.mp.api.WxMpService; |
| ... | ... | @@ -28,6 +29,12 @@ public interface MpServiceFactory { |
| 28 | 29 | */ |
| 29 | 30 | WxMpService getMpService(String appId); |
| 30 | 31 | |
| 32 | + default WxMpService getRequiredMpService(String appId) { | |
| 33 | + WxMpService wxMpService = getMpService(appId); | |
| 34 | + Assert.notNull(wxMpService, "找到对应 appId({}) 的 WxMpService,请核实!", appId); | |
| 35 | + return wxMpService; | |
| 36 | + } | |
| 37 | + | |
| 31 | 38 | /** |
| 32 | 39 | * 获得 appId 对应的 WxMpMessageRouter 实例 |
| 33 | 40 | * | ... | ... |
yudao-server/src/main/resources/application.yaml
| ... | ... | @@ -92,6 +92,7 @@ yudao: |
| 92 | 92 | security: |
| 93 | 93 | permit-all_urls: |
| 94 | 94 | - /admin-ui/** # /resources/admin-ui 目录下的静态资源 |
| 95 | + - /admin-api/mp/open/** # 微信公众号开放平台,微信回调接口,不需要登录 | |
| 95 | 96 | swagger: |
| 96 | 97 | title: 管理后台 |
| 97 | 98 | description: 提供管理员管理的所有功能 |
| ... | ... | @@ -119,6 +120,7 @@ yudao: |
| 119 | 120 | - /admin-api/system/sms/callback/* # 短信回调接口,无法带上租户编号 |
| 120 | 121 | - /admin-api/pay/notify/callback/* # 支付回调通知,不携带租户编号 |
| 121 | 122 | - /jmreport/* # 积木报表,无法携带租户编号 |
| 123 | + - /admin-api/mp/open/** # 微信公众号开放平台,微信回调接口,无法携带租户编号 | |
| 122 | 124 | ignore-tables: |
| 123 | 125 | - system_tenant |
| 124 | 126 | - system_tenant_package | ... | ... |