目录
前言:
一、公众号中配置
1、获取AppID、AppSecret:
2、配置IP白名单:
问题:
解决:
3、配置JS接口安全域名:
重要:认真阅读系统提示的注意事项:
问题:
解决:
二、前端页面开发:
三、后端服务开发:
* 流程:
代码:
(1)主要代码:
(2)相关代码:
校验工具:
1、appId、secret校验及生成access_token工具:微信公众平台接口调试工具
2、 微信 JS 接口签名校验工具:微信 JS 接口签名校验工具
错误码:
前言:
先给大家放一个微信官方文档的链接,要知道,接入第三方,最正确的方式,就是看第三方提供的文档,根据自己的需求在文档里找相关内容已经操作步骤。
微信官方文档连接:微信官方文档 | 微信开放文档
这里给大家放的是微信的相关内容开发的总文档链接,大家根据自己需求在里面点击进入查看自己需要的文档即可。
微信功能有太多,这篇文章以接入微信扫一扫为例说明,好了,话不多说,进入正题~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
一、公众号中配置
1、获取AppID、AppSecret:
ps:(1)这个AppID、AppSecret是后面后端服务开发中需要用到的
*(2)这个AppSecret最好找个地方记下来,后面接入其他服务开发时进行复用,尽量不要重置,若重置,会对之前用到这个AppSecret的服务有影响。
2、配置IP白名单:
这里的ip应该配置部署服务器的网络出口机器的ip,这里配置后,后面才能获取到正确的access_token
问题:
调用微信的接口报错 :
{ errcode: 40164, errmsg: 'invalid ip 117.100.47.169 ipv6 ::ffff:117.100.47.169, not in whitelist hint: [39lrcA01394100]' }
解决:
这个问题就是因为这里没有加入白名单,将报错信息中的ip加入到这个白名单里即可。
3、配置JS接口安全域名:
重要:认真阅读系统提示的注意事项:
(1)将MP_verify_aZa3PhSE1MBXupsK.txt这个文件下载下来,放到自己的项目路径下
(2)确保可以访问,最好自己先访问确定一下
(3)这个路径最好和自己的页面路径位于同一目录层次下,不然可能会找不到页面路径
(4)配置时,域名签名不要http://或https://,直接域名加路径即可,配置在可访问的文件的上级目录即可,例如:https://wx.qq.com/mp/MP_verify_aZa3PhSE1MBXupsK.txt可以访问到你项目下的这个文件,那么你这里要配置的应该是:wx.qq.com/mp
(5)一个月最多可以保存5次
问题:
调用微信的接口报错 : config:invalid url domain
解决:
这个错的原因就是这里配置的JS接口安全域名和当前页面的域名不一致导致的,将这里的MP_verify_aZa3PhSE1MBXupsK.txt文件和页面放在同一路径下即可。
二、前端页面开发:
博主是做后端开发的,前端在此不做过多阐述,其实就是后端给前端一个接口,提供以下信息,其他页面开发,放一个文档链接,大家自己查看:
微信接入前端JS-SDK说明文档:概述 | 微信开放文档
wx.config({debug: true, // 开启调试模式,调用的所有api的返回值会在客户端alert出来,若要查看传入的参数,可以在pc端打开,参数信息会通过log打出,仅在pc端时才会打印。appId: '', // 必填,公众号的唯一标识timestamp: , // 必填,生成签名的时间戳nonceStr: '', // 必填,生成签名的随机串signature: '',// 必填,签名jsApiList: [] // 必填,需要使用的JS接口列表
});
三、后端服务开发:
从上面前端所需内容可以看出,后端需要提供给前端的其实就那几个参数,jsApiList是前端的参数,无需返回,我们可以创建一个对象来返回:
@Data
public class WeChatConfigVo {//appIdprivate String appId;//时间戳private Long timestamp;//随机串private String nonceStr;//签名private String signature;
}
* 流程:
这里大概说一下流程:(appId和appSecret之前页面已经拿到了)
1、拿到前端传入的当前页面url,注意这个url指的是当前页面的url,并非当前请求的url,所以应该让前端传入,而并非服务端获取
2、生成随机串、获取当前时间戳
3、根据appId和appSecret获取access_token
4、根据access_token去获取ticket
5、根据随机串、时间戳、ticket和url生成签名
6、将数据返回给前端页面
代码:
到了大家最开心的时候了,上代码:
(1)主要代码:
package com.sense.cloud.activateService.service;import com.alibaba.fastjson.JSONException;
import com.alibaba.fastjson.JSONObject;
import com.sense.cloud.activateService.utils.HttpRequestUtils;
import com.sense.cloud.activateService.utils.IPUtil;
import com.sense.cloud.activateService.vo.AccessTokenVo;
import com.sense.cloud.activateService.vo.GetWechatConfigParamReq;
import com.sense.cloud.activateService.vo.WeChatConfigVo;
import com.sense.cloud.common.code.ErrorCode;
import com.sense.cloud.common.exception.AppException;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;import javax.servlet.http.HttpServletRequest;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.*;@Service
@Slf4j
public class WeChatConfigService {@Value("${wechatConfig.appId}")private String appId;@Value("${wechatConfig.appSecret}")private String appSecret;// 获取ACCESS_TOKEN的urlpublic final static String GET_ACCESS_TOKEN_URL = "https://api.weixin.qq.com/cgi-bin/token";// 获取ticket的urlpublic final static String GET_TICKET_URL = "https://api.weixin.qq.com/cgi-bin/ticket/getticket";/*** 获取微信公众号认证参数* @param request* @return*/public WeChatConfigVo getWechatConfigParams(GetWechatConfigParamReq req, HttpServletRequest request){try {if (log.isDebugEnabled()) {log.debug("WeChatConfigService getWechatConfigParams req {}: ", req);}String params = request.getQueryString();if(StringUtils.isEmpty(params)){params="";}else{params="?"+params;}//注意这个url指的是当前页面的url,并非当前请求的url,所以应该让前端传入,而并非服务端获取String url = req.getUrl();log.debug("Url:{}", url);//request.getRequestURL().toString();String ipAddress = IPUtil.getIpAddress(request);// 时间戳long timestamp = (new Date().getTime()) / 1000;// 随机串String nonceStr = UUID.randomUUID().toString();// 获取 access_tokenAccessTokenVo accessToken = getAccessToken(appId, appSecret);String access_token = accessToken.getToken();log.debug("access_token:{}" ,access_token);// 根据 access_token 获取 ticketHashMap<String, String> getTicketParams = new HashMap<>();getTicketParams.put("access_token", access_token);getTicketParams.put("type", "jsapi");String response = HttpRequestUtils.sendGet(GET_TICKET_URL, getTicketParams);if (StringUtils.isEmpty(response)) {log.debug("获取微信公众号ticket失败, response == > {}", response);}JSONObject jsonObject = JSONObject.parseObject(response);log.debug("getTicket response:{}" ,JSONObject.toJSONString(jsonObject));String ticket = jsonObject.getString("ticket");if(log.isDebugEnabled()) {log.debug("要加密的参数:"+nonceStr+" "+ticket+" "+timestamp+" "+url);}//根据随机串、时间戳、ticket和url生成签名String signature = getsig(nonceStr,ticket,String.valueOf(timestamp),url);WeChatConfigVo weChatConfigVo = new WeChatConfigVo();weChatConfigVo.setAppId(appId);weChatConfigVo.setTimestamp(timestamp);weChatConfigVo.setNonceStr(nonceStr);weChatConfigVo.setSignature(signature);log.debug(JSONObject.toJSONString(weChatConfigVo));return weChatConfigVo;} catch (Exception e) {log.error("获取微信公众号认证参数失败 :", e);throw e;}}/*** 获取access_token*/public static AccessTokenVo getAccessToken(String appid, String appsecret) {AccessTokenVo accessToken = null;Map<String, String> paramMap = new HashMap<>();paramMap.put("grant_type", "client_credential");paramMap.put("appid", appid);paramMap.put("secret", appsecret);String response = HttpRequestUtils.sendGet(GET_ACCESS_TOKEN_URL, paramMap);if (StringUtils.isEmpty(response)) {log.debug("获取获取access_token失败");throw new AppException(ErrorCode.TOKEN_INVALID);}if (log.isDebugEnabled()) {log.debug("resp: {}", response);}JSONObject jsonObject = JSONObject.parseObject(response);if (log.isDebugEnabled()) {log.debug("respJSONObject : {}", jsonObject.toJSONString());}// 如果请求成功if (null != jsonObject) {try {accessToken = new AccessTokenVo();accessToken.setToken(jsonObject.getString("access_token"));accessToken.setExpiresIn(jsonObject.getString("expires_in"));} catch (JSONException e) {accessToken = null;// 获取token失败}}return accessToken;}/*** 拼接 signature 签名* @param noncestr* @param jsapi_ticket* @param timestamp* @param url* @return*/private static String getsig(String noncestr,String jsapi_ticket,String timestamp,String url){String[] paramArr = new String[] { "jsapi_ticket=" + jsapi_ticket,"timestamp=" + timestamp, "noncestr=" + noncestr, "url=" + url };Arrays.sort(paramArr);// 将排序后的结果拼接成一个字符串String content = paramArr[0].concat("&"+paramArr[1]).concat("&"+paramArr[2]).concat("&"+paramArr[3]);String gensignature = null;try {MessageDigest md = MessageDigest.getInstance("SHA-1");// 对拼接后的字符串进行 sha1 加密System.out.println("拼接加密签名:"+content);byte[] digest = md.digest(content.getBytes());gensignature = byteToStr(digest);} catch (NoSuchAlgorithmException e) {e.printStackTrace();}// 将 sha1 加密后的字符串与 signature 进行对比if (gensignature != null) {return gensignature;// 返回signature} else {return "false";}}/*** 将字节数组转换为十六进制字符串** @param byteArray* @return*/private static String byteToStr(byte[] byteArray) {String strDigest = "";for (int i = 0; i < byteArray.length; i++) {strDigest += byteToHexStr(byteArray[i]);}return strDigest;}/*** 将字节转换为十六进制字符串** @param mByte* @return*/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];String s = new String(tempArr);return s;}
}
(2)相关代码:
package com.sense.cloud.activateService.controller;import com.sense.cloud.activateService.service.WeChatConfigService;
import com.sense.cloud.activateService.vo.GetWechatConfigParamReq;
import com.sense.cloud.activateService.vo.WeChatConfigVo;
import com.sense.cloud.common.code.ErrorCode;
import com.sense.cloud.common.exception.AppException;
import com.sense.cloud.common.vo.BaseRes;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;import javax.servlet.http.HttpServletRequest;/*** @Author: Hu Wentao* @Date: 2021/11/1 15:55* @Description: 微信公众号配置*/
@Slf4j
@CrossOrigin
@RestController
public class WechatConfigController {@Autowiredprivate WeChatConfigService weChatConfigService;/*** 获取微信公众号参数* @param request* @return*/@PostMapping("/wechat/getWechatConfigParam")public BaseRes<WeChatConfigVo> getWechatConfigParam(@RequestBody GetWechatConfigParamReq req, HttpServletRequest request) {try {if (log.isDebugEnabled()) {log.debug("WechatConfigController getWechatConfigParam : {}", req);}if (req == null || StringUtils.isEmpty(req.getUrl())) {throw new AppException(ErrorCode.ERR_PARA);}WeChatConfigVo configVo = weChatConfigService.getWechatConfigParams(req, request);return BaseRes.ok(configVo);} catch (Exception e) {log.error("获取微信公众号参数失败 :", e);return BaseRes.fail(ErrorCode.ERR_SYS);}}
}
package com.sense.cloud.activateService.vo;import lombok.Data;/*** @Author: Hu Wentao* @Date: 2021/11/2 15:58* @Description: 获取微信公众号配置参数请求体*/@Data
public class GetWechatConfigParamReq {//urlprivate String url;
}
校验工具:
1、appId、secret校验及生成access_token工具:微信公众平台接口调试工具
2、 微信 JS 接口签名校验工具:微信 JS 接口签名校验工具
错误码:
这里放些从微信官方扒下来的错误码信息,方便大家查错