相关文章
在开发微信公众号时,需要先去微信公众平台填写服务器配置,并验证 Token,没想到第一步就踩坑了!!!
一、服务器配置
-
路径:开发 --> 基本配置 --> 服务器配置
-
参数说明
-
服务器地址 (URL)
-
注意:该 URL 一定要通过外网能访问到 (此处我使用了 frp 进行内网穿透,以便在本地机器直接调试)
-
在提交配置的时候,微信服务器会发送一个 GET 请求到该 URL 上,并会携带 signature、timestamp、nonce、echostr 这四个参数
参数 作用 signature 微信加密签名,signature 结合了开发者填写的
token 参数和请求中的 timestamp 参数、nonce 参数timestamp 时间戳 nonce 随机数 echostr 随机字符串
-
-
令牌 (Token)
- 可由我们自行定义,主要作用是参与生成签名,会与微信服务器请求的签名进行比较
-
消息加解密密钥 (EncodingAESKey)
- 可由我们自行定义或随机生成,主要作用是参与接收和推送给公众平台消息的加解密
-
消息加解密方式
-
共有三种模式:明文模式、兼容模式、安全模式,此处我选择的是安全模式,大家可以根据自己的具体需求,选择相应的模式
-
-
二、验证 Token
-
WXPublicController.java
@RestController @RequestMapping(value = "/wxpublic") public class WXPublicController { @GetMapping(value = "/verify_wx_token") public String verifyWXToken(HttpServletRequest request) throws AesException { String msgSignature = request.getParameter("signature"); String msgTimestamp = request.getParameter("timestamp"); String msgNonce = request.getParameter("nonce"); String echostr = request.getParameter("echostr"); if (WXPublicUtils.verifyUrl(msgSignature, msgTimestamp, msgNonce)) { return echostr; } return null; } }
-
WXPublicUtils.java
public class WXPublicUtils { /** * 验证Token * @param msgSignature 签名串,对应URL参数的signature * @param timeStamp 时间戳,对应URL参数的timestamp * @param nonce 随机串,对应URL参数的nonce * * @return 是否为安全签名 * @throws AesException 执行失败,请查看该异常的错误码和具体的错误信息 */ public static boolean verifyUrl(String msgSignature, String timeStamp, String nonce) throws AesException { // 这里的 WXPublicConstants.TOKEN 填写你自己设置的Token就可以了 String signature = SHA1.getSHA1(WXPublicConstants.TOKEN, timeStamp, nonce); if (!signature.equals(msgSignature)) { throw new AesException(AesException.ValidateSignatureError); } return true; } }
-
SHA1.java
public class SHA1 { /** * 用SHA1算法验证Token * * @param token 票据 * @param timestamp 时间戳 * @param nonce 随机字符串 * @return 安全签名 * @throws AesException */ public static String getSHA1(String token, String timestamp, String nonce) throws AesException { try { String[] array = new String[] { token, timestamp, nonce }; StringBuffer sb = new StringBuffer(); // 字符串排序 Arrays.sort(array); for (int i = 0; i < 3; i++) { sb.append(array[i]); } String str = sb.toString(); // SHA1签名生成 MessageDigest md = MessageDigest.getInstance("SHA-1"); md.update(str.getBytes()); byte[] digest = md.digest(); StringBuffer hexstr = new StringBuffer(); String shaHex = ""; for (int i = 0; i < digest.length; i++) { shaHex = Integer.toHexString(digest[i] & 0xFF); if (shaHex.length() < 2) { hexstr.append(0); } hexstr.append(shaHex); } return hexstr.toString(); } catch (Exception e) { e.printStackTrace(); throw new AesException(AesException.COMPUTE_SIGNATURE_ERROR); } } }
-
AesException.java
public class AesException extends Exception { public final static int VALIDATE_SIGNATURE_ERROR = -40001; public final static int PARSE_XML_ERROR = -40002; public final static int COMPUTE_SIGNATURE_ERROR = -40003; public final static int ILLEGAL_AES_KEY = -40004; public final static int VALIDATE_APPID_ERROR = -40005; public final static int ENCRYPT_AES_ERROR = -40006; public final static int DECRYPT_AES_ERROR = -40007; public final static int ILLEGAL_BUFFER = -40008; public final static int ENCODE_BASE64_ERROR = -40009; public final static int DECODE_BASE64_ERROR = -40010; public final static int GEN_RETURN_XML_ERROR = -40011; private int code; private static String getMessage(int code) { switch (code) { case VALIDATE_SIGNATURE_ERROR: return "签名验证错误"; case PARSE_XML_ERROR: return "xml解析失败"; case COMPUTE_SIGNATURE_ERROR: return "sha加密生成签名失败"; case ILLEGAL_AES_KEY: return "SymmetricKey非法"; case VALIDATE_APPID_ERROR: return "appid校验失败"; case ENCRYPT_AES_ERROR: return "aes加密失败"; case DECRYPT_AES_ERROR: return "aes解密失败"; case ILLEGAL_BUFFER: return "解密后得到的buffer非法"; case ENCODE_BASE64_ERROR: return "base64加密错误"; case DECODE_BASE64_ERROR: return "base64解密错误"; case GEN_RETURN_XML_ERROR: return "xml生成失败"; default: return null; } } public int getCode() { return code; } public AesException(int code) { super(getMessage(code)); this.code = code; } }
三、完成配置
-
此时,我们提交配置,Token 就能验证通过了