Bootstrap

【微信公众号】用户关注公众号获取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了

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

;