微信公众号的端口映射及服务开发

article/2025/10/1 18:44:36

一. 开发准备

微信公众号申请---->实名认证---->服务器开发---->绑定服务器

PS: 这里有一点需要注意的就是, 微信开发必须是80端口或者443端口, 如果我们有云服务器主机一切都好办. 但是如果没有我们还有几个备选方案:

1. 花生壳 , net123 : 这两个都需要实名认证(上传省份证的那种), 可以花6块钱买一个永久

2. ngrok : 这个貌似不能用了, 之前用的是这个, 免费的

3. Xtunnel : 刚发现的端口映射, 免费, 操作简单

这篇文章我就使用Xtunnel来进行端口映射

再加一个: holer ,  隧道魔法

 

二. 用Xtunnel进行端口映射

1.编辑映射

注意这里的映射类型选择网站映射, 内网端口设为80端口

确定之后就会有一个外网地址, 如: abc.d.ef.org, 这就是我们后面填写url所需要的地址了.

PS: 可能不太稳定, 今天挂了, 建议多准备几个端口映射工具或者买一个花生壳服务.

2.本地tomcat配置

我们需要把本地的tomcat服务器也配置为80端口. 把server.xml里的端口改为80, 如果不会请自行百度.

我们最好把服务器备份一个, 把用的服务器放在c盘目录下, 把web工程的war包放在webapps目录下, 启动服务器时会自动解压部署的.

具体的web开发我们下面慢慢说.

三. 微信服务器开发

step1 : token验证原理

先说一下token验证流程原理, 自己的资源服务器和微信服务器进行绑定, 提交的url为token验证的接口(servlet), 微信服务器会把echostr发到自己资源的服务器, 然后我们的服务器进行token验证, 看看是否匹配. 匹配之后再将echostr返回给微信服务器, 这样服务器就绑定成功. 同时, 我们自己的资源服务器和微信服务器的消息收发都是通过验证token的那个接口(servlet)来实现的.

绑定流程图:

token验证的具体实现:

