业务实践系列(九):微信公众号相关开发

star2017 1年前 ⋅ 87 阅读

最近对接了个微信公众号,access_token,open_id,带参二维码,事件推送,自定义菜单。做个记录。

微信官方文档 | 公众号:开发文档

网页授权

通过code换取网页授权access_token

获取access_token

此 access_token 不是网页授权的 access_token,是访问微信接口必传的 token。

带参二维码

公众号带参二维码

Controller 接口:

/**
 * @desc: 微信公众号授权
 */
@RestController
@RequestMapping("/wx")
public class WxAuthController {
    private static final Logger logger = LogManager.getLogger(WxAuthController.class);

    @Autowired
    private WxAuthService wxAuthService;
    @Autowired
    private AuthProperties authProperties;

    /**
     * @desc: code换取网关授权acces_token,这里返回openId
     * @param: [wxWebAuth]
     */
    @PostMapping("/openId")
    public ResponseModel<?> openId(@RequestBody @Validated WxWebAuth wxWebAuth) {
        if (!wxWebAuth.getState().equals(authProperties.getWxState())) {
            return ResponseModel.fail("state 值错误");
        }
        WxAccessToken wxAccessToken = wxAuthService.getWxOpenId(wxWebAuth);
        return ResponseModel.success(new WxOpenId(wxAccessToken.getOpenid()));
    }

    /**
     * @desc: 获取微信 accessToken
     */
    /*@PostMapping("/accessToken")
    public ResponseModel<?> accessToken(String state) {
        if (!state.equals(authProperties.getWxState())) {
            return ResponseModel.fail("state 值错误");
        }
        return ResponseModel.success(wxAuthService.getWxAccessToken());
    }*/

    /**
     * @desc: 带参的公众号二维码
     */
    @PostMapping("/qrcode/user")
    public ResponseModel<WxQrCodeUrl> getUserWxQrCode(@RequestBody @Validated IdVO idVO) {
        WxQrCodeUrl wxQrCodeUrl = wxAuthService.getUserWxQrCode(idVO.getId());
        return ResponseModel.success(wxQrCodeUrl);
    }
}

Service 实现:

/**
 * @desc: 微信授权
 */
@Service
public class WxAuthServiceImpl implements WxAuthService {
    private static final Logger logger = LogManager.getLogger(WxAuthServiceImpl.class);

    // 微信用户open_id的接口域名
    private static final String WX_WEB_AUTH_DOMAIN = "https://api.weixin.qq.com/sns/oauth2/access_token";
    // 微信公众号接口access_token域名
    private static final String WX_ACCESS_TOKEN_DOMAIN = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential";
    // 微信获取二维码ticket域名
    private static final String WX_QRCODE_TICKET_DOMAIN = "https://api.weixin.qq.com/cgi-bin/qrcode/create?access_token=";

    @Autowired
    private AuthProperties authProperties;
    @Autowired
    private RestTemplate restTemplate;


    /**
     * @desc: code换取网关授权acces_token,这里返回openId
     * @param: [wxWebAuth]
     */
    @Override
    public WxAccessToken getWxOpenId(WxWebAuth wxWebAuth) {
        String wxWebAuthUrl = this.getWxWebAuthUrl(wxWebAuth);
        ResponseEntity<String> entity = restTemplate.getForEntity(wxWebAuthUrl, String.class, (Object) null);
        WxAccessToken wxAccessToken = JSON.parseObject(entity.getBody(), WxAccessToken.class);
        logger.info("----->微信公众号网页授权响应:{}", JSON.toJSONString(wxAccessToken));
        if (StringUtils.isNotBlank(wxAccessToken.getOpenid())) {
            return wxAccessToken;
        } else {
            logger.error("----->获取access_token异常:{}", wxWebAuthUrl);
            throw new BusinessException("获取access_token异常");
        }
    }

