前言

因公司需要开发一款手机打卡程序,本人没有安卓APP开发经验,所以决定将写个服务号的公众号,集外出打卡,打卡查询等功能;

一,开发前测试帐号申请

以下是官方给出的建议,大家可以多参考参考

1)如果想简单的发送消息,达到宣传效果,建议可选择订阅号;
2)如果想用公众号获得更多的功能,例如开通微信支付,建议可以选择服务号;
3)如果想用来管理内部企业员工、团队,对内使用,可申请企业号;
4)订阅号可通过微信认证资质审核通过后有一次升级为服务号的入口,升级成功后类型不可再变;
5)服务号不可变更成订阅号。

申请地址:

http://mp.weixin.qq.com/debug/cgi-bin/sandbox?t=sandbox/login

二,选择natapp进行内网外穿,为什么?这是因为自制程序可以连外网,但怎样让微信服务器与你的自制程序通信?那只能把自己的程序也设置为外网,但用传统的打包布署发布到LINUX?那将会对开发极为不便。natapp就是解决我们可以直接将tomcat设置为内网外穿,这样每次开发完,直接运行,外网就可以找到你这个内网外穿的外网。我个人喜好natapp,当然也有其它优秀的内网外穿工具,可自行选择。

(1). 下载
在以下地址:https://natapp.cn/,进行下载,打开链接之后,选择客户端下,如下图:

图1:客户端下载
我在这里下载的是windows64位。
(2). 注册并登录
同时还需要进行注册,(注册过程不详述)并进行登陆。登陆后的页面如下图:

图2:登录成功后的页面
(3). 购买隧道
此时我们需要进行购买隧道,如下图。

图3:购买隧道
如果是自己的项目可以使用免费隧道。不过我推荐大家使用VIP_2型,根据自己的需要进行选择。我使用的是付费隧道。(如下图),我购买是一起花了13元,建议购买前去网上找优惠码,可便宜2元。然后建议不要买太便宜的,哈哈。

图4:隧道购买成功
(4). 配置已购买隧道
下面是购买成功后,我的隧道,我之前申请了一个免费隧道,一个付费隧道,所以我的里面有两个隧道。如下图

选择第二个隧道中的配置按钮,进行隧道配置。打开如图所示:

图5:配置已购买隧道
此时为止,natapp的配置已完成。

(5). 启动natapp
新建config.ini文件,存储在下载的客户端同一个目录下。该文件中需要添加内容如下

[default]
authtoken= 6fdb788dce71d7e2
clienttoken= #对应客户端的clienttoken,将会忽略authtoken,若无请留空,
log=none #log 日志文件,可指定本地文件, none=不做记录,stdout=直接屏幕输出 ,默认为none
loglevel=ERROR #日志等级 DEBUG, INFO, WARNING, ERROR 默认为 DEBUG
http_proxy= #代理设置 如 http://10.123.10.10:3128 非代理上网用户请务必留空

双击natapp程序启动成功如下图所示:


至此,大功告成.

三.在微信平台进行配置.

完成上面第一步的注册后,即下面这个网址.

http://mp.weixin.qq.com/debug/cgi-bin/sandbox?t=sandbox/login

说明一下.

appid:是公众号开发识别码,配合开发者密码可调用公众号的接口能力。
 appsecret:是校验公众号开发者身份的密码,具有极高的安全性。

再往下看,我们会看到URL和Token这两个属性,和上面appid/appsecret不同的是,上面的是微信分配给我们的,但是下面这两个是需要我们填进去的。

我们先来了解一下,这两个属性有什么作用。

