微信公众号开发(一)——开发模式接入,消息的接收与响应

微信公众号

1

想自己开一个公众号,先学习一下用 Java 进行微信公众号的开发。

微信公众号的管理有开发模式和编辑模式两种,两者是互斥的。

腾讯是这么讲的:

编辑模式:编辑模式指所有的公众号运营者都可以通过简单的编辑,设置“按关键字回复”等功能。您可以设定常用的文字/语言/图片/录音作为回复消息,并制定自动回复的规则。当订阅用户的行为符合自动回复规则的时候,就会收到自动回复的消息。

开发模式:开发模式是为开发者提供与用户进行消息交互的能力。对于成功接入消息接口的公众账号,当用户发消息给公众号,微信公众平台服务器会使用http请求对接入的网址进行消息推送,第三方服务器可通过响应包回复特定结构,从而达到回复消息的目的。

所以接下来要讲的是如果使用微信的开发模式。

2 准备工作

微信的开发模式如果要调用他的接口需要微信认证300元。微信有公共平台测试号:

开发 — 开发者工具 — 公众号平台测试账号

地址映射工具

微信的 URL 要求必须是: http:// 80端口,且必须是能够在公网访问的,本地的不行。所以这里用到一个映射工具叫做 ngrok 。下载之后使用,cmd

ngrok http 8080

这里写图片描述

3 开发者模式接入

准备完毕开始开发,包结构如下:

包结构

开发者模式接入

通过 servlet 的 GET 请求进行校验

校验参数

微信需要对以上四个参数进行校验,将 timestamp,nonce,token 三者排序拼接成字符串,再进行 Sha1 加密之后,如果和 signature 相同则表示校验通过:

public static boolean checkSignature(String signature,String timestamp,String nonce){
        //排序
        String[] arr = new String[]{token,timestamp,nonce};
        Arrays.sort(arr);

        //生成字符串
        StringBuffer content = new StringBuffer();
        for(int i = 0; i < arr.length; i++){
            content.append(arr[i]);
        }

        //sha1 加密 java实现消息摘要加密
        String temp = getSha1(content.toString());

        //和微信传递过来的参数进行校验
        return temp.equals(signature);
    }

sha1加密

public static String getSha1(String str) {
        if (str == null || str.length() == 0) {
            return null;
        }
        char hexDigits[] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
                'a', 'b', 'c', 'd', 'e', 'f' };

        try {
            MessageDigest mdTemp = MessageDigest.getInstance("SHA1");
            mdTemp.update(str.getBytes("UTF-8"));

            byte[] md = mdTemp.digest();
            int j = md.length;
            char buf[] = new char[j * 2];
            int k = 0;
            for (int i = 0; i < j; i++) {
                byte byte0 = md[i];
                buf[k++] = hexDigits[byte0 >>> 4 & 0xf];
                buf[k++] = hexDigits[byte0 & 0xf];
            }
            return new String(buf);
        } catch (Exception e) {
            return null;
        }
    }

进行校验的servlet

protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //验证消息的确来自微信服务器 校验四个参数
        String signature = request.getParameter("signature"); //微信加密签名
        String timestamp = request.getParameter("timestamp"); //时间戳
        String nonce = request.getParameter("nonce"); //随机数
        String echostr = request.getParameter("echostr"); //随机字符串

        PrintWriter out = response.getWriter();
        if(CheckUtil.checkSignature(signature, timestamp, nonce)){
            out.print(echostr);
        }
    }

配置web.xml

<servlet>
    <servlet-name>weixinServlet</servlet-name>
    <servlet-class>com.shuiyujie.servlet.WeiXinServlet</servlet-class>
  </servlet>
  <servlet-mapping>
    <servlet-name>weixinServlet</servlet-name>
    <url-pattern>/wx.do</url-pattern>
  </servlet-mapping>

访问效果

访问是500,说明已经通了。接下来用 ngrok 映射工具生成映射地址,到公网上去访问看看行不行,这个在前面讲过不做演示。

在访问成功之后,对微信中的服务器配置进行填写提交。

配置成功

可以提交并且启用开发模式了,启用了开发模式则编辑模式下的配置全部失效。

4 消息的接收与响应

图片来自与网络
图片来自于网络

数据交互的过程如上图所示,通过 Post 请求进行。根据请求类型的不同,微信规定了不同的参数,并且通过 XML 的格式进行传递。通过查看开发文档,我们就可以了解所需要的参数。又由于是通过 XML 的格式传递的,为了便于后台程序的解析,所以将 XML 装换成集合类型。

4.1 文本消息的 PO 类

参数列表