    /**
     * @desc: 获取微信 accessToken
     */
    @Override
    public WxAccessToken getWxAccessToken() {
        String wxAccessTokenUrl = getWxAccessTokenUrl();
        ResponseEntity<String> entity = restTemplate.getForEntity(wxAccessTokenUrl, String.class, (Object) null);
        String body = entity.getBody();
        WxAccessToken wxAccessToken = JSON.parseObject(body, WxAccessToken.class);
        logger.info("----->获取微信accessToken:{}", JSON.toJSONString(wxAccessToken));
        if (StringUtils.isNotBlank(wxAccessToken.getAccessToken())) {
            return wxAccessToken;
        } else {
            logger.error("----->获取access_token异常:{}", wxAccessTokenUrl);
            throw new BusinessException("获取access_token异常");
        }
    }

    /**
     * @desc: 获取带参公众号二维码
     * @param: [id]
     */
    @Override
    public WxQrCodeUrl getUserWxQrCode(Long id) {
        String ticketUrl = WX_QRCODE_TICKET_DOMAIN + this.getWxAccessToken().getAccessToken();
        WxTicketScene wxTicketScene = new WxTicketScene(id);
        WxTicketActionInfo wxTicketActionInfo = new WxTicketActionInfo(wxTicketScene);
        WxTicketRequest wxTicketRequest = new WxTicketRequest(wxTicketActionInfo);

        HttpHeaders headers = new HttpHeaders();
        MediaType type = MediaType.parseMediaType("application/json; charset=UTF-8");
        headers.setContentType(type);
        ObjectMapper mapper = new ObjectMapper();
        try {
            String requestParams = mapper.writeValueAsString(wxTicketRequest);
            HttpEntity<String> formEntity = new HttpEntity<>(requestParams, headers);
            ResponseEntity<String> entity = restTemplate.postForEntity(ticketUrl, formEntity, String.class);
            WxQrCodeUrl wxQrCodeUrl = JSON.parseObject(entity.getBody(), WxQrCodeUrl.class);
            logger.info("----->获取公众号二维码,响应:{}", JSON.toJSONString(wxQrCodeUrl));
            if (StringUtils.isBlank(wxQrCodeUrl.getUrl())) {
                logger.error("----->获取公众号二维码,响应异常:{}", JSON.toJSONString(wxQrCodeUrl));
                throw new BusinessException("获取公众号二维码失败,请重试");
            }
            return wxQrCodeUrl;
        } catch (JsonProcessingException e) {
            e.printStackTrace();
            logger.error("----->获取公众号二维码请求参数序列化错误:{}", JSON.toJSONString(wxTicketRequest));
            throw new BusinessException("请求参数序列化错误");
        }
    }

    /**
     * @desc: 拼接访问URL
     */
    public String getWxWebAuthUrl(WxWebAuth wxWebAuth) {
        StringBuilder sb = new StringBuilder(WX_WEB_AUTH_DOMAIN);
        sb.append("?appid=")
                .append(authProperties.getWxAppid())
                .append("&secret=")
                .append(authProperties.getWxSecret())
                .append("&code=")
                .append(wxWebAuth.getCode())
                .append("&grant_type=authorization_code");
        return sb.toString();
    }

    /**
     * @desc: 获取微信公众号接口access_token域名
     */
    public String getWxAccessTokenUrl() {
        StringBuilder sb = new StringBuilder(WX_ACCESS_TOKEN_DOMAIN);
        sb.append("&appid=")
                .append(authProperties.getWxAppid())
                .append("&secret=")
                .append(authProperties.getWxSecret());
        return sb.toString();
    }
}

推送消息

下面示例包括未关注扫码推送消息已关注扫码推送消息,取消关注推送消息 的处理。

Controller 接口:

/**
 * @desc: 微信消息
 * @author: gxing
 * @date: 2020/9/14
 */
@RestController
@RequestMapping("/wx")
public class WxMsgController {
    private static final Logger logger = LogManager.getLogger(WxMsgController.class);

    @Autowired
    private WxMsgService wxMsgService;
    @Autowired
    private AuthProperties authProperties;

