最近项目有一些微信公众号方面的需求,所以就去研究了一下公众号后台的开发,在这期间也请教了一些之前做过公众号开发的同事,也与一起进行开发的同事互相讨论,使得我们的开发工作有条不紊的进行下去,在此也将开发的过程和一些心得输出出来,方便后面的同事以及微信开发爱好者借鉴,如果有问题也请大家指正。
我们要进行微信公众号的开发,首先要申请一个微信公众号
大家可以百度搜索“微信公众号”,或者登陆网站https://mp.weixin.qq.com/,进行微信公众号的注册,如下图所示
注:个人用户数只能注册订阅号,由于本人已经注册,所以直接登录
选择订阅号,填写相关信息,注册成功后登陆。
要进行微信公众号的开发,首先要准备一个网络映射的工具,或者可以有直接部署的服务器,进行域名的映射。
我这里选择了norgk进行映射,大家可以在网上搜索,类似于ngrok、花生壳等工具。
首先在百度上搜索ngrok选择合适的安装包进行安装,这里就不过多描述,大家可自行百度。
安装完成,打开启动后,如下图所示
Forwarding对应的http://和https://为本地端口映射的域名,在后面基本配置中会使用到。
在进行微信公众号开发之前,我们在微信开发者文档中可以看到“接入指南”
接入微信公众平台开发,开发者需要按照如下步骤完成:
2. 验证服务器地址的有效性
看一下官方文档:https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421135319
开发者提交信息后,微信服务器将发送GET请求到填写的服务器地址URL上,GET请求携带参数如下表所示:
参数 | 描述 |
---|---|
signature | 微信加密签名,signature结合了开发者填写的token参数和请求中的timestamp参数、nonce参数。 |
timestamp | 时间戳 |
nonce | 随机数 |
echostr | 随机字符串 |
开发者通过检验signature对请求进行校验(下面有校验方式)。若确认此次GET请求来自微信服务器,请原样返回echostr参数内容,则接入生效,成为开发者成功,否则接入失败。加密/校验流程如下:
1)将token、timestamp、nonce三个参数进行字典序排序 2)将三个参数字符串拼接成一个字符串进行sha1加密 3)开发者获得加密后的字符串可与signature对比,标识该请求来源于微信
意思是说:对微信服务器发过来消息忠token、timestamp、nonce三个参数进行加密处理,然后加密得到的字符串与signature微信加密签名相比较,如果相等则返回echostr随机字符串。
开始搭建本地服务
我这边使用springboot框架为基础开发,创建微信验证controller
@RestController
public class WeChatCheckController {
private static final Logger LOGGER = LoggerFactory.getLogger(WeChatCheckController.class);
/**
* 开发者通过检验signature对请求进行校验
*
* @param request request
* @param response response
* @return String
*/
@RequestMapping(value= "/",method = RequestMethod.GET)
public void doGet(HttpServletRequest request, HttpServletResponse response) {
try {
// 微信加密签名
String signature = request.getParameter("signature");
// 时间戳
String timestamp = request.getParameter("timestamp");
// 随机数
String nonce = request.getParameter("nonce");
// 随机字符串
String echostr = request.getParameter("echostr");
PrintWriter out = response.getWriter();
// 通过检验signature对请求进行校验,若校验成功则原样返回echostr,表示接入成功,否则接入失败
if (SignUtil.checkSignature(signature, timestamp, nonce)) {
out.print(echostr);
} else {
//校验失败返回其他
out.print(timestamp);
}
out.close();
} catch (Exception e) {
LOGGER.error("微信服务器校验失败,失败信息:", e);
}
}
public class SignUtil {
/**
* 验证签名
*
* @param signature signature
* @param timestamp timestamp
* @param nonce nonce
* @return boolean
* @throws Exception
*/
public static boolean checkSignature(String signature, String timestamp,
String nonce) throws Exception {
// 1.将token、timestamp、nonce三个参数进行字典序排序
String[] arr = new String[] {Constants.WeChat.TOKEN, timestamp, nonce};
Arrays.sort(arr);
// 2. 将三个参数字符串拼接成一个字符串进行sha1加密
StringBuilder content = new StringBuilder();
for (String anArr : arr) {
content.append(anArr);
}
MessageDigest md = MessageDigest.getInstance("SHA-1");
// 将三个参数字符串拼接成一个字符串进行sha1加密
byte[] digest = md.digest(content.toString().getBytes());
String tmpStr = byteToStr(digest);
// 3.将sha1加密后的字符串可与signature对比,标识该请求来源于微信
return tmpStr.equals(signature.toUpperCase());
}
private static String byteToStr(byte[] byteArray) {
StringBuilder strDigest = new StringBuilder();
for (byte aByteArray : byteArray) {
strDigest.append(byteToHexStr(aByteArray));
}
return strDigest.toString();
}
private static String byteToHexStr(byte mByte) {
char[] digit = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A',
'B', 'C', 'D', 'E', 'F'};
char[] tempArr = new char[2];
tempArr[0] = digit[(mByte >>> 4) & 0X0F];
tempArr[1] = digit[mByte & 0X0F];
return new String(tempArr);
}
}