mirror of
				https://github.com/YunaiV/ruoyi-vue-pro.git
				synced 2025-10-31 10:37:41 +08:00 
			
		
		
		
	mp:增加公众号的校验签名回调
This commit is contained in:
		| @ -0,0 +1,40 @@ | ||||
| package cn.iocoder.yudao.module.mp.controller.admin.open; | ||||
|  | ||||
| import cn.hutool.core.util.StrUtil; | ||||
| import cn.iocoder.yudao.module.mp.controller.admin.open.vo.MpOpenCheckSignatureReqVO; | ||||
| import cn.iocoder.yudao.module.mp.framework.mp.core.MpServiceFactory; | ||||
| import io.swagger.annotations.Api; | ||||
| import io.swagger.annotations.ApiOperation; | ||||
| import lombok.extern.slf4j.Slf4j; | ||||
| import me.chanjar.weixin.mp.api.WxMpService; | ||||
| import org.springframework.validation.annotation.Validated; | ||||
| import org.springframework.web.bind.annotation.*; | ||||
|  | ||||
| import javax.annotation.Resource; | ||||
|  | ||||
| @Api(tags = "管理后台 - 公众号回调") | ||||
| @RestController | ||||
| @RequestMapping("/mp/open") | ||||
| @Validated | ||||
| @Slf4j | ||||
| public class MpOpenController { | ||||
|  | ||||
|     @Resource | ||||
|     private MpServiceFactory mpServiceFactory; | ||||
|  | ||||
|     @ApiOperation("校验签名") // 参见 https://developers.weixin.qq.com/doc/offiaccount/Basic_Information/Access_Overview.html 文档 | ||||
|     @GetMapping(value = "/{appId}", produces = "text/plain;charset=utf-8") | ||||
|     public String checkSignature(@PathVariable("appId") String appId, | ||||
|                                  MpOpenCheckSignatureReqVO reqVO) { | ||||
|         log.info("[checkSignature][appId({}) 接收到来自微信服务器的认证消息({})]", appId, reqVO); | ||||
|         // 校验请求签名 | ||||
|         WxMpService wxMpService = mpServiceFactory.getRequiredMpService(appId); | ||||
|         // 校验通过 | ||||
|         if (wxMpService.checkSignature(reqVO.getTimestamp(), reqVO.getNonce(), reqVO.getSignature())) { | ||||
|             return reqVO.getEchostr(); | ||||
|         } | ||||
|         // 校验不通过 | ||||
|         return "非法请求"; | ||||
|     } | ||||
|  | ||||
| } | ||||
| @ -0,0 +1,34 @@ | ||||
| package cn.iocoder.yudao.module.mp.controller.admin.open.vo; | ||||
|  | ||||
|  | ||||
| import io.swagger.annotations.ApiModel; | ||||
| import io.swagger.annotations.ApiModelProperty; | ||||
| import lombok.Data; | ||||
| import lombok.EqualsAndHashCode; | ||||
| import lombok.ToString; | ||||
| import org.springframework.web.bind.annotation.RequestParam; | ||||
|  | ||||
| import javax.validation.constraints.NotEmpty; | ||||
|  | ||||
| @ApiModel("管理后台 - 公众号校验签名 Request VO") | ||||
| @Data | ||||
| public class MpOpenCheckSignatureReqVO { | ||||
|  | ||||
|     @ApiModelProperty(value = "微信加密签名", required = true, example = "490eb57f448b87bd5f20ccef58aa4de46aa1908e") | ||||
|     @NotEmpty(message = "微信加密签名不能为空") | ||||
|     private String signature; | ||||
|  | ||||
|     @ApiModelProperty(value = "时间戳", required = true, example = "1672587863") | ||||
|     @NotEmpty(message = "时间戳不能为空") | ||||
|     private String timestamp; | ||||
|  | ||||
|     @ApiModelProperty(value = "随机数", required = true, example = "1827365808") | ||||
|     @NotEmpty(message = "随机数不能为空") | ||||
|     private String nonce; | ||||
|  | ||||
|     @ApiModelProperty(value = "随机字符串", required = true, example = "2721154047828672511") | ||||
|     @NotEmpty(message = "随机字符串不能为空") | ||||
|     @SuppressWarnings("SpellCheckingInspection") | ||||
|     private String echostr; | ||||
|  | ||||
| } | ||||
| @ -0,0 +1,6 @@ | ||||
| /** | ||||
|  * 提供 RESTful API 给前端: | ||||
|  * 1. admin 包:提供给管理后台 yudao-ui-admin 前端项目 | ||||
|  * 2. app 包:提供给用户 APP yudao-ui-app 前端项目,它的 Controller 和 VO 都要添加 App 前缀,用于和管理后台进行区分 | ||||
|  */ | ||||
| package cn.iocoder.yudao.module.mp.controller; | ||||
| @ -94,65 +94,65 @@ public class DefaultMpServiceFactory implements MpServiceFactory { | ||||
|         // 第二步,创建 WxMpService 对象 | ||||
|         WxMpService service = new WxMpServiceImpl(); | ||||
|         service.setWxMpConfigStorage(configStorage); | ||||
|         return null; | ||||
|         return service; | ||||
|     } | ||||
|  | ||||
|     private WxMpMessageRouter buildMpMessageRouter(WxMpService mpService) { | ||||
|         final WxMpMessageRouter newRouter = new WxMpMessageRouter(mpService); | ||||
|         WxMpMessageRouter router = new WxMpMessageRouter(mpService); | ||||
|         // 记录所有事件的日志(异步执行) | ||||
|         newRouter.rule().handler(logHandler).next(); | ||||
|         router.rule().handler(logHandler).next(); | ||||
|  | ||||
|         // 接收客服会话管理事件 | ||||
|         newRouter.rule().async(false).msgType(WxConsts.XmlMsgType.EVENT) | ||||
|         router.rule().async(false).msgType(WxConsts.XmlMsgType.EVENT) | ||||
|                 .event(WxMpEventConstants.CustomerService.KF_CREATE_SESSION) | ||||
|                 .handler(kfSessionHandler).end(); | ||||
|         newRouter.rule().async(false).msgType(WxConsts.XmlMsgType.EVENT) | ||||
|         router.rule().async(false).msgType(WxConsts.XmlMsgType.EVENT) | ||||
|                 .event(WxMpEventConstants.CustomerService.KF_CLOSE_SESSION) | ||||
|                 .handler(kfSessionHandler) | ||||
|                 .end(); | ||||
|         newRouter.rule().async(false).msgType(WxConsts.XmlMsgType.EVENT) | ||||
|         router.rule().async(false).msgType(WxConsts.XmlMsgType.EVENT) | ||||
|                 .event(WxMpEventConstants.CustomerService.KF_SWITCH_SESSION) | ||||
|                 .handler(kfSessionHandler).end(); | ||||
|  | ||||
|         // 门店审核事件 | ||||
|         newRouter.rule().async(false).msgType(WxConsts.XmlMsgType.EVENT) | ||||
|         router.rule().async(false).msgType(WxConsts.XmlMsgType.EVENT) | ||||
|                 .event(WxMpEventConstants.POI_CHECK_NOTIFY) | ||||
|                 .handler(storeCheckNotifyHandler).end(); | ||||
|  | ||||
|         // 自定义菜单事件 | ||||
|         newRouter.rule().async(false).msgType(WxConsts.XmlMsgType.EVENT) | ||||
|         router.rule().async(false).msgType(WxConsts.XmlMsgType.EVENT) | ||||
|                 .event(WxConsts.MenuButtonType.CLICK).handler(menuHandler).end(); | ||||
|  | ||||
|         // 点击菜单连接事件 | ||||
|         newRouter.rule().async(false).msgType(WxConsts.XmlMsgType.EVENT) | ||||
|         router.rule().async(false).msgType(WxConsts.XmlMsgType.EVENT) | ||||
|                 .event(WxConsts.MenuButtonType.VIEW).handler(nullHandler).end(); | ||||
|  | ||||
|         // 关注事件 | ||||
|         newRouter.rule().async(false).msgType(WxConsts.XmlMsgType.EVENT) | ||||
|         router.rule().async(false).msgType(WxConsts.XmlMsgType.EVENT) | ||||
|                 .event(WxConsts.EventType.SUBSCRIBE).handler(subscribeHandler) | ||||
|                 .end(); | ||||
|  | ||||
|         // 取消关注事件 | ||||
|         newRouter.rule().async(false).msgType(WxConsts.XmlMsgType.EVENT) | ||||
|         router.rule().async(false).msgType(WxConsts.XmlMsgType.EVENT) | ||||
|                 .event(WxConsts.EventType.UNSUBSCRIBE) | ||||
|                 .handler(unsubscribeHandler).end(); | ||||
|  | ||||
|         // 上报地理位置事件 | ||||
|         newRouter.rule().async(false).msgType(WxConsts.XmlMsgType.EVENT) | ||||
|         router.rule().async(false).msgType(WxConsts.XmlMsgType.EVENT) | ||||
|                 .event(WxConsts.EventType.LOCATION).handler(locationHandler) | ||||
|                 .end(); | ||||
|  | ||||
|         // 接收地理位置消息 | ||||
|         newRouter.rule().async(false).msgType(WxConsts.XmlMsgType.LOCATION) | ||||
|         router.rule().async(false).msgType(WxConsts.XmlMsgType.LOCATION) | ||||
|                 .handler(locationHandler).end(); | ||||
|  | ||||
|         // 扫码事件 | ||||
|         newRouter.rule().async(false).msgType(WxConsts.XmlMsgType.EVENT) | ||||
|         router.rule().async(false).msgType(WxConsts.XmlMsgType.EVENT) | ||||
|                 .event(WxConsts.EventType.SCAN).handler(scanHandler).end(); | ||||
|  | ||||
|         // 默认 | ||||
|         newRouter.rule().async(false).handler(msgHandler).end(); | ||||
|         return newRouter; | ||||
|         router.rule().async(false).handler(msgHandler).end(); | ||||
|         return router; | ||||
|     } | ||||
|  | ||||
| } | ||||
|  | ||||
| @ -1,5 +1,6 @@ | ||||
| package cn.iocoder.yudao.module.mp.framework.mp.core; | ||||
|  | ||||
| import cn.hutool.core.lang.Assert; | ||||
| import cn.iocoder.yudao.module.mp.dal.dataobject.account.MpAccountDO; | ||||
| import me.chanjar.weixin.mp.api.WxMpMessageRouter; | ||||
| import me.chanjar.weixin.mp.api.WxMpService; | ||||
| @ -28,6 +29,12 @@ public interface MpServiceFactory { | ||||
|      */ | ||||
|     WxMpService getMpService(String appId); | ||||
|  | ||||
|     default WxMpService getRequiredMpService(String appId) { | ||||
|         WxMpService wxMpService = getMpService(appId); | ||||
|         Assert.notNull(wxMpService, "找到对应 appId({}) 的 WxMpService,请核实!", appId); | ||||
|         return wxMpService; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * 获得 appId 对应的 WxMpMessageRouter 实例 | ||||
|      * | ||||
|  | ||||
| @ -92,6 +92,7 @@ yudao: | ||||
|   security: | ||||
|     permit-all_urls: | ||||
|       - /admin-ui/** # /resources/admin-ui 目录下的静态资源 | ||||
|       - /admin-api/mp/open/** # 微信公众号开放平台,微信回调接口,不需要登录 | ||||
|   swagger: | ||||
|     title: 管理后台 | ||||
|     description: 提供管理员管理的所有功能 | ||||
| @ -119,6 +120,7 @@ yudao: | ||||
|       - /admin-api/system/sms/callback/* # 短信回调接口,无法带上租户编号 | ||||
|       - /admin-api/pay/notify/callback/* # 支付回调通知,不携带租户编号 | ||||
|       - /jmreport/* # 积木报表,无法携带租户编号 | ||||
|       - /admin-api/mp/open/** # 微信公众号开放平台,微信回调接口,无法携带租户编号 | ||||
|     ignore-tables: | ||||
|       - system_tenant | ||||
|       - system_tenant_package | ||||
|  | ||||
		Reference in New Issue
	
	Block a user
	 YunaiV
					YunaiV