    /**
     * 微信消息接收和token验证
     *
     * @param request
     * @param response
     * @throws IOException
     */
    @GetMapping(value = "/msg/invoke")
    public String checkToken(HttpServletRequest request, HttpServletResponse response) throws IOException {
        // 微信加密签名
        String signature = request.getParameter("signature");
        // 时间戳
        String timestamp = request.getParameter("timestamp");
        // 随机数
        String nonce = request.getParameter("nonce");
        // 随机字符串
        String echostr = request.getParameter("echostr");
        // 通过检验signature对请求进行校验,若校验成功则原样返回echostr,表示接入成功,否则接入失败
        String token = authProperties.getWxVerifyToken();
        String[] strArray = {token, timestamp, nonce};
        // 1.排序
        Arrays.sort(strArray);
        // 2.sha1加密
        String mySignature = Hex.encodeHexString(DigestUtils.digest(DigestUtils.getSha1Digest(),
                (StringUtil.join(strArray, StringUtils.EMPTY)).getBytes(Charsets.UTF_8)));
        // 3.字符串校验
        if (mySignature.equals(signature)) {
            // 如果检验成功原样返回echostr,微信服务器接收到此输出,才会确认检验完成。
            return echostr;
        } else {
            return StringUtil.EMPTY;
        }
    }

    /**
     * 接收微信推送的消息
     *
     * @param request
     * @param response
     * @throws IOException
     */
    @PostMapping(value = "/msg/invoke")
    public String pushMsg(HttpServletRequest request, HttpServletResponse response) throws IOException {
        ServletInputStream inputStream = request.getInputStream();
        String msg = IOUtils.toString(inputStream, StandardCharsets.UTF_8);
        logger.info("----->接收到微信公众号推送的消息:{}", msg);
        this.wxMsgService.wxMsgPush(msg);
        return "success";
    }
}

Service 实现:

下面示例包括未关注扫码推送消息,已关注扫码推送消息,取消关注推送消息

/**
 * @desc: 微信消息服务
 */
@Service
public class WxMsgServiceImpl implements WxMsgService {
    private static final Logger logger = LogManager.getLogger(WxMsgController.class);

    // 事件KEY值,qrscene_为前缀,后面为二维码的参数值
    private static final String EVENT_PRE = "qrscene_";

    @Autowired
    private ManagerService managerService;


    /**
     * @desc: 微信消息推送
     * @param: [msg]
     */
    @Override
    public ResponseModel<?> wxMsgPush(String msg) {
        Map<String, String> eventMap = null;
        try {
            eventMap = WXUtil.xmlToMap(msg);
        } catch (Exception e) {
            logger.error("----->微信推送消息转Map错误:{}", msg);
            return ResponseModel.fail();
        }

        String msgType = eventMap.get("MsgType");
        if (msgType.equals(WxMsgTypeEnums.EVENT.getType())) {
            logger.info("----->消息类型:{}", msgType);
            String event = eventMap.get("Event");
            logger.info("----->事件类型:{}", event);
            if (StringUtils.equalsAny(event, WxEventEnums.SUBSCRIBE.getEvent(), WxEventEnums.SCAN.getEvent())) {
                // 关注处理
                return this.subscribe(eventMap, event);
            } else if (event.equals(WxEventEnums.UNSUBSCRIBE.getEvent())) {
                // 取消关注处理
                return this.unsubscribe(eventMap);
            } else {
                logger.warn("----->微信推送事件类型不是关注或取消关注类型");
                return ResponseModel.fail();
            }
        } else {
            return ResponseModel.fail();
        }
    }

    /**
     * @desc: 关注公众号事件
     * @param: [eventMap]
     */
    private ResponseModel<?> subscribe(Map<String, String> eventMap, String event) {
        String eventKey = eventMap.get("EventKey");
        Long userId = null;

        if (event.equals(WxEventEnums.SUBSCRIBE.getEvent())) {
            logger.info("----->员工未关注公众号,扫码关注绑定");
            userId = Long.valueOf(eventKey.split("_")[1]);
        }
        if (event.equals(WxEventEnums.SCAN.getEvent())) {
            logger.info("----->员工已关注公众号,扫码关注绑定");
            userId = Long.valueOf(eventKey);
        }

        String openId = eventMap.get("FromUserName");
        ResponseModel<?> responseModel = managerService.userSubscribe(new BzStaffOpenIdVO(userId, openId));
        logger.info("----->员工绑定公众号:{}", JSON.toJSONString(responseModel));
        return responseModel;

    }