URL:就是指我们自己的服务器地址
该URL是开发者用来接收和响应微信消息和事件的接口URL
(必须以http://或https://开头,分别支持80端口和443端口,这就是为什么我刚刚第二步说要进行内网外穿,哈哈)

Token:可任意填写,用作生成签名(必须为英文或数字,长度为3-32字符)
该签名在后边会用到,这里暂时随便填个内容也可以

接下来我们需要了解的是微信与我们的服务器交互的过程:

当我们在微信app上,给公众号发送一条内容的时候,实际会发送到微信的服务器上,此时微信的服务器就会对内容进行封装成某种格式的数据比如xml格式,再转发到我们配置好的URL上,所以该URL实际就是我们处理数据的一个请求路径。所以该URL必须是能暴露给外界访问的一个公网地址,不能使用内网地址,生产环境可以申请腾讯云,阿里云服务器等,但是在开发环境中可以暂时利用一些软件来完成内网穿透,便于修改和测试,如NATAPP,花生壳等软件,使用起来也很方便,在本地安装对应的软件,配置运行后,直接使用软件分配的临时域名来访问本地应用即可,只是偶尔会存在网络不稳定的情况。这里不详细介绍如何使用了,具体教程可参考软件官网。

在开发的过程中,我们会经常使用到微信公众号提供给开发者的开发文档
具体地址:https://mp.weixin.qq.com/wiki
大家打开后可以选择”接入指南”,参考微信提供的一些帮助信息。

四.正式开发

(1),URL接入

我们需要先来了解一下接入的过程是怎么样的。下图是微信官方对接入过程的介绍。

由以上介绍可知,当我们填入url与token的值,并提交后,微信会发送一个get请求到我们填写的url上,并且携带4个参数,而signature参数结合了开发者填写的token参数和请求中的timestamp参数、nonce参数来做的加密签名,我们在后台需要对该签名进行校验,看是否合法。实际上,我们发现微信带过来的4个参数中并没有带token参数,仅有signature是和token有关的,所以我们应该在本地应用中也准备一个和填入的token相同的参数,再通过微信传入的timestamp与nonce做相同算法的加密操作,若结果与微信传入的signature相同,即为合法,则原样返回echostr参数,代表接入成功,否则不做处理,则接入失败。

我这里是使用的springboot+springmvc+mybaties

1.首先新建一个servlet,为什么是servlet而不是controller?因为servlet支持一个入口对应两个方法,即get post.开发到后面你就会明白这个意义.

package com.cosun.cosunp.weixin;import com.cosun.cosunp.service.IPersonServ;
import com.cosun.cosunp.tool.Constants;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.context.support.SpringBeanAutowiringSupport;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Date;
import java.util.List;
import java.util.Map;/*** @author:homey Wong* @Date: 2019/9/11 上午 9:03* @Description:* @Modified By:* @Modified-date:*/
@WebServlet(urlPatterns = "/weixin/hello")
public class WeiXinServlet extends HttpServlet {private static Logger logger = LogManager.getLogger(WeiXinServlet.class);@AutowiredIPersonServ personServ;public static final String tooken = "homeyhomeyhomey";private static JedisPool pool;private static Jedis jedis;public WeiXinServlet() {//空构造函数}@Overrideprotected 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 outPW = null;try {outPW = response.getWriter();if (CheckUtil.checkSignature(signature, timestamp, nonce, tooken)) {System.out.println("签名成功!");outPW.write(echostr);} else {System.out.println("签名失败");}} catch (IOException e) {e.printStackTrace();} finally {outPW.close();}}
}

工具类CheckUtil

package com.cosun.cosunp.weixin;import java.security.MessageDigest;
import java.util.Arrays;/*** @author:homey Wong* @Date: 2019/9/11 0011 上午 9:10* @Description:* @Modified By:* @Modified-date:*/
public class CheckUtil {public static boolean checkSignature(String singnature, String timestamp, String nonce, String tooken) {String[] arr = {tooken, timestamp, nonce};Arrays.sort(arr);StringBuilder sb = new StringBuilder();for (String s : arr) {sb.append(s);}String temp = getSha1(sb.toString());return temp.equals(singnature);}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;}}}

可以拿你的手机测试一下.OK后,如下,可以互动了

