暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

微信公众号开发:Spring Boot弹幕应用中接收消息

天码营 2016-06-30
784

点击上面“天码营”,加入我们,快速成长~

「内容简介」微信公众平台为我们提供了很强大的管理功能,包括群发消息、自动回复、自定义菜单等等。今天来学习一下如何利用微信的开放接口来实现一个微信弹幕的简单后台。

微信公众号大家都很熟悉了,当你拥有了微信公众号之后,你可以在微信公众平台中对你的公众号进行管理。微信公众平台为我们提供了很强大的管理功能,包括群发消息、自动回复、自定义菜单等等。但是,需求是多种多样的,微信公众平台中的功能必然不能满足所有的用户需求。今天,我们就来学习一下如何利用微信的开放接口来实现一个微信弹幕的简单后台。

申请微信公众号

第一步,我们首先得拥有一个微信公众号,进入微信公众平台,点击右上角【立即注册】进入注册页面:

按要求填写基本信息,激活邮箱、验证个人信息等等操作后,注册成功。此时,作为一个最简单的公众号,微信已经给我们了很多相应的权限,在【接口权限】页面中我们可以看到我们所拥有的所有权限:

浏览一下权限以及状态,我们可以看到,我们已经拥有了接收消息的相应的权限。现在,我们就来通过该接口在我们的程序中接收普通的消息。

服务器配置

接收微信公众号消息的流程是这样的:用户向公众号发送消息,微信接收消息,并向你的开发服务器发送一个POST请求,该请求包含了该消息的相关信息,如由谁发给谁、内容是什么等,最后开发服务器接收消息并进行相应的处理。

我们可以看到,微信会主动的向你的开发服务器推送相应的消息,很明显,我们需要在微信公众平台中进行相应的设置:

点击修改配置,此时需要填写以下四个信息:

  • URL 微信将发送数据到该地址,该URL需要处理GET请求以及POST请求。GET请求用于微信验证该URL是否属于你,当你提交你的服务器配置时,微信会往该地址发送一个GET请求,只有当该请求返回结果正确时,你的修改才会成功,否则提示【token验证失败】。POST请求用于接收微信的相关信息,当有人往公众号发送信息时,微信将把相关信息通过POST请求发送到该地址

  • Token 用于加密的令牌,GET请求时验证微信身份时会用到,可以随意填写

  • EncodingAESKey 用于对信息加密,当使用安全模式时,微信发送过来的信息是用该字段进行加密后的字符串,你可以使用微信提供的算法对字符串进行解密,获取信息内容,直接随机生成即可

  • 消息加密方式

    • 明文模式 发送过来的消息不进行加密,直接使用即可

    • 兼容模式 明文、密文共存,方便调试

    • 安全模式 发送过来的消息只有密文,安全系数较高

这里,我们根据实际情况将URL、Token、EncodingAESKey填好,直接选择明文模式接收公众号的消息。

在介绍URL参数时我们也提到了,此时点击提交,由于我们未完成URL GET请求的处理,将提示【token验证失败】,因此,我们先把服务器配置的提交放一放,先来完成服务端的开发。

验证服务器地址

当我们提交服务器配置时,微信会向我们配置的URL发送一个验证请求,该请求包含下列四个参数:

  • signature 微信加密签名,signature结合了开发者填写的token参数和请求中的timestamp参数、nonce参数。

  • timestamp 时间戳

  • nonce 随机数

  • echostr 随机字符串

我们接收到请求后,验证过程如下:

  1. 将token、timestamp、nonce三个参数进行字典序排序

  2. 将三个参数字符串拼接成一个字符串进行sha1加密

  3. 开发者获得加密后的字符串可与signature对比,标识该请求来源于微信

  4. 若该请求来源于微信,原样返回echostr,则接入成功,否则接入失败

Java代码如下:

    @Value("${weixin.token}")
    private String token;
    @RequestMapping(value = "/XXX", method = RequestMethod.GET)
    public String validate(@RequestParam String signature,
                           @RequestParam String timestamp,
                           @RequestParam String nonce,
                           @RequestParam String echostr) {
        if(!accessing(signature, timestamp, nonce, echostr)){
            return "";
        }
        return  this.echostr;
    }
    private boolean accessing(String signature, String timestamp, String nonce, String echostr) {
        String[] ArrTmp = { token, timestamp, nonce };
        Arrays.sort(ArrTmp);
        StringBuilder sb = new StringBuilder();
        for (String aArrTmp : ArrTmp) {
            sb.append(aArrTmp);
        }
        try {
            String pwd = Encrypt(sb.toString());
            if(StringUtils.trim(pwd).equals(StringUtils.trim(signature))){
                this.echostr =echostr;
                return true;
            }else{
                return false;
            }
        } catch (NoSuchAlgorithmException e) {
            return false;
        }
    }
    private String Encrypt(String strSrc) throws NoSuchAlgorithmException {
        MessageDigest md;
        String strDes;
        byte[] bt = strSrc.getBytes();
        md = MessageDigest.getInstance("SHA-1");
        md.update(bt);
        strDes = bytes2Hex(md.digest());
        return strDes;
    }
    private String bytes2Hex(byte[] bts) {
        String des = "";
        String tmp;
        for (byte bt : bts) {
            tmp = (Integer.toHexString(bt & 0xFF));
            if (tmp.length() == 1) {
                des += "0";
            }
            des += tmp;
        }
        return des;
    }

将该代码部署到服务器上后,提交服务器配置,我们可以看到配置提交成功。接下来,当有用户向公众号发送信息时,我们就能接收到相应的请求了。

接收用户消息

用户消息有很多种,为了方便处理,这里我们只处理普通的用户消息,如果大家对其它类型的消息(图片、语音、视频、小视频、地址位置、链接)有兴趣,可以自行查看微信开发文档。

当用户发送普通消息时,我们接收到的消息格式如下:

<xml>
 <ToUserName><![CDATA[toUser]]></ToUserName>
 <FromUserName><![CDATA[fromUser]]></FromUserName> 
 <CreateTime>1348831860</CreateTime>
 <MsgType><![CDATA[text]]></MsgType>
 <Content><![CDATA[this is a test]]></Content>
 <MsgId>1234567890123456</MsgId>
</xml>

我们的目的是实现一个弹幕游戏的后台。这里,接收到消息后,我们向Redis的某个频道广播一条消息,前端会监听Redis的该频道,发现有消息推送过来后,通过websocket向浏览器推送消息。具体代码如下:

    private static final String TEXT_MSG_TYPE = "text";
    private static final String REDIS_BULLET_CHANNEL = "bullet_message";
    @RequestMapping(value = "", method = RequestMethod.POST)
    public void message(@RequestBody String requestStr) throws IOException {
        try {
            Document document = DocumentHelper.parseText(requestStr);
            String msgType = document.getRootElement().elementText("MsgType");
            if(StringUtils.isBlank(msgType)) {
                return;
            }
            if(TEXT_MSG_TYPE.equals(msgType)) {
                logger.debug("send content: {}", document.getRootElement().elementText("Content"));
                template.convertAndSend(REDIS_BULLET_CHANNEL,
                        new Gson().toJson(ImmutableMap.of("Content", document.getRootElement().elementText("Content"))));
            }
        }   catch (Exception e) {
            e.printStackTrace();
        }
    }


了解更多Java Web开发内容:


三个月时间如何成为Java Web全栈工程师?

点击下方“阅读原文”,可以获得更多天码营教程。

文章转载自天码营,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论