    /**
     * @desc: 取消关注公众号事件
     * @param: [eventMap]
     */
    private ResponseModel<?> unsubscribe(Map<String, String> eventMap) {
        String openId = eventMap.get("FromUserName");
        ResponseModel<?> responseModel = managerService.userUnsubscribe(new OpenIdVO(openId));
        logger.info("----->员工取消关注公众号解绑:{}", JSON.toJSONString(responseModel));
        if (responseModel.isSuccess()) {
            logger.info("----->员工取消关注公众号解绑成功 openId:{}", openId);
        } else {
            logger.info("----->员工取消关注公众号解绑失败 openId:{}", openId);
        }
        return responseModel;
    }
}

创建自定义菜单

要接收微信推送的消息,就必须在微信公众号平台 开启 服务器配置,此情况下就不能使用微信的自定义菜单,必须走开发模式调微信接口来创建菜单。

创建菜单接口

Controller 接口:

@RestController
@RequestMapping("/wx")
public class WxMenuController {

    @Autowired
    private WxMenuService wxMenuService;
    @Autowired
    private AuthProperties authProperties;

    /**
     * @desc: 创建菜单
     * @param: [token]
     */
    @PostMapping("/createMenu")
    public ResponseModel<?> createMenu(String state) {
        if (StringUtils.isBlank(state) || !state.equals(authProperties.getWxState())) {
            return ResponseModel.fail("Hello World");
        }
        boolean createFlag = false;
        try {
            ResourceLoader resourceLoader = new DefaultResourceLoader();
            Resource resource = resourceLoader.getResource("classpath:/wxmenus/baseMenu.json");
            InputStream is = resource.getInputStream();
            String menuJson = IOUtils.toString(is, Charsets.UTF_8);
            menuJson = menuJson.replace("APPID", authProperties.getWxAppid())
                    .replace("REDIRECT_URI", authProperties.getWxRedirectUri())
                    .replace("STATE", authProperties.getWxState());
            createFlag = wxMenuService.createDiyMenu(menuJson);
        } catch (IOException e) {
            e.printStackTrace();
            throw new BusinessException("----->读取自定义菜单json异常");
        }
        if (createFlag) {
            return ResponseModel.success();
        }
        return ResponseModel.fail("创建自定义菜单失败");
    }
}

Service 实现:

public class WxMenuServiceImpl implements WxMenuService {
    private static final Logger logger = LogManager.getLogger(WxMsgServiceImpl.class);

    /**
     * 创建自定义菜单URL
     */
    private static final String createDiyMenuUrl = "https://api.weixin.qq.com/cgi-bin/menu/create?access_token=ACCESS_TOKEN";

    @Autowired
    private WxAuthService wxAuthService;
    @Autowired
    private RestTemplate restTemplate;

    /**
     * @desc: 创建菜单
     * @param: [menuJson]
     */
    @Override
    public boolean createDiyMenu(String menuJson) {
        WxAccessToken wxAccessToken = wxAuthService.getWxAccessToken();
        String url = createDiyMenuUrl.replace("ACCESS_TOKEN", wxAccessToken.getAccessToken());
        HttpHeaders headers = new HttpHeaders();
        MediaType type = MediaType.parseMediaType("application/json; charset=UTF-8");
        headers.setContentType(type);
        logger.info("----->创建自定义菜单json:{}", menuJson);
        HttpEntity<String> formEntity = new HttpEntity<>(menuJson, headers);
        ResponseEntity<String> entity = restTemplate.postForEntity(url, formEntity, String.class);
        String body = entity.getBody();
        logger.info("----->创建自定义菜返回结果:{}", body);
        JSONObject jsonObject = JSON.parseObject(body);
        Integer errcode = (Integer) jsonObject.get("errcode");
        if (errcode == 0) {
            return true;
        }
        return false;
    }
}

菜单json文件

{
    "button": [
        {
            "name": "便利保障",
            "sub_button": [
                {
                    "type": "view",
                    "name": "我的订单",
                    "url": "https://open.weixin.qq.com/connect/oauth2/authorize?appid=APPID&redirect_uri=REDIRECT_URI&response_type=code&scope=snsapi_base&state=STATE#wechat_redirect"
                }
            ]
        }
    ]
}

相关示例

自定义菜单

菜单JSON

