JavaWeb 微信公众号开发实践

重庆某工作室主要创始人之一。曾参与多个基于微信公众号的游戏、活动等项目开发,个人公众号:Newtol。

文章正文

公众号的简单介绍

微信公众号目前主要分为三类:服务号、订阅号和企业号(现企业微信)

服务号

为企业和组织提供更强大的业务服务与用户管理能力,主要偏向服务类交互(功能类似 12315、114、银行,提供绑定信息,服务交互的); 适用人群:媒体、企业、政府或其他组织。群发次数:服务号 1 个月(按自然月)内可发送4条群发消息。

订阅号

为媒体和个人提供一种新的信息传播方式,主要功能是在微信侧给用户传达资讯(功能类似报纸杂志,提供新闻信息或娱乐趣事) 适用人群:个人、媒体、企业、政府或其他组织。 群发次数:订阅号(认证用户、非认证用户)1 天内可群发 1 条消息。

企业号

主要用于公司内部通讯使用,需要先验证身份才可以关注成功企业号。

前期准备工作

测试号的注册

因为微信公众号的接口是具有固定的调用的权限的,所以我们需要开启微信公众账号的测试账号来开发测试。这一部分去微信公众平台注册即可。首先我们选择注册类型为订阅号 -> 完成基本信息的注册 -> 选择“开发者工具”->“公众平台测试号”-> 完成注册 enter image description here enter image description here

内网穿透

这一部分就是我们不需要将代码部署到服务器就可以直接与微信服务器进行直接对接进行本地开发测试的关键了。将大大减少我们开发的时间。这里我们将需要一个第三方的工具 Natapp。

  • 首先我们需要去 Natapp 官网进行注册,接下来就是一些常规的注册,我们就不在这里过多的赘述。

  • 接下来我们就来到首页,点击箭头所指的“购买隧道”。然后我们就能看到一些关于隧道的各种类型。由于这里我们只是做示例,就选择免费型,你可以根据自己的实际情况选择隧道类型

  • natapp首页

  • 隧道协议我们就选择 web,因为我用的开发环境使用的是 9090 端口,所以我们就让隧道映射到 9090 端口就ok了(根据自己实际使用的端口号填写)

  • 隧道购买

  • 接下来,我们就需要下载 Natapp 的客户端了,为了让 Natapp 能够在后台运行,我们还需要下载一个 nssm,下载后根据自身的配置选择版本,然后和刚刚下载好的 Natapp 放在同一个文件夹下。

  • 接下来 win+R 输入 cmd 并进入 nssm 所在的目录(注:win10 可直接按住 shift 点击右键,在此处打开命令窗口)

  • 运行:

nssm install natapp
  • 在弹出的安装“-autoken=...”处填写刚刚在购买隧道时候的 authtoken

natapp安装界面

authtoken所在位置

然后我们就安装完毕了。然后双击 natapp.exe。

enter image description here

我们就发现 http://weucuk.natappfree.cc 就指向了我本地的 9090 端口。

本地 Redis 的安装

这一部分不是必须的,你完全选择继续使用 Mysql 数据库,但是为了更好的体验,在这个地方我一直选用的是 Redis 来储存 Accesstoken (后面会有对 Accesstoken 的详细介绍)这一部分我就不再详细的介绍,小伙伴们可自行百度即可。Redis 在 Windows 环境成功运行如图:

enter image description here

接入微信服务器

  1. 填写服务器配置
  2. 验证服务器地址的有效性
  • 将 token、timestamp、nonce 三个参数进行字典序排序
  • 将三个参数字符串拼接成一个字符串进行 sha1 加密
  • 开发者获得加密后的字符串可与 signature(来自微信服务器)对比,标识该请求来源于微信

项目目录

这里写图片描述

核心代码

  • IndexServlet
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Arrays;

/**
 * @Author: REN
 * @Description:接收微信服务器传过来的参数
 * @Date: Created in 20:26 2018/4/24
 */
