ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

微信公众号绑定后台服务器已有账号

2026/9/21 1:32:47 拓冰建站 浏览量
微信公众号绑定后台服务器已有账号

前置条件

  1. 已认证公众号/已申请微信测试接口
    公众号->设置与开发->基本配置中获取appid与appsecret
    微信测试接口同理
  2. 自己的服务器资源
  3. 需要微信的接口,获取access_token、生成带参数二维码、获取二维码图片(GET请求(请使用https协议)https://mp.weixin.qq.com/cgi-bin/showqrcode?ticket=TICKET)
  4. 建立数据库绑定表(对服务器中的用户与公众号的绑定)

maven插件

下方代码用到的XmlUtil与BeanUtil需要该插件

<!-- https://mvnrepository.com/artifact/cn.hutool/hutool-all -->  
<dependency>  <groupId>cn.hutool</groupId>  <artifactId>hutool-all</artifactId>  <version>5.8.25</version>  
</dependency>

具体代码

三个基础类

绑定类

public class WeChatBindModel extends BaseModel<String> {  /**  *网站用户标识  */  @Column(name = "user_id",columnDefinition = "VARCHAR")  private String userId;  /**  *公众号用户标识  */  @Id  @Column(name = "open_id",columnDefinition = "VARCHAR")  private String openId;  /**  *创建时间  */  @Column(name = "create_time",columnDefinition = "VARCHAR")  private String createTime;  }

消息类

@Data  
public class WechatNotifyRequestVO {  private String ToUserName;  private String FromUserName;  private String CreateTime;  private String MsgType;  private String MsgId;  private String Event;  private String EventKey;  private String Ticket;  private String Content;  
}

method枚举类

public enum RequestMethodEnum {  /** get*/  GET(1,"GET"),  /** post*/  POST(2,"POST");  private final Integer code;  private final String desc;  RequestMethodEnum(Integer id, String name){  this.code = id;  this.desc = name;  }  public Integer getCode() {  return code;  }  public String getDesc() {  return desc;  }  
}

微信接入服务器的接口

接口

@RequestMapping(value = "")  
public void checkSignature(@RequestBody String xml, HttpServletRequest request, HttpServletResponse response) throws IOException {  String method = request.getMethod();  // get方法,验证微信配置是否成功if (RequestMethodEnum.GET.getDesc().equals(method)) {  String signature = request.getParameter("signature");/// 微信加密签名  String timestamp = request.getParameter("timestamp");/// 时间戳  String nonce = request.getParameter("nonce"); /// 随机数  String echostr = request.getParameter("echostr"); // 随机字符串  boolean bool = TokenValidator.validate(signature, timestamp, nonce);  if(bool){  response.getWriter().print(echostr);  }  } else {  // post方法,用来接收微信返回的消息wechatbindService.notifyMsg(xml, response);  }}

验证类TokenValidator

import java.security.MessageDigest;  
import java.security.NoSuchAlgorithmException;  
import java.util.Arrays;public class TokenValidator {  private static final String TOKEN = "FBuVsYCoPQxbPAFnrwZGRxTIWD";  public static boolean validate(String signature, String timestamp, String nonce) {  String[] arr = new String[]{TOKEN, timestamp, nonce};  Arrays.sort(arr);  StringBuilder sb = new StringBuilder();  for (String s : arr) {  sb.append(s);  }        String str = sb.toString();  String result = "";  try {  MessageDigest md = MessageDigest.getInstance("SHA-1");  byte[] digest = md.digest(str.getBytes());  result = bytesToHex(digest);  } catch (NoSuchAlgorithmException e) {  e.printStackTrace();  }        return result.equals(signature);  }  private static String bytesToHex(byte[] bytes) {  StringBuilder sb = new StringBuilder();  for (byte b : bytes) {  String hex = Integer.toHexString(b & 0xFF);  if (hex.length() == 1) {  sb.append("0");  }  sb.append(hex);  }        return sb.toString();  }  
}

消息转译与绑定

主要思路
  1. 生成带参数二维码中可以传入场景值数字或者字符串,假设当前服务器的用户标识为字符串,那么可以将对应的场景值传入字符串,也就是userId(在scene_str中传入id即可)。
  2. 生成的二维码在手机扫码后,会对服务器进行传值,将场景值与openid同时给到服务器进行绑定(在EventKey中获取场景值)

传参样例

{"expire_seconds": 604800, "action_name": "QR_STR_SCENE", "action_info": {"scene": {"scene_str": "d30dadd6-7941-4759-8bca-87eae8e10437"}}}

返回样例

<xml><ToUserName><![CDATA[开发者ID]]></ToUserName><FromUserName><![CDATA[openid]]></FromUserName><CreateTime>1708652664</CreateTime><MsgType><![CDATA[event]]></MsgType><Event><![CDATA[subscribe]]></Event><EventKey><![CDATA[d30dadd6-7941-4759-8bca-87eae8e10437]]></EventKey></xml>

主要方法

@Transactional(rollbackFor = Exception.class)  
@Override  
public void notifyMsg(String xml, HttpServletResponse response) throws IOException {  log.info("后台接收到公众号发来的数据是:\n{}", xml);  // 把微信发送给后端的xml消息转成WechatNotifyRequestVO对象  Map<String, Object> map = XmlUtil.xmlToMap(xml);  WechatNotifyRequestVO notify = BeanUtil.fillBeanWithMap(map, new WechatNotifyRequestVO(), true);  // 绑定用户信息,不存在则插入,不匹配则更新  log.info("notify \n{}",notify);  boolean isBind = handleBindInfo(notify);  // 事件类型,subscribe表示订阅,unsubscribe表示取消订阅, null表示用户发送消息过来  String event = notify.getEvent();  String content = "";  if (StringUtils.equals(event, "subscribe")) {  log.info("用户关注公众号了....");  // 欢迎语句  returnResponse(response, notify, "[Party]终于等到你!欢迎你的关注!");  }  if (StringUtils.equals(event, "unsubscribe")) {  log.info("用户取消公众号的订阅了....");  // 删除所绑定的用户信息  }  if (!StringUtils.isBlank(notify.getContent())) {  log.info("用户发送消息过来,消息内容是:" + notify.getContent());  //回复,可以回复表情,蓝色可点击链接文字等  //收到消息自动回复  returnResponse(response, notify, "/:sun已经收到您的消息~");  }  if (isBind) {  returnResponse(response, notify, "绑定成功");  }}

返回微信的方法

public void returnResponse(HttpServletResponse response, WechatNotifyRequestVO wechatNotifyRequestVO, String content) throws IOException {  response.setHeader("Content-type", "application/xml");  response.setCharacterEncoding("UTF-8");  response.getWriter().write(getXmlReturnMsg(wechatNotifyRequestVO.getFromUserName(),wechatNotifyRequestVO.getToUserName(),System.currentTimeMillis() / 1000, content));  
}

对返回的xml封装成一个方法

public String getXmlReturnMsg(String toUser,String fromUser,Long createTime,String content) {  return "<xml>\n" +  "  <ToUserName><![CDATA["+toUser+"]]></ToUserName>\n" +  "  <FromUserName><![CDATA["+fromUser+"]]></FromUserName>\n" +  "  <CreateTime>"+createTime+"</CreateTime>\n" +  "  <MsgType><![CDATA[text]]></MsgType>\n" +  "  <Content><![CDATA["+content+"]]></Content>\n" +  "</xml>";  
}

对绑定信息的处理

public boolean handleBindInfo(WechatNotifyRequestVO wechatNotifyRequestVO) {  // 判断是否是绑定信息  if (wechatNotifyRequestVO.getEvent().equals("SCAN") && wechatNotifyRequestVO.getEventKey() != null) {  return saveBindInfo(wechatNotifyRequestVO);  } else if (wechatNotifyRequestVO.getEvent().equals("subscribe") && wechatNotifyRequestVO.getEventKey() != null) {  return saveBindInfo(wechatNotifyRequestVO);  }    return false;  
}public boolean saveBindInfo(WechatNotifyRequestVO wechatNotifyRequestVO) {  // 获取二维码参数  String userId = wechatNotifyRequestVO.getEventKey();  // 如果event是SCAN则进行正则匹配  String pattern = "qrscene_(\\d+)";  Pattern regexPattern = Pattern.compile(pattern);  Matcher matcher = regexPattern.matcher(userId);  if (matcher.find()) {  userId = matcher.group(1);  }  // 获取openid  String openId = wechatNotifyRequestVO.getFromUserName();  // 获取时间  String time = wechatNotifyRequestVO.getCreateTime();  WeChatBindModel model = new WeChatBindModel(userId, openId, time);  WeChatBindModel existingModel = wechatbindRepo.findByOpenId(model.getOpenId());  if (existingModel != null && existingModel.getOpenId().equals(openId)) {  return false;  } else if (existingModel != null) {  existingModel.setUserId(model.getUserId()); existingModel.setCreateTime(model.getCreateTime());  wechatbindRepo.save(existingModel);  return true;  } else {  wechatbindRepo.save(model);  return true;  }}

参考链接