{
    "button": [
        {
            "name": "心理测评",
            "sub_button": [
                {
                    "type": "view",
                    "name": "开始测评",
                    "url": "https://open.weixin.qq.com/connect/oauth2/authorize?appid=${appid}&redirect_uri=${frontBaseUrl}&response_type=code&scope=snsapi_userinfo&state=STATE#wechat_redirect"
                },
                {
                    "type": "view",
                    "name": "我的测评",
                    "url": "https://open.weixin.qq.com/connect/oauth2/authorize?appid=${appid}&redirect_uri=${frontBaseUrl}/myEvaluationList&response_type=code&scope=snsapi_userinfo&state=state#wechat_redirect"
                }
            ]
        },
        {
            "name": "我的",
            "sub_button": [
                {
                    "type": "view",
                    "name": "个人信息",
                    "url": "https://open.weixin.qq.com/connect/oauth2/authorize?appid=${appid}&redirect_uri=${frontBaseUrl}/userPersonalInfo&response_type=code&scope=snsapi_userinfo&state=state#wechat_redirect"
                },
                {
                    "type": "view",
                    "name": "我的咨询师",
                    "url": "https://open.weixin.qq.com/connect/oauth2/authorize?appid=${appid}&redirect_uri=${frontBaseUrl}/counselorList&response_type=code&scope=snsapi_userinfo&state=state#wechat_redirect"
                },
                {
                    "type": "view",
                    "name": "注册咨询师",
                    "url": "https://open.weixin.qq.com/connect/oauth2/authorize?appid=${appid}&redirect_uri=${frontBaseUrl}/regEntry&response_type=code&scope=snsapi_userinfo&state=state#wechat_redirect"
                },
                {
                    "type": "view",
                    "name": "邀请加入",
                    "url": "https://open.weixin.qq.com/connect/oauth2/authorize?appid=${appid}&redirect_uri=${frontBaseUrl}/teamQRCode?type=1&response_type=code&scope=snsapi_userinfo&state=state#wechat_redirect"
                },
                {
                    "type": "view",
                    "name": "关于",
                    "url": "https://open.weixin.qq.com/connect/oauth2/authorize?appid=${appid}&redirect_uri=${frontBaseUrl}/about&response_type=code&scope=snsapi_userinfo&state=state#wechat_redirect"
                }
            ]
        }
    ]
}

Controller 接口:

/**
 * 微信自定义菜单,个性化菜单Controller
 */
@ApiIgnore
@RestController
@RequestMapping("/wechat/menu")
public class WechatMenuController {

    @Autowired
    private WechatMenuService wechatMenuService;

    @Autowired
    private WechatTagService wechatTagService;

    @Autowired
    private WechatProperties wechatProperties;

    /**
     * 创建基础(普通用户)菜单
     * 
     * @return
     * @throws IOException
     */
    @PostMapping("/create-base")
    public ResponseModel<?> createBaseMenu(String pwd) throws IOException {
        if(!StringUtil.equals(pwd, "Clear1234")) {
            return ResponseModel.fail("黄河之水天上来");
        }
        boolean createFlag = false;
        ResourceLoader resourceLoader = new DefaultResourceLoader();
        Resource resource = resourceLoader.getResource("classpath:/vmenus/baseMenu.json");
        try (InputStream is = resource.getInputStream()) {
            String menuJson = IOUtils.toString(is, Charsets.UTF_8);
            menuJson = menuJson.replace("${appid}", wechatProperties.getMp().getAppId()).replace("${frontBaseUrl}",
                    wechatProperties.getMp().getFrontBaseUrl());
            createFlag = wechatMenuService.createDiyMenu(menuJson);
        }
        return ResponseModel.success(createFlag);
    }

    /**
     * 创建咨询师菜单
     * 
     * @return
     * @throws IOException
     */
    @PostMapping("/create-counselor")
    public ResponseModel<?> createCounselorMenu(String pwd) throws IOException {
        if(!StringUtil.equals(pwd, "Clear1234")) {
            return ResponseModel.fail("奔流到海不复回");
        }
        ResourceLoader resourceLoader = new DefaultResourceLoader();
        Resource resource = resourceLoader.getResource("classpath:/vmenus/counselorMenu.json");
        String menuJson = IOUtils.toString(resource.getInputStream(), Charsets.UTF_8);
        Integer tagId = wechatTagService.getCounselorTag().getTagId();
        menuJson = menuJson.replace("${appid}", wechatProperties.getMp().getAppId())
                .replace("${frontBaseUrl}", wechatProperties.getMp().getFrontBaseUrl())
                .replace("${tagId}", String.valueOf(tagId));
        String menuId = wechatMenuService.createIndividuationMenu(menuJson);
        Map<String, String> resultMap = new HashMap<>();
        resultMap.put("menuId", menuId);
        return ResponseModel.success(resultMap);
    }
}