通过微信文档可以知道,文本消息传递的参数列表如上图所示,所以要新建一个 Po 类。

public class TextMessage {
        private String ToUserName;
        private String FromUserName;
        private long CreateTime;
        private String MsgType;
        private String Content;
        private String MsgId;

添加 get, set方法

4.2 XML 和集合互相转换的工具类

XML 转集合

public static Map<String,String> xmlToMap(HttpServletRequest request) throws IOException, DocumentException{

        Map<String,String> map = new HashMap<String,String>();
        SAXReader reader = new SAXReader();

        InputStream ins = request.getInputStream();
        Document doc = reader.read(ins);

        //获取根节点
        Element root = doc.getRootElement();

        List<Element> list = root.elements();

        for(Element e : list){
            map.put(e.getName(), e.getText());
        }
        ins.close();

        return map;
    }

文本信息转 XML

public static String textMessageToXml(TextMessage textMessage){
        XStream xstream = new XStream();
        xstream.alias("xml", textMessage.getClass());
        return xstream.toXML(textMessage);
    }

servlet doPost 接收响应

protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        response.setCharacterEncoding("UTF-8");
        response.setCharacterEncoding("UTF-8");
        PrintWriter out = response.getWriter();
        try {
            Map<String,String> map = MessageUtil.xmlToMap(request);
            String fromUserName = map.get("FromUserName");
            String toUserName = map.get("ToUserName");
            String msgType = map.get("MsgType");
            String content = map.get("Content");

            String message = null;
            if("text".equals(msgType)){
                TextMessage text = new TextMessage();
                text.setFromUserName(toUserName); //原来的信息发送者,将变成信息接受者
                text.setToUserName(fromUserName); //原理的接受者,变成发送者
                text.setMsgType("text"); //表示消息的类型是text类型
                text.setCreateTime(new Date().getTime());
                text.setContent("您发送的信息是:" + content);
                message = MessageUtil.textMessageToXml(text); //装换成 xml 格式发送给微信解析

                System.out.println(message);
            }
            out.print(message);
        } catch (DocumentException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

    }

接下来需要启动 tomcat 运行程序。

效果图

这样消息回复的效果已经完成了,最后来让他能够接收更多类型的消息。比如常见的一些:关注后的消息回复,取消关注之后的消息回复,还有关键字的消息回复

5 更多接收消息

由于会接收多种消息类型,修改 MessageUtil 消息工具类,为了方便把常用的消息类型都定义成常量,并且将需要回复的消息,例如关注之后回复的消息,关键词回复的消息都定义好

定义常量

public class MessageUtil {  

    public static final String MESSAGE_TEXT = "text";
    public static final String MESSAGE_NEWS = "news";
    public static final String MESSAGE_IMAGE = "image";
    public static final String MESSAGE_VOICE = "voice";
    public static final String MESSAGE_MUSIC = "music";
    public static final String MESSAGE_VIDEO = "video";
    public static final String MESSAGE_LINK = "link";
    public static final String MESSAGE_LOCATION = "location";
    public static final String MESSAGE_EVNET = "event";
    public static final String MESSAGE_SUBSCRIBE = "subscribe";
    public static final String MESSAGE_UNSUBSCRIBE = "unsubscribe";
    public static final String MESSAGE_CLICK = "CLICK";
    public static final String MESSAGE_VIEW = "VIEW";
    public static final String MESSAGE_SCANCODE= "scancode_push";
/**
     * 拼接文本消息
     * @param toUserName
     * @param fromUserName
     * @param content
     * @return
     */
    public static String initText(String toUserName,String fromUserName,String content){
        TextMessage text = new TextMessage();
        text.setFromUserName(toUserName);
        text.setToUserName(fromUserName);
        text.setMsgType(MessageUtil.MESSAGE_TEXT);
        text.setCreateTime(new Date().getTime());
        text.setContent(content);
        return textMessageToXml(text);
    }