@WebServlet(urlPatterns = "/weixin/hello")
public class WeiXinServlet extends HttpServlet {private static Logger logger = LogManager.getLogger(WeiXinServlet.class);@AutowiredIPersonServ personServ;public static final String tooken = "homeyhomeyhomey";private static JedisPool pool;private static Jedis jedis;@Overrideprotected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {response.setCharacterEncoding("UTF-8");response.setCharacterEncoding("UTF-8");pool = new JedisPool(new JedisPoolConfig(), "127.0.0.1");jedis = pool.getResource();System.out.println(jedis.get(Constants.accessToken));System.out.println(jedis.get(Constants.jsapi_ticket));try {// https://open.weixin.qq.com/connect/oauth2/authorize?appid=wxd5109277d8902606&redirect_uri=http://homey.nat100.top/weixin/getMobileLocate&response_type=code&scope=snsapi_userinfo&state=1#wechat_redirectPrintWriter out = response.getWriter();Map<String, String> map = WeiXinUtil.xmlToMap(request);String fromUserName = map.get("FromUserName");String toUserName = map.get("ToUserName");String msgType = map.get("MsgType");String content = map.get("Content");String key = map.get("EventKey");String message = null;StringBuilder returnMes = new StringBuilder();if ("event".equals(msgType)) {if (clickquery.equals(key)) {List<OutClockIn> allOutClockIn = personServ.findAllOutClockInByOpenId(fromUserName);if (allOutClockIn.size() > 0) {for (OutClockIn on : allOutClockIn) {returnMes.append(on.getClockInDateStr() + ":");returnMes.append(on.getClockInDateAMOnStr() + ",");returnMes.append(on.getClockInAddrAMOn() + ",");if (on.getAmOnUrl() != null && on.getAmOnUrl().trim().length() > 0) {returnMes.append("上午已摄像.");} else {returnMes.append("上午还未摄像.");}returnMes.append(on.getClockInDatePMOnStr() + ",");returnMes.append(on.getClockInAddrPMOn() + ".");if (on.getPmOnUrl() != null && on.getPmOnUrl().trim().length() > 0) {returnMes.append("下午已摄像.");} else {returnMes.append("下午还未摄像.");}returnMes.append(on.getClockInDateNMOnStr() + ",");returnMes.append(on.getClockInAddNMOn() + ".");if (on.getNmOnUrl() != null && on.getNmOnUrl().trim().length() > 0) {returnMes.append("晚上已摄像.");} else {returnMes.append("晚上还未摄像.");}}} else {returnMes.append("暂无考勤信息");}InMsgEntity text = new InMsgEntity();text.setFromUserName(toUserName); //原来的信息发送者,将变成信息接受者text.setToUserName(fromUserName); //原理的接受者,变成发送者text.setMsgType("text"); //表示消息的类型是text类型text.setCreateTime(new Date().getTime());text.setContent("您的考勤信息是:" + returnMes);message = WeiXinUtil.textMessageToXml(text); //装换成 xml 格式发送给微信解析}} else if ("text".equals(msgType)) {InMsgEntity text = new InMsgEntity();text.setFromUserName(toUserName); //原来的信息发送者,将变成信息接受者text.setToUserName(fromUserName); //原理的接受者,变成发送者text.setMsgType("text"); //表示消息的类型是text类型text.setCreateTime(new Date().getTime());text.setContent("您发送的信息是:" + content);message = WeiXinUtil.textMessageToXml(text); //装换成 xml 格式发送给微信解析}out.print(message);} catch (Exception e) {e.printStackTrace();}}
}
 public void setRedisValue(AccessToken accessToken) {// 初始化Redis连接池pool = new JedisPool(new JedisPoolConfig(), "127.0.0.1");jedis = pool.getResource();jedis.set(Constants.accessToken, accessToken.getAccessToken());jedis.set(Constants.expiresin, accessToken.getExpiresin() + "");//jedis.set(Constants.jsapi_ticket, accessToken.getJsapi_ticket());}

至此微信公众号的接入就已经完成了,后继可以进行功能开发啦.

微信公众号开发--服务号相关推荐

  1. 微信公众平台的服务号和订阅号

    微信公众平台 服务号 订阅号  作者:方倍工作室  地址:http://www.cnblogs.com/txw1958/p/ServiceNumber-subscriptionNumber.html ...

  2. c#实现微信公众号开发--服务号通过oauth2获取用户信息

    2018年春节策划了一个"带现金红包的贺年卡"微信号推广活动,先说下效果:2个小时实现新增关注用户4万多户,活动页面PV达到16.8万,后因红包预算费用原因结束活动. 实现原理:每 ...

  3. 微信公众平台开发订阅号

    忙活着搭建了ecplise和tomcat,申请了sae服务器,最后回过头来才发现微信订阅号的开发,如果仅仅只是每天发布一些文章,根本不需要去开通开发者帐号,只需要在注册成功后,就可以去群发里面编辑文章 ...

  4. 用Sunny_ngrok免费地址映射工具解决微信公众平台开发本地测试问题

    问题: 1.微信公众平台开发如何进行本地测试? 2.微信公众平台目前只支持80和433端口如何解决? 3.如何解决外网访问内网? 案例场景: 在微信公众平台开发服务号,开发新功能,想本地起服务进行测试 ...

  5. 基于java的微信公众平台开发(一)--账号申请与服务器的搭建

    2019独角兽企业重金招聘Python工程师标准>>> 微信公众号开发文档:https://mp.weixin.qq.com/wiki 微信公众平台接口调试工具:https://mp ...

  6. 微信公众平台开发中提示“该公众号提供的服务出现故障”问题解决

    问题描述: 在处理室内数据推送的时候,服务器能正常接收并处理请求,,客户端也能正常接收消息 但是在每次收到消息后,微信界面会出现,"该公众号提供的服务出现故障"字样. 解决思路一: ...

  7. 用c#开发微信(1)服务号的服务器配置和企业号的回调模式 - url接入

    2019独角兽企业重金招聘Python工程师标准>>> 阅读目录 一.用法 二.实现方法 最近研究了下服务号的服务器配置和企业号的回调模式.真正实现完后,觉得很简单,但一开始还是走了 ...

  8. 升讯威微信营销系统开发教程:(1)订阅号和服务号深入分析

    微信开发系列教程,将以一个实际的微信平台项目为案例,深入浅出的讲解微信开发.应用各环节的实现方案和技术细节. 原创内容,欢迎转载,转载请注明出处. 首先在第1章节中,我们先理清什么是订阅号,什么又是服 ...

  9. 微信公众平台订阅号、服务号和企业号三者之间的区别与联系

    现在很多人用微信营销,但是网上经常能看到有人问订阅号.服务号和企业号到底该选择哪个,下面我们会详细的讲解订阅号.服务号和企业号的区别与联系,需要的朋友可以参考下. 9月18日,微信正式开启了微信企业号 ...

最新文章

  1. select刷新后保存原先选择的信息
  2. boost::asio ssl
  3. 多个容器一起打包_程序员修神之路容器技术为什么会这么流行(记得去抽奖)
  4. 使用vagrant因用户权限导致文件不可写问题的解决
  5. Serv-U FTP Jail Break(越权遍历目录、下载任意文件)
  6. C++进阶教程之信号处理
  7. Linux系统编程 -- 信号及signal函数
  8. Nginx重启时丢失nginx.pid文件
  9. MAC编译OpenJDK8:error: invalid argument ‘-std=gnu++98‘ not allowed with ‘C‘
  10. matlab simulink 过程控制,[转载]MATLAB/Simulink与过程控制系统
  11. 使用tkinter+爬虫实现网易云音乐下载器
  12. 2017年节假日放假安排来了!
  13. 银河麒麟v10_备受瞩目的银河麒麟v10系统究竟好不好?开箱测评瞬间明了
  14. STM32F103_study66_The punctual atoms(STM32 Temperature sensor experiment)
  15. 系列笔记-USYD悉尼大学Data1001 RQuiz1——RQuiz6 做法讲解
  16. (iphone铃声制作)i39 for mac破解版永久激活方法
  17. VMware收购Wavefront增强云管理产品组合
  18. Connext DDSQoS参考
  19. Python的字符串方法join(插入间隔符)
  20. 磁盘在计算机没显示win10,win10机械硬盘检测不到怎么解决?

热门文章

  1. 2005年4月24日
  2. 毕业设计答辩ppt技巧
  3. 国产单片机MCU DSH550 ,可应用于中央空调温控器上
  4. Mac OS X上的不同字体位置及功能
  5. Unifier培训: 系列讲解26 : 项目级的业务流程--变更单(总承包商业务)
  6. 谷歌量子计算机和九章知乎,量子计算机《九章》问世 知乎微博消息: 北京时间 12 月 4 日凌晨 3 点,一篇重要文章以 First Releas... - 雪球...
  7. 基于jsp的中学班级信息管理系统
  8. 开题报告:基于java在线外卖点餐送餐系统 毕业设计论文开题报告模板
  9. 数据说Digg的兴衰史 从辉煌到没落
  10. java快照_网页快照 java 实现 | 学步园