Service 接口:

/**
 * 微信公众平台回调业务分发业务及相关业务实现
 */
@Slf4j
@Service
public class WechatMpInvokeServiceImpl implements WechatMpInvokeService {

    @Autowired
    private WechatProperties wechatProperties;

    @Override
    public String validateSign(HttpServletRequest request) {
        // 微信加密签名
        String signature = HttpUtil.getRequestParameter(request, "signature");
        // 时间戳
        String timestamp = HttpUtil.getRequestParameter(request, "timestamp");
        // 随机数
        String nonce = HttpUtil.getRequestParameter(request, "nonce");
        // 随机字符串
        String echostr = HttpUtil.getRequestParameter(request, "echostr");
        if (log.isInfoEnabled()) {
            log.info("微信-开始校验签名,收到来自微信的echostr字符串:{}", echostr);
        }
        // 1.排序
        String token = wechatProperties.getMp().getServerVerifyToken();
        String[] strArray = { token, timestamp, nonce };
        Arrays.sort(strArray);
        // 2.sha1加密
        String mySignature = Hex.encodeHexString(DigestUtils.digest(DigestUtils.getSha1Digest(),
                (StringUtil.join(strArray, StringUtil.EMPTY)).getBytes(Charsets.UTF_8)));
        // 3.字符串校验
        if (StringUtil.equalsIgnoreCase(mySignature, signature)) {
            // 如果检验成功原样返回echostr,微信服务器接收到此输出,才会确认检验完成。
            if (log.isInfoEnabled()) {
                log.info("微信-签名校验通过,回复给微信的echostr字符串:{}", echostr);
            }
            return echostr;
        } else {
            log.error("微信-签名校验失败");
            return StringUtil.EMPTY;
        }
    }

    @Override
    public String porcessInvokeMessage(String content) {
        String responseContent = null;
        List<WechatMpInvokeMessageProcessor> processors = WechatMpInvokeMessageProcessorFactory.getProcessors(content);
        if (BeanUtil.isNotEmpty(processors)) {
            WechatInvokeMessage wechatInvokeMessage = WechatMpInvokeMessageProcessorFactory.getWechatInvokeMessage();
            for (WechatMpInvokeMessageProcessor processor : processors) {
                String result = processor.process(wechatInvokeMessage);
                System.out.println("--------------------------------------\n" + result);
                if (StringUtil.isNotEmpty(result)) {
                    responseContent = result;
                }
            }
        }
        WechatMpInvokeMessageProcessorFactory.removeWechatInvokeMessage();
        return responseContent;
    }
}

微信消息推送

Controller 接口:

/**
 * 微信公众平台相关Controller
 */
@Slf4j
@ApiIgnore
@RestController
@RequestMapping("/wechat")
public class WechatMpInvokeController {

    @Autowired
    private WechatMpInvokeService wechatMpInvokeService;

    /**
     * 微信公众平台开发-基本配置-服务器配置-服务器地址启用校验
     * 
     * @param request
     * @param response
     * @return
     */
    @GetMapping("/invoke")
    public String invokeValidate(HttpServletRequest request, HttpServletResponse response) {
        return wechatMpInvokeService.validateSign(request);
    }