public class IndexServlet extends HttpServlet{
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //微信公众号管理界面配置参数
        String token = Const.Token;
        //获取请求的四个参数
        String signature = req.getParameter("signature");
        String timestamp = req.getParameter("timestamp");
        String nonce = req.getParameter("nonce");
        String echostr = req.getParameter("echostr");
        //检验四个参数是否有效
        if (!StringUtil.hasBlank(signature, timestamp, nonce, echostr)) {
            String[] list = {token, timestamp, nonce};
            //字典排序
            Arrays.sort(list);
            //拼接字符串
            StringBuilder builder = new StringBuilder();
            for (String str : list) {
                builder.append(str);
            }
            //sha1加密
            String hashcode = EncryptUtil.sha1(builder.toString());
            //不区分大小写差异情况下比较是否相同
            if (hashcode.equalsIgnoreCase(signature)) {
                //响应输出
                resp.getWriter().println(echostr);
            }
        }
    }
}
  • Const.java
/**
 * @Author: REN
 * @Description:基础的配置
 * @Date: Created in 10:33 2018/3/23
 */
public class Const {
    //微信配置
    public static final String AppId = "你自己的AppId";
    public static final String AppSecret = "你自己的AppSecret";
    public static final String Token = "你自己的Token";
}
  • StringUtil.java
public class StringUtil {

    /**
     * 检查字符串是否为空
     * @param str
     * @return
     */
    public static boolean isBlank(String str) {
        return str == null || str.equals("");
    }

    /**
     * 检查是否存在空字符串
     * @param strs
     * @return
     */
    public static boolean hasBlank(String ... strs) {
        if (strs == null || strs.length == 0) {
            return true;
        } else {
            for (String str : strs) {
                if (isBlank(str)) return true;
            }
            return false;
        }
    }
}
  • EncryptUtil.java
import java.io.UnsupportedEncodingException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;

/**
 * @Author: REN
 * @Description:参数的加密
 * @Date: Created in 21:57 2018/3/20
 */
public class EncryptUtil {
    private static final char[] digit = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};

    /**
     * 此类不需要实例化
     */
    private EncryptUtil() {
    }

    /**
     * 32位MD5加密,结果大写
     * @param str
     * @return
     */
    public static String MD5(String str) {
        return encode(str, "MD5");
    }

    /**
     * 32位SHA1加密,结果大写
     * @param str
     * @return
     */
    public static String sha1(String str) {
        return encode(str, "SHA-1");
    }

    private static String encode(String str, String algorithm) {
        String rs = null;
        try {
            MessageDigest md = MessageDigest.getInstance(algorithm);
            byte[] digest = md.digest(str.toString().getBytes("UTF-8"));
            rs = byteToStr(digest);
        } catch (NoSuchAlgorithmException e) {
            System.out.println("该加密方式不存在");
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
        return rs;
    }

    /**
     * 将byte数组变为16进制对应的字符串
     * @param byteArray byte数组
     * @return 转换结果
     */
    private static String byteToStr(byte[] byteArray) {
        int len = byteArray.length;
        StringBuilder strDigest = new StringBuilder(len * 2);
        for (byte aByteArray : byteArray) {
            strDigest.append(byteToHexStr(aByteArray));
        }
        return strDigest.toString();
    }

    private static String byteToHexStr(byte mByte) {
        char[] tempArr = new char[2];
        tempArr[0] = digit[(mByte >>> 4) & 0X0F];
        tempArr[1] = digit[mByte & 0X0F];
        return new String(tempArr);
    }
}
  • web.xml
<!DOCTYPE web-app PUBLIC
 "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
 "http://java.sun.com/dtd/web-app_2_3.dtd" >

<web-app>
  <display-name>Archetype Created Web Application</display-name>
  <servlet>
    <servlet-name>IndexServlet</servlet-name>
    <servlet-class>IndexServlet</servlet-class>
  </servlet>
  <servlet-mapping>
    <servlet-name>IndexServlet</servlet-name>
    <url-pattern>/weiXin</url-pattern>
  </servlet-mapping>
</web-app>

微信服务器的配置

  • 开启 natapp 获得内网穿透的 url
  • 微信公众平台填写相关配置(使用的测试号)

这里写图片描述

点击确定后,如果提示配置成功,那么就成功接入微信公众平台了。

Access_token 的获取

accesstoke

作者正在撰写中...
隐藏内容 支付可见
内容互动
写评论
加载更多
评论文章
¥1.99 购买
× 订阅 Java 精选频道
¥ 元/月
订阅即可免费阅读所有精选内容