微信发送的Token验证 后台如何编写

微信公众号接口配置信息需要我们后台提供接口校验sign并返回对应信息才能配置成功,官网样例是php那么java如何实现呢。结合网络一些解决办法,这边做个总结。

编写Handler接收微信请求并返回echostr

用SPRING MVC 编写一个请求处理方法。注意mapping 要跟配置的url 匹配上。例如配置如下:
在这里插入图片描述
那么我们需要编写一个 /weixin/verityToken的处理方法如下:

@RequestMapping(value = "/verityToken")
	public void verityToken(HttpServletRequest request, HttpServletResponse response) throws Exception {
		log.info("验证TOKEN,请求进来了...");

		// 微信加密签名
		String signature = request.getParameter("signature");
		// 时间戳
		String timestamp = request.getParameter("timestamp");
		// 随机数
		String nonce = request.getParameter("nonce");
		// 随机字符串
		String echostr = request.getParameter("echostr");

		//******************校验token**********************//
		PrintWriter out = response.getWriter();
		List<String> paramList = new ArrayList<>();
		paramList.add(nonce);
		paramList.add(timestamp);
		paramList.add(WECHAT_TOKEN);
		//按字节排序
		Collections.sort(paramList);
		//按顺序拼接字符串
		StringBuilder stringBuilder = new StringBuilder();
		paramList.forEach(stringBuilder::append);
		String signMe = ShaUtil.getSHA1_Oth(stringBuilder.toString());
		log.info("本系统签名为:{}",signMe);
		if(signMe.equals(signature)) {
			out.print(echostr);
		}else {
			out.print("");
		}
		out.close();
	}

这里主要做三件事,第一获取微信入参,第二校验签名,第三返回echostr。如果你觉得麻烦,其实只要把微信的入参echostr直接返回给他就可以验证通过了,但是不太安全。

接下来看看如何校验签名

注意点 需要校验签名以及签名方法

这里他是把 nonce timestamp 和token的值排序后拼接在一起通过 sha1 签名

但是需要注意的是他直接将值排序并签名而非组装拼接为key=value&key2=value。

假如:

nonce=51469832&timestamp=1652693511&token=cfq

那么最终签名字符串是

165269351151469832chenfuqiang

为了方便查看我加个分隔符

1652693511[timestamp值] 51469832 [nonce值] cfq[token值]

以下附带Sha签名工具

package ecan.netapp.util;

import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;

/**
 * @author cfq
 * 2022/5/16 17:06
 */
public class ShaUtil {

	private static final String SHA_1 = "SHA-1";
	private static final String SHA_224 = "SHA-224";
	private static final String SHA_256 = "SHA-256";
	private static final String SHA_384 = "SHA-384";
	private static final String SHA_512 = "SHA-512";

	private static final char[] hexDigits = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};

	public static String getSHA1(String painText, boolean uppercase) {
		return getSha(painText, SHA_1, uppercase);
	}

	/**
	 * Takes the raw bytes from the digest and formats them correct.
	 *
	 * @param bytes the raw bytes from the digest.
	 * @return the formatted bytes.
	 */
	private static String getFormattedText(byte[] bytes) {
		int len = bytes.length;
		StringBuilder buf = new StringBuilder(len * 2);
		// 把密文转换成十六进制的字符串形式
		for (int j = 0; j < len; j++) {
			buf.append(hexDigits[(bytes[j] >> 4) & 0x0f]);
			buf.append(hexDigits[bytes[j] & 0x0f]);
		}
		return buf.toString();
	}

	public static String getSHA1_Oth(String str) {
		if (str == null) {
			return null;
		}
		try {
			MessageDigest messageDigest = MessageDigest.getInstance("SHA1");
			messageDigest.update(str.getBytes());
			return getFormattedText(messageDigest.digest());
		} catch (Exception e) {
			throw new RuntimeException(e);
		}
	}

	public static String getSHA224(String painText, boolean uppercase) {
		return getSha(painText, SHA_224, uppercase);
	}

	public static String getSHA256(String painText, boolean uppercase) {
		return getSha(painText, SHA_256, uppercase);
	}

	public static String getSHA384(String painText, boolean uppercase) {
		return getSha(painText, SHA_384, uppercase);
	}

	public static String getSHA512(String painText, boolean uppercase) {
		return getSha(painText, SHA_512, uppercase);
	}

	/**
	 * 利用Java原生摘要实现SHA加密(支持大小写,默认小写)
	 *
	 * @param plainText 要加密的数据
	 * @param algorithm 要使用的算法(SHA-1,SHA-2224,SHA-256,SHA-384,SHA-512)
	 * @param uppercase 是否转为大写
	 * @return
	 */
	private static String getSha(String plainText, String algorithm, boolean uppercase) {
		//输入的字符串转换成字节数组
		byte[] bytes = plainText.getBytes(StandardCharsets.UTF_8);
		MessageDigest messageDigest;
		try {
			//获得SHA转换器
			messageDigest = MessageDigest.getInstance(algorithm);
			//bytes是输入字符串转换得到的字节数组
			messageDigest.update(bytes);
		} catch (NoSuchAlgorithmException e) {
			throw new RuntimeException("SHA签名过程中出现错误,算法异常");
		}
		//转换并返回结果,也是字节数组,包含16个元素
		byte[] digest = messageDigest.digest();
		//字符数组转换成字符串返回
		String result = byteArrayToHexString(digest);
		//转换大写
		return uppercase ? result.toUpperCase() : result;
	}

	/**
	 * 将字节数组转为16进制字符串
	 *
	 * @param bytes 要转换的字节数组
	 * @return
	 */
	private static String byteArrayToHexString(byte[] bytes) {
		StringBuilder builder = new StringBuilder();
		for (byte b : bytes) {
			//java.lang.Integer.toHexString() 方法的参数是int(32位)类型,
			//如果输入一个byte(8位)类型的数字,这个方法会把这个数字的高24为也看作有效位,就会出现错误
			//如果使用& 0XFF操作,可以把高24位置0以避免这样错误
			String temp = Integer.toHexString(b & 0xFF);
			if (temp.length() == 1) {
				//1得到一位的进行补0操作
				builder.append("0");
			}
			builder.append(temp);
		}
		return builder.toString();
	}

}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

呆呆的小青蛙

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值