import java.io.IOException;
import java.io.PrintWriter;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 org.apache.log4j.Logger;/*** 网关控制层* @author later**/
@WebServlet("/TokenServlet")       //这就是待会我们访问的接口
public class TokenServlet extends HttpServlet {	private static final long serialVersionUID = 1L;private static Logger log =  Logger.getLogger("TokenServlet");	/*** 验证信息是否来自微信*/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();//通过检验signature对请求进行校验,若校验成功则原样返回echostr,表示接入成功,否则接入失败if (SignUtil.checkSignature(signature, timestamp, nonce)) {out.print(echostr);        //向微信返回echostr}else{log.error("未能通过token验证_TokenServlet");System.out.println("未能通过token验证_TokenServlet");}out.close();       //关闭输出流通道out =  null;}/*** 处理微信服务器发来的消息*/protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {request.setCharacterEncoding("UTF-8");  response.setCharacterEncoding("UTF-8"); // 调用核心业务类接收消息、处理消息  String respMessage = GatewayService.processRequest(request);// 响应消息  PrintWriter out = response.getWriter();  out.print(respMessage);  out.close();       }}
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Arrays;public class SignUtil {private static String token = "token";     //微信绑定页面填入的token/** * 验证签名 *  * @param signature * @param timestamp * @param nonce * @return */  public static boolean checkSignature(String signature, String timestamp, String nonce) {  String[] arr = new String[] { token, timestamp, nonce };  // 将token、timestamp、nonce三个参数进行字典序排序  Arrays.sort(arr);  StringBuilder content = new StringBuilder();  for (int i = 0; i < arr.length; i++) {  content.append(arr[i]);  }  MessageDigest md = null;  String tmpStr = null;  try {  md = MessageDigest.getInstance("SHA-1");  // 将三个参数字符串拼接成一个字符串进行sha1加密  byte[] digest = md.digest(content.toString().getBytes());  tmpStr = byteToStr(digest);  } catch (NoSuchAlgorithmException e) {  e.printStackTrace();  }  content = null;  // 将sha1加密后的字符串可与signature对比,标识该请求来源于微信  return tmpStr != null ? tmpStr.equals(signature.toUpperCase()) : false;  } /** * 将字节数组转换为十六进制字符串 *  * @param byteArray * @return */  private static String byteToStr(byte[] byteArray) {  String strDigest = "";  for (int i = 0; i < byteArray.length; i++) {  strDigest += byteToHexStr(byteArray[i]);  }  return strDigest;  }/** * 将字节转换为十六进制字符串 *  * @param mByte * @return */  private static String byteToHexStr(byte mByte) {  char[] Digit = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };  char[] tempArr = new char[2];  tempArr[0] = Digit[(mByte >>> 4) & 0X0F];  tempArr[1] = Digit[mByte & 0X0F];  String s = new String(tempArr);  return s;  }  }

step2 : 服务器结构

这里的结构和我的前一篇文章类似 : java web接口开发笔记

1. main包:

   main方法1: 获取token管理

             AccessTokenManager---->TokenThread---->WeixinUtil>>getAccessToken()

            功能: 每3600s获取微信access_token并保存至本地mysql数据库

   main方法2: 菜单管理

public static String menu_create_url = "https://api.weixin.qq.com/cgi-bin/menu/create?access_token=ACCESS_TOKEN";// 菜单创建(POST) 限100(次/天)   

微信创建菜单接口:

  • 用获取getAccessToken()方法获取access_token, 保存至数据库, 在从数据库查询access_token写入这个url去创建菜单.
  • getMenu() 作为createMenu(getMenu(), at.getToken()); 这个方法的实参具体构建菜单, getMenu()是我们提前写好的菜单.

2. service包(业务层):

这层和下面一层涉及到微信服务器发送给我们的消息, 可供我们直观的看见, 所以我们也应该好好理解一下.

dao      : 与数据库的操作,增删改查等方法
model  : 一般都是javabean对象,例如与数据库的某个表相关联。
service :  供外部调用,等于对dao,model等进行了包装。
impl     : 定义的接口
util       : 通常都是工具类,如字符串处理、日期处理等

AccessTokenService.java : 包含两种方法: 获取token, 保存token

TokenService.java            : 这是网关业务层 : 用于处理微信发过来的请求

        先解析微信发过来的请求request, 解析成我们需要的数据流或者文件流. 这里使用了MessageUtil类, 处理微信发到服务器的消息.  并且新建一个文本消息, 把我们要发给微信的消息转换成xml, 通过servlet的doPost方法发送给微信.

3. utils包:

这里是一些常用的网络方法, 和数据库连接操作类

额(⊙o⊙)…思路乱了, 感觉自己解释不清楚, 继续尝试解释吧. 有错误欢迎交流.

 

step3 : 本地mysql

给大家看一下我的本地mysql的设计, 这里就很简单不必多说了.

DBHelper.java

其实这个数据库操作类可灵活了, 连接好本地mysql数据库, 写入基本的操作方法, 在写入自己需要的方法, 后面直接拿过来调用方便的很. 大家自行添加就OK啦.

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;/*** JDBC封装* @author later**/
//本地mysql数据库: restful_api     表名: access_token
public class DBHelper {private static final String DRIVENAME = "com.mysql.jdbc.Driver";private static final String URL = "jdbc:mysql://127.0.0.1:3306/restful_api";private static final String USER = "root";private static final String PASSWORD = "root";private Connection conn = null;private Statement st = null;private PreparedStatement ppst = null;private ResultSet rs = null;/*** 加载驱动*/static{try {Class.forName(DRIVENAME).newInstance();} catch (Exception e) {System.out.println("驱动加载失败:"+e.getMessage());}}/*** 连接数据库* @return*/public Connection getConn(){try {conn =  DriverManager.getConnection(URL,USER,PASSWORD);} catch (SQLException e) {System.out.println("数据库连接失败:"+e.getMessage());}return conn;}/*** 获取结果集(无参)* @param sql* @return*/private ResultSet getRs(String sql){conn = this.getConn();try {		st = conn.createStatement();rs = st.executeQuery(sql);} catch (SQLException e) {System.out.println("查询(无参)出错:"+e.getMessage());}return rs;}/*** 获取结果集* @param sql* @param params* @return*/private ResultSet getRs(String sql,Object[] params){conn = this.getConn();try {			ppst = conn.prepareStatement(sql);if(params!=null){for(int i = 0;i<params.length;i++){ppst.setObject(i+1, params[i]);}}			rs = ppst.executeQuery();} catch (SQLException e) {System.out.println("查询出错:"+e.getMessage());}return rs;}/*** 查询* @param sql* @param params* @return*/public List<Object> query(String sql,Object[] params){List<Object> list = new ArrayList<Object>();ResultSet rs = null;if(params!=null){rs = getRs(sql, params);}else{rs = getRs(sql);}ResultSetMetaData rsmd = null;int columnCount = 0; try {			rsmd = rs.getMetaData();  columnCount = rsmd.getColumnCount();			while(rs.next()){Map<String, Object> map = new HashMap<String, Object>();for(int i = 1;i<=columnCount;i++){map.put(rsmd.getColumnLabel(i), rs.getObject(i));  }list.add(map);}} catch (SQLException e) {System.out.println("结果集解析出错:"+e.getMessage());} finally {closeConn();}return list;}/*** 更新(无参)* @param sql*/public int update(String sql){		int affectedLine = 0;//受影响的行数conn = this.getConn();try {		st = conn.createStatement();affectedLine = st.executeUpdate(sql);} catch (SQLException e) {System.out.println("更新(无参)失败:"+e.getMessage());} finally {closeConn();}return affectedLine;}/*** 更新* @param sql* @param params* @return*/public int update(String sql,Object[] params){int affectedLine = 0;//受影响的行数conn = this.getConn();try {ppst = conn.prepareStatement(sql);if(params!=null){for(int i = 0;i<params.length;i++){ppst.setObject(i+1, params[i]);}}affectedLine = ppst.executeUpdate();} catch (SQLException e) {System.out.println("更新失败:"+e.getMessage());} finally {closeConn();}return affectedLine;}private void closeConn(){if(rs!=null){try {rs.close();} catch (SQLException e) {System.out.println(e.getMessage());}}if(st!=null){try {st.close();} catch (SQLException e) {System.out.println(e.getMessage());}}if(ppst!=null){try {ppst.close();} catch (SQLException e) {System.out.println(e.getMessage());}}if(conn!=null){try {conn.close();} catch (SQLException e) {System.out.println(e.getMessage());}}}}

 

 

 

四. 实验结果

测试需要在微信公众号网页里启用服务器, 然后根据我们服务器具体的实现一层一层的测试.

下图是第一次测试的结果, 我们可以看见还要很多的功能并未具体实现. 以及未到达预期的一些效果, 这时候我们就该回去继续修改我们服务器端的代码, 并继续测试直至实现自己想要的功能.

 

测试不全, 服务器开发完善后继续测试.

 


http://chatgpt.dhexx.cn/article/tv92IKXk.shtml

相关文章

[JAVA实现]微信公众号网页授权登录,java开发面试笔试题

我总结出了很多互联网公司的面试题及答案,并整理成了文档,以及各种学习的进阶学习资料,免费分享给大家。 扫描二维码或搜索下图红色VX号,加VX好友,拉你进【程序员面试学习交流群】免费领取。也欢迎各位一起在群里探讨技术。 推荐文章:Java 面试知识点解析;Mysql优化技巧…

后端操作微信公众号

一、功能说明 员工端使用微信公众号完成审批操作&#xff0c;涉及到的功能包含&#xff1a;自定义菜单、授权登录、消息 1、微信公众号一级菜单为&#xff1a;审批列表、审批中心、我的 2、员工关注公众号&#xff0c;员工第一次登录微信公众号&#xff0c;通过微信授权登录进行…

Java基于微信公众号接口实现授权登录源码及原理分析

微信公众号授权登录操作前提必须注册微信公众平台账号&#xff0c;注意的是订阅号不支持授权登录操作&#xff0c;因此对于个人开发者注册的订阅号是无法实现的&#xff0c;必须注册企业号的微信平台账号而具体注册流程就不详细介绍了&#xff0c;有什么疑问可去微信公众号平台…

微信公众号多域名回调系统1.0发布

这是一款基于ThinkPHP6.0框架的微信公众号多域名回调系统。 微信公众号后台默认只能授权2个网页域名&#xff0c;用本系统可突破这个限制&#xff0c;用同一个公众号对接无限多个网站。网站后台支持回调域名白名单的管理&#xff0c;以及登录记录的查看。 本系统还有微信access…

PHP微信扫码关注公众号并授权登录源码

PHP微信扫码登录看起来简单&#xff0c;但做起来有点麻烦&#xff0c;开发起来就会浪费很多的时间。 PHP判断是否首次关注公众号&#xff0c;扫码关注公众号获取微信用户头像、openid和省市等信息源码。 演示体验地址: https://www.skpan.cn/user/login.html 网盘下载地址:…

2023最新微信公众号无限回调系统源码/已修复BUG亲测可用

正文: 测试环境&#xff1a; Nginx 1.20.2 MySQL 5.6.50 PHP-7.2 1.创建站点 2.到根目录上传源码 3.创建数据库并导入 4.修改数据库信息 根目录/config.php 第5&#xff0c;6&#xff0c;7行 5.后台地址域名/admin 账号admin 密码123456 6.修改域名 根目录下 api.php 第…

Java微信公众号开发登录

MySQL基础开发篇 这部分的内容应该更合适那些刚入坑的朋友们或者是对于基础部分掌握不牢固的朋友,因此有一定经验的或者基础不错的可以自动跳至下一章内容阅读,这部分我仅把目录内容截图展示。 MySQL的优化以及管理维护 MySQL作为一款关系型数据库,SQL语句的优化是尤其重要的…

mysql 推送微信公众号_10分钟完成微信公众号第三方平台全网发布

背景&#xff1a;在微信公众平台配置服务器URL时&#xff0c;使用了新浪云SAE自带的二级域名&#xff0c;提交时出现一个安全风险的警告&#xff0c;网上查了下&#xff0c;许多服务平台和团队也遇到同样的问题。 经过一番研究 … 为什么会有安全风险的警告&#xff1f; 微信公…

springboot微信公众号管理系统vue内容文章文件上传jsp源码mysql

本项目为前几天收费帮学妹做的一个项目&#xff0c;Java EE JSP项目&#xff0c;在工作环境中基本使用不到&#xff0c;但是很多学校把这个当做编程入门的项目来做&#xff0c;故分享出本项目供初学者参考。 一、项目描述 这是一个基于springbootvue的微信公众号管理系统 二…

微信公众号迁移,需要做些什么

❤️ 个人主页&#xff1a;水滴技术 &#x1f680; 支持水滴&#xff1a;点赞&#x1f44d; 收藏⭐ 留言&#x1f4ac; &#x1f338; 订阅专栏&#xff1a;微信公众平台 文章目录 一、开通开发者二、设置IP白名单三、自定义菜单四、认证五、网页授权域名六、模板消息七、转换…

PHP微信公众号网页授权登录 扫码登录 获取用户基本信息

前言 现在微信登录是一个网站、APP的标配&#xff0c;所以微信授权登录是我们应该要掌握的。微信授权登录有4种方式&#xff1a; 1、通过微信开放平台2、通过认证的微信服务号3、通过认证的微信订阅号4、通过微信小程序曲线救国 今天我们就讲解的是微信服务号&#xff0c;通…

微信公众号多域名回调系统

介绍&#xff1a; 这是一款基于ThinkPHP6.0框架的微信公众号多域名回调系统。 微信公众号后台默认只能授权2个网页域名&#xff0c;用本系统可突破这个限制&#xff0c;用同一个公众号对接无限多个网站。网站后台支持回调域名白名单的管理&#xff0c;以及登录记录的查看。 本…

微信公众号授权获取用户信息及jwt登录

Java 微信公众号授权获取用户信息及jwt登录 &#xff01;&#xff01;&#xff01; 前言 前几篇文章小编分享过&#xff1a; 微信小程序授权获取用户手机号 jwt登录 (含源码)微信小程序支付 公众号支付 (含源码) 工作之余&#xff0c;分享下 “ 微信公众号授权 获取用户信…

手把手教程用Java实现微信公众号扫码登录功能

文章目录 前言一、环境准备二、使用步骤1. 使用微信工具包2. 创建数据表3. 登录页面代码逻辑4. 验证微信公众号登录 总结 前言 微信现今是我们必不可少的社交工具了&#xff0c;围绕微信这个生态实际上有很多东西可以做&#xff0c;我们经常会看到一些网站通过微信扫码进如公众…

[一维前缀和]leetcode303:区域和检索 - 数组不可变(easy)

题目&#xff1a; 题解&#xff1a; 一维前缀和&#xff0c;元素数组[0,j]的前缀和对应prefix[j1] 代码如下&#xff1a; class NumArray { private:vector<int> prefix; public://题解&#xff1a;一维前缀和NumArray(vector<int>& nums) {int nnums.size()…

前缀和矩阵前缀和

前缀和 前缀和是一种重要的预处理&#xff0c;能大大降低查询的时间复杂度。我们可以简单理解为“数列的前 n n n 项的和”。 例题 有 N N N 个的正整数放到数组 A A A 里&#xff0c;现在要求一个新的数组 B B B&#xff0c;新数组的第 i i i 个数 B [ i ] B[i] B[i]…

【算法基础】一维前缀和 + 二维前缀和

&#x1f466;个人主页&#xff1a;Weraphael ✍&#x1f3fb;作者简介&#xff1a;目前正在学习c和算法 ✈️专栏&#xff1a;【C/C】算法 &#x1f40b; 希望大家多多支持&#xff0c;咱一起进步&#xff01;&#x1f601; 如果文章有啥瑕疵 希望大佬指点一二 如果文章对你有…

前缀和与差分总结

目录 1.前缀和 2.前缀和算法的好处 3.二维前缀和 4.差分 5.一维差分 6.二维差分 1.前缀和 前缀和是指某序列的前n项和&#xff0c;可以把它理解为数学上的数列的前n项和&#xff0c;而差分可以看成前缀和的逆运算。合理的使用前缀和与差分&#xff0c;可以将某些复杂的…

一维前缀和

今天我们来讲讲一维前缀和 首先我们来介绍一下一维前缀和&#xff0c;emmm其实和数列的前n项和差不多&#xff0c;它能在 O ( 1 ) O(1) O(1) 的时间复杂度操作数组的任意子区间&#xff0c;比如我们要找出一段区间的和。 我们先预处理出一个前缀和数组&#xff0c;为了方便使…

【算法】前缀和(一维前缀和与二维前缀和)

前缀和是一种重要的预处理&#xff0c;能大大降低查询的时间复杂度。 【一维前缀和】 给定一个数组A[1,2,……n]&#xff0c;则它的前缀和数组为PrefixSum[1..n]。定义为&#xff1a;PrefixSum[i] A[0]A[1]...A[i-1]&#xff1b; 【例子】 A[5,6,7,8] --> PrefixSum[5,…