    /**
     * 关注回复
     * @return
     */
    public static String menuText(){
        StringBuffer sb = new StringBuffer();
        sb.append("欢迎关注公众号弄浪的鱼:\n\n");
        sb.append("1 回复关键字1\n");
        sb.append("2 回复关键字2\n");
        sb.append("3 回复关键字3\n\n");
        sb.append("本公众号还在开发中,没啥能看的,别玩坏了哈~~");
        return sb.toString();
    }

servlet对接收到的消息进行判断并作出响应

protected void doPost(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        response.setCharacterEncoding("UTF-8");
        response.setCharacterEncoding("UTF-8");
        PrintWriter out = response.getWriter();
        try {
            Map<String, String> map = MessageUtil.xmlToMap(request);
            String fromUserName = map.get("FromUserName");
            String toUserName = map.get("ToUserName");
            String msgType = map.get("MsgType");
            String content = map.get("Content");

            String message = null;

            if (MessageUtil.MESSAGE_TEXT.equals(msgType)) {
                if ("1".equals(content)) {
                    message = MessageUtil.initText(toUserName, fromUserName, MessageUtil.firstMenu());
                } else if ("2".equals(content)) {
                    message = MessageUtil.initText(toUserName, fromUserName, MessageUtil.secondMenu());
                } else if ("3".equals(content)) {
                    message = MessageUtil.initText(toUserName, fromUserName, MessageUtil.threeMenu());
                } else if ("?".equals(content) || "?".equals(content)) {
                    message = MessageUtil.menuText();
                }
            } else if (MessageUtil.MESSAGE_EVNET.equals(msgType)) {// 事件推送
                String eventType = map.get("Event"); // 事件分成多种,分别判断处理
                if (MessageUtil.MESSAGE_SUBSCRIBE.equals(eventType)) { // 这里先写一个关注之后的事件
                    message = MessageUtil.initText(toUserName, fromUserName, MessageUtil.menuText());
                }
            }

            System.out.println(message);

            out.print(message);
        } catch (DocumentException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

    }

成功

以上,关注回复和关键词回复成功。

最后再把微信程序部署到服务器上就可以正常访问了,下一篇就总结怎么部署到阿里云。

  • 0
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
接入第三方登录是让用户方便快捷地使用已有账号登录你的网站或应用程序,提高用户体验的一种方式。本文将介绍如何使用 PHP 实现微信公众号第三方登录。 1. 获取微信授权 首先,需要获取微信用户的授权。具体步骤如下: 1)引导用户打开微信授权页面: ```php $appid = 'your_appid'; $redirect_uri = urlencode('http://yourdomain.com/callback.php'); $scope = 'snsapi_userinfo'; $url = "https://open.weixin.qq.com/connect/oauth2/authorize?appid=$appid&redirect_uri=$redirect_uri&response_type=code&scope=$scope&state=STATE#wechat_redirect"; header("Location: $url"); ``` 其中,`$appid` 是你的微信公众号的 AppID,`$redirect_uri` 是授权后回调的 URL,`$scope` 是授权作用域,可以是 `snsapi_base` 或 `snsapi_userinfo`,`$state` 是自定义参数,用于防止 CSRF 攻击。 2)获取授权码: 用户同意授权后,会重定向到 `$redirect_uri` 指定的 URL,带上授权码 `code` 和 `state` 参数。 ```php $code = $_GET['code']; $state = $_GET['state']; ``` 3)获取 access_token 和 openid: 使用授权码 `code` 获取 `access_token` 和 `openid`。 ```php $access_token_url = "https://api.weixin.qq.com/sns/oauth2/access_token?appid=$appid&secret=$secret&code=$code&grant_type=authorization_code"; $response = file_get_contents($access_token_url); $result = json_decode($response, true); $access_token = $result['access_token']; $openid = $result['openid']; ``` 其中,`$secret` 是你的微信公众号的 AppSecret。 2. 获取用户信息 获取到 `access_token` 和 `openid` 后,可以使用以下代码获取用户信息: ```php $userinfo_url = "https://api.weixin.qq.com/sns/userinfo?access_token=$access_token&openid=$openid&lang=zh_CN"; $response = file_get_contents($userinfo_url); $userinfo = json_decode($response, true); ``` 其中,`$userinfo` 包含用户的昵称、头像等信息。 3. 将用户信息保存到数据库 最后,将获取到的用户信息保存到数据库中,以便下次使用时快速登录。 ```php // 连接数据库 $con = mysqli_connect('localhost', 'username', 'password', 'database'); mysqli_set_charset($con, "utf8"); // 查询用户是否已存在 $sql = "SELECT * FROM users WHERE openid='$openid'"; $result = mysqli_query($con, $sql); if (mysqli_num_rows($result) == 0) { // 用户不存在,插入新用户信息 $nickname = mysqli_real_escape_string($con, $userinfo['nickname']); $headimgurl = mysqli_real_escape_string($con, $userinfo['headimgurl']); $sql = "INSERT INTO users (openid, nickname, headimgurl) VALUES ('$openid', '$nickname', '$headimgurl')"; mysqli_query($con, $sql); } // 保存用户登录状态 $_SESSION['openid'] = $openid; ``` 以上就是使用 PHP 实现微信公众号第三方登录的步骤。需要注意的是,为了确保安全性,应该对用户输入的数据进行过滤和验证,防止 SQL 注入和 XSS 攻击等。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值