    /**
     * 接收/处理微信服务器消息
     * 
     * <pre>
     * {@code
     * 1、用户发送的文本、图片、语音、视频等消息:
     * <xml>
     *   <ToUserName><![CDATA[gh_511a0c58efbd]]></ToUserName>
     *   <FromUserName><![CDATA[oVH4bweVY1Sgq2zvKuyWBPV3pQaI]]></FromUserName>
     *   <CreateTime>1564817259</CreateTime>
     *   <MsgType><![CDATA[text]]></MsgType>
     *   <Content><![CDATA[?]]></Content>
     *   <MsgId>22402794709385993</MsgId>
     * </xml>
     * 
     * 2、扫码关注公众号通知消息:
     * <xml>
     *   <ToUserName><![CDATA[gh_511a0c58efbd]]></ToUserName>
     *   <FromUserName><![CDATA[oVH4bwUa8FZeXpXRI2mj_3cDMf00]]></FromUserName>
     *   <CreateTime>1564817112</CreateTime>
     *   <MsgType><![CDATA[event]]></MsgType>
     *   <Event><![CDATA[subscribe]]></Event>
     *   <EventKey><![CDATA[]]></EventKey>
     * </xml>
     * }
     * </pre>
     * 
     * @param request
     * @param response
     * @return
     * @see https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421140453
     */
    @PostMapping("/invoke")
    public String invoke(HttpServletRequest request, HttpServletResponse response) {
        try (ServletInputStream inputStream = request.getInputStream()) {
            String content = IOUtils.toString(inputStream, Charsets.UTF_8);
            if (log.isInfoEnabled()) {
                log.info("接收到微信公众平台消息:\n{}", content);
            }
            String responseContent = wechatMpInvokeService.porcessInvokeMessage(content);
            if (StringUtil.isNotEmpty(responseContent)) {
                return responseContent;
            }
        } catch (Exception ex) {
            log.error("接收/处理微信公众平台服务器消息时发生异常", ex);
        }
        return "success";
    }
}

Service 实现:

/**
 * 微信公众平台回调业务分发业务及相关业务实现
 */
@Slf4j
@Service
public class WechatMpInvokeServiceImpl implements WechatMpInvokeService {

    @Autowired
    private WechatProperties wechatProperties;

    @Override
    public String validateSign(HttpServletRequest request) {
        // 微信加密签名
        String signature = HttpUtil.getRequestParameter(request, "signature");
        // 时间戳
        String timestamp = HttpUtil.getRequestParameter(request, "timestamp");
        // 随机数
        String nonce = HttpUtil.getRequestParameter(request, "nonce");
        // 随机字符串
        String echostr = HttpUtil.getRequestParameter(request, "echostr");
        if (log.isInfoEnabled()) {
            log.info("微信-开始校验签名,收到来自微信的echostr字符串:{}", echostr);
        }
        // 1.排序
        String token = wechatProperties.getMp().getServerVerifyToken();
        String[] strArray = { token, timestamp, nonce };
        Arrays.sort(strArray);
        // 2.sha1加密
        String mySignature = Hex.encodeHexString(DigestUtils.digest(DigestUtils.getSha1Digest(),
                (StringUtil.join(strArray, StringUtil.EMPTY)).getBytes(Charsets.UTF_8)));
        // 3.字符串校验
        if (StringUtil.equalsIgnoreCase(mySignature, signature)) {
            // 如果检验成功原样返回echostr,微信服务器接收到此输出,才会确认检验完成。
            if (log.isInfoEnabled()) {
                log.info("微信-签名校验通过,回复给微信的echostr字符串:{}", echostr);
            }
            return echostr;
        } else {
            log.error("微信-签名校验失败");
            return StringUtil.EMPTY;
        }
    }

    @Override
    public String porcessInvokeMessage(String content) {
        String responseContent = null;
        List<WechatMpInvokeMessageProcessor> processors = WechatMpInvokeMessageProcessorFactory.getProcessors(content);
        if (BeanUtil.isNotEmpty(processors)) {
            WechatInvokeMessage wechatInvokeMessage = WechatMpInvokeMessageProcessorFactory.getWechatInvokeMessage();
            for (WechatMpInvokeMessageProcessor processor : processors) {
                String result = processor.process(wechatInvokeMessage);
                System.out.println("--------------------------------------\n" + result);
                if (StringUtil.isNotEmpty(result)) {
                    responseContent = result;
                }
            }
        }
        WechatMpInvokeMessageProcessorFactory.removeWechatInvokeMessage();
        return responseContent;
    }
}

相关参考

  1. 微信公众号开发(三)自定义菜单
  2. 微信公众号开发:自定义菜单
更多内容请访问:IT源点

相关文章推荐
  • 该目录下还没有内容!

全部评论: 0

    我有话说: