【微信公众号】用户关注公众号获取openid

首先写俩接口



import cn.hutool.core.util.StrUtil;

import com.xhsoft.mp.service.SubscribeService;
import com.xhsoft.mp.util.HexUtils;
import com.xhsoft.mp.util.MessageUtil;
import com.xhsoft.mp.util.MsgTypeEnum;
import io.swagger.annotations.Api;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import lombok.val;
import org.springblade.core.tool.jackson.JsonUtil;
import org.springframework.web.bind.annotation.*;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.validation.constraints.NotNull;
import java.io.UnsupportedEncodingException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Arrays;
@Slf4j
@RestController
@AllArgsConstructor
@RequestMapping("xxxxx")
@Api(value = "微信公众号回调", tags = "微信公众号回调")
public class WeChatCallbackController {
	/**
	 * 验证只接受微信后台的服务请求
	 *     开发者通过检验signature对请求进行校验。
	 *     若确认此次GET请求来自微信服务器,请原样返回echostr参数内容,则接入生效,成为开发者成功。
	 *     验证流程如下:
	 *     1、将token timestamp nonce三个参数进行字典序排序;
	 *     2、将三个参数字符串拼接成一个字符串进行sha1加密;
	 *     3、将加密后的字符串与signature对比,相同则该请求来源于微信。
	 */

	//微信公众号配置的Token
	private static final String TOKEN = "xxxxxx";

	@Resource
	private SubscribeService subscribeService;
	@GetMapping( "/xxxxxx/authenticate/{appKey}")
	public String authenticate(@PathVariable("appKey") String appKey, @RequestParam String signature, @RequestParam String timestamp,
							   @RequestParam String nonce, @RequestParam String echostr) {
		log.info("进入微信回调get方法,微信回调参数:signature=[{}],timestamp=[{}],nonce=[{}],echostr=[{}]", signature, timestamp, nonce, echostr);
		boolean result = checkSignature(signature, timestamp, nonce);
		if (result) {
			return echostr;
		}
		return "FAILURE";
	}
	/**
	 *  微信消息响应,通过公众号-基本配置-服务器配置,填写回调地址
	 *
	 * @param request	请求信息
	 * @return  java.lang.String
	 */
	@PostMapping( "/xxxxxx/authenticate/{appKey}")
	public String authenticate(@PathVariable("appKey") String appKey, HttpServletRequest request, HttpServletResponse response) throws UnsupportedEncodingException {
		log.info("进入微信回调post方法");
		return authenticateOfficial(request,appKey);
	}


	public boolean checkSignature(String signature, String timestamp, String nonce) {
		String[] array = new String[]{ TOKEN, timestamp, nonce};
		//先对这三个字符串字典排序,然后拼接
		Arrays.sort(array);
		StringBuilder sb = new StringBuilder();
		for (String s : array) {
			sb.append(s);
		}
		//使用SHA-1算法对拼接字符串加密
		MessageDigest messageDigest;
		String hexStr = null;
		try {
			messageDigest = MessageDigest.getInstance("SHA-1");
			byte[] digest = messageDigest.digest(sb.toString().getBytes());
			//将加密后的字符串转换成16进制字符串
			hexStr = HexUtils.bytesToHexStr(digest);

		} catch (NoSuchAlgorithmException e) {
			e.printStackTrace();
		}
		//返回校验结果
		return signature.equalsIgnoreCase(hexStr);
	}

	@NotNull
	private String authenticateOfficial(HttpServletRequest request,String appKey) throws UnsupportedEncodingException {
		//将请求、响应的编码均设置为UTF-8(防止中文乱码)
		request.setCharacterEncoding("UTF-8");
		val msg = MessageUtil.parseRequest(request);
		// 扫码或关注公众号
		log.info("wechat callback message=[{}]", JsonUtil.toJson(msg));
		//消息类型
		MsgTypeEnum msgTypeEnum = MsgTypeEnum.getByType((String) msg.get("Event"));
		if (null == msgTypeEnum) {
			return StrUtil.EMPTY;
		}
		//根据不同消息类型做不同处理
		try {
			switch (msgTypeEnum) {
				case CLICK:
					String eventKey = (String) msg.get("EventKey");
					log.info("菜单点击事件:{}",eventKey);
					break;
				case EVENT:
					break;
				case SUBSCRIBE:
					//处理关注事件
					subscribeService.doSubscribeEvent(msg, msgTypeEnum,appKey);
					log.info("用户关注公众号");
					break;
				case UNSUBSCRIBE:
					log.info("用户取消关注公众号");
					break;
				default:
					break;
			}
		}catch (Exception e){
			log.error("处理微信公众号请求异常:", e);
		}
		return StrUtil.EMPTY;
	}
}
public class HexUtils {
	public static String bytesToHexStr(byte[] bytes) {
		StringBuilder hexString = new StringBuilder();
		for (byte b : bytes) {
			String hex = Integer.toHexString(0xff & b);
			if (hex.length() == 1) hexString.append('0');
			hexString.append(hex);
		}
		return hexString.toString();
	}
}
package com.xhsoft.mp.util;

import cn.hutool.core.util.XmlUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.IOUtils;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;

import javax.servlet.http.HttpServletRequest;
import java.io.*;
import java.nio.charset.StandardCharsets;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

@Slf4j
public class MessageUtil {
	/**
	 * 解析微信公众号回调xml
	 * @param request 请求
	 * @return
	 */
	public static Map<String,Object> parseRequest(HttpServletRequest request){

		InputStream inputStream = null;
		Map<String,Object> map = new HashMap<>();
		SAXReader reader = new SAXReader();
		Document document;
		try {
			inputStream = request.getInputStream();
			//读取输入流获取文档对象
			document = reader.read(inputStream);
			//根据文档对象获取根节点
			Element rootElement = document.getRootElement();
			//获取根所有子节点
			List<Element> elements = rootElement.elements();
			for (Element e:elements) {
				map.put(e.getName(),e.getStringValue());
			}
		} catch (IOException | DocumentException e) {
			log.error("处理微信公众号请求异常:", e);
		} finally {
			if (inputStream != null) {
				try {
					inputStream.close();
				} catch (IOException ioe) {
					log.error("关闭inputStream异常:", ioe);
				}
			}
		}
		return map;
	}
}
package com.xhsoft.mp.util;

public enum MsgTypeEnum {
	EVENT("事件", "event"),
	SUBSCRIBE("用户未关注时,进行关注后的事件推送", "subscribe"),
	UNSUBSCRIBE("用户取消关注时的事件推送", "unsubscribe"),
	SCAN("用户已关注时的事件推送", "SCAN"),
	CLICK("事件类型,CLICK", "CLICK");

	private final String name;
	private final String type;

	MsgTypeEnum(String name, String type) {
		this.name = name;
		this.type = type;
	}

	public String getName() {
		return name;
	}

	public String getType() {
		return type;
	}

	public static MsgTypeEnum getByType(String type) {
		for (MsgTypeEnum msgType : values()) {
			if (msgType.getType().equals(type)) {
				return msgType;
			}
		}
		throw new IllegalArgumentException("未知的类型: " + type);
	}
}

然后你需要在微信设置里配置回调服务的接口

如果遇到xml解析报错 

org.dom4j.DocumentException: Error on line 1 of document  : Content is not allowed in prolog. Nested exception: Content is not allowed in prolog.

那就是你的xss没有设置它

#blade配置
blade:
  #xss配置
  xss:
    enabled: true
    skip-url:
      - /xxxx/xxxx/**

之后就能拿到openid了

后面就是为了发送模板消息

### 获取微信公众号用户OpenID 为了获取微信公众号用户OpenID,通常采用 OAuth2 授权机制来实现。当用户同意授权后,开发者可以利用获得的 code 参数去换取 access_token 和 openid。 #### 使用OAuth2.0授权码模式获取OpenID 构建授权链接如下所示: ```plaintext https://open.weixin.qq.com/connect/oauth2/authorize? appid=APPID& redirect_uri=REDIRECT_URI& response_type=code& scope=snsapi_base& state=STATE#wechat_redirect ``` 其中 `APPID` 是应用唯一标识,在完成微信公众平台注册后可得到;`REDIRECT_URI` 表示回调地址,即授权后的跳转页面路径;`SCOPE` 设置为 `snsapi_base` 可仅获取用户OpenID[^3]。 一旦用户点击此链接并确认授权,则会重定向至指定 URL 并附带一个临时票据 `code` 参数。随后可以通过 POST 请求向 `/sns/oauth2/access_token` 发送请求以交换该 `code` 来取得包含有 OpenID 的响应数据包。 POST 请求参数列表: - grant_type (固定值 "authorization_code") - appid (第三方用户唯一凭证) - secret (第三方用户唯一凭证密钥, 即appsecret) - code (填写第一步获取的code参数) API 地址: ```plaintext https://api.weixin.qq.com/sns/oauth2/access_token?grant_type=authorization_code&appid=APPID&secret=SECRET&code=CODE ``` 返回 JSON 数据样例: ```json { "access_token":"ACCESS_TOKEN", "expires_in":7200, "refresh_token":"REFRESH_TOKEN", "openid":"OPENID", "scope":"SCOPE" } ``` 上述过程中所提到的 `OPENID` 就是代表当前登录者的身份识别符,可用于后续的消息推送等功能开发中[^1]。 对于 Java 开发者来说,还需要确保服务器端已准备好用于验证签名的文件,并可通过公网访问,以便顺利完成整个流程中的安全校验部分[^2]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值