开发接入2.1微信公众平台接口测试账号的申请流程及步骤
优采云 发布时间: 2021-08-01 00:31开发接入2.1微信公众平台接口测试账号的申请流程及步骤
一、前言
随着微信的普及,年轻一代逐渐从QQ转向微信。界面简洁,功能强大,男女老少皆宜,是微信的特色。正是这一特性,使微信成为了中国社交软件的巨头。因此,很多产品需要在微信中开发以满足需求。
本文主要讲服务号的开发,与微信服务器的交互,以及使用微信公众号的Oauth2授权,在微信中展示本地开发的内容并进行交互。由于公众号申请需要时间和经验,需要公司相关资质,作为个人开发者,可以先在微信官方平台申请测试号,使用测试号授权与微信服务器交互并调试这一页。在开始之前,朋友们需要了解他们的需求。微信分为企业号、服务号、订阅号。不同的账户有不同的功能。具体可以到微信官网查看需要开发什么类型的账号。
二、development 接入2.1 微信公众平台接口测试账号申请
如前言所述,在开发中,我们一半人会使用官方微信公众号作为我们的生产线环境,然后aut环境可以使用测试号进行开发,从而实现与微信的交互。测试账号申请链接如下
测试帐户申请
进入后,您将看到以下屏幕。登录
登录后,您会看到以下屏幕:
因为我已经配置好了,所以显示配置后的相关参数。如果是第一次进入,需要自己配置相关参数。首先要注意的是,系统会为你生成一个appId,appsercet,后面会讲到这个的作用。现在你需要配置 URL 和 Token。
具体来说,这个所谓的URL就是你需要在你的代码中与微信进行Token验证交互的一种方法。配置完成后,微信会使用配置的url发起http请求。请注意,发起方法是获取请求。因此,代码中提供的接口需要将请求头设置为get请求:
method=(RequestMethod.GET) 方法中需要验证微信发送的消息。下面的token需要和你项目中配置的token一致。微信发送请求后,会带上签名和时间戳。 , 随机数被检查。如果匹配成功,则可以进行下一步。如果不一致,则匹配失败。需要注意的是,这个url需要有自己的域名。我个人在Sunny-Ngrok上申请了内网穿透,并赠送了一个域名。
2.2 sunn-Ngrok 内网渗透账号申请
点击申请账号
账户创建后,会有如上图所示。其实,开通域名的方式有很多种。这里我只是给我一个参考方法。然后点击打开隧道,购买隧道
一个月10元不算贵。购买后点击隧道管理
购买后会有记录,可以同时查看赠送域名和查看状态。此时的状态为离线。如果是离线,微信公众平台微信发起的请求无法配置从本机接受,需要到官网下载客户端启动隧道。
cmd命令行进入sunny.exe所在目录并执行
sunny.exe clientid 隧道 ID
如上图所示,将域名指向你本地的ip和端口号后,就可以在公众号提交配置了。在我自己机器的代码中,我做了如下配置:
@RequestMapping(value = "/wx/wxmsgreceive", method = {RequestMethod.GET})
public void verifywxtoken(HttpServletRequest request, HttpServletResponse response) throws Exception {
request.setCharacterEncoding("UTF-8");
response.setCharacterEncoding("UTF-8");
logger.info("开始校验信息是否是从微信服务器发出");
// 签名
String signature = request.getParameter("signature");
// 时间戳
String timestamp = request.getParameter("timestamp");
// 随机数
String nonce = request.getParameter("nonce");
// 通过检验signature对请求进行校验,若校验成功则原样返回echostr,表示接入成功,否则接入失败
String result = Sha1.gen(SERVER_TOKEN, timestamp, nonce);
if (signature.equals(result)) {
// 随机字符串
String echostr = request.getParameter("echostr");
logger.debug("接入成功,echostr {}", echostr);
response.getWriter().write(echostr);
}
}
微信验证码
public static String gen(String token, String timestamp, String nonce) throws NoSuchAlgorithmException {
String[] arr = new String[]{token, timestamp, nonce};
Arrays.sort(arr);
StringBuffer content = new StringBuffer();
for (String a : arr) {
content.append(a);
}
return DigestUtils.sha1Hex(content.toString());
}
因为我配置的请求头是/wx/wxmsgreceive,所以公共平台上的配置页面是域名+/wx/wxmsgreceive。
此时,当您在网页上点击提交时,微信会向本机发送认证请求,以确保本机的服务必须开启。
这是官方的说明,所以我们开发的项目中需要提供一个暴露的接口来验证微信服务器,主要是验证微信公众号网页上填写的token验证。官方文档说明如下:
所以在项目中,放置在配置文件或常量池中的token必须是微信公众号访问中填写的token的字符串。如果输入不同或者方法中的解密比较有问题,在网页上点击确定,就会出现“失败”字样。
2.3 微信开发者工具准备
下载微信开发者工具。
点击下载微信开发者工具
下载完成后扫码登录
选择微信公众号网页开发
此工具是后续开发中访问URL和前端调试必不可少的工具。
2.3 Oauth2 授权
此时测试号已经配置好了,那么接下来我们如何开发呢?在微信公众号页面,部分页面需要根据用户的身份回显不同的信息,比如用户的还款清单。这些实现首先需要获取个人信息然后与数据库进行交互,需要获取用户的唯一标识。在微信中,每个人都有并且只有一个唯一标识openId。这个openId需要通过微信的授权和回调获取。同时,微信公开文档上也有详细的授权方式介绍。如何授权可以根据自己的业务需要使用。在实际业务中,根据项目的特点固化不同的用户。我们所做的是将用户固化到数据库中,生成一个 UUID,并将 UUID 放入 cookie。授权非常重要。通过它的控件,我们可以通过获取当前用户的openid来比较用户是会员还是普通会员,是非管理员还是管理员等。这在页面跳转中起着至关重要的作用。同时,在代码中,一些静态页面可能不一定需要授权,任何人都可以查看。所以在代码中,我使用了一个过滤器,直接在前台发布了一系列.css、.js、.html等。特殊要求,我拉过来,要求微信授权。例如,我在项目中。 .go请求中需要验证,通过微信回调获取当前用户信息,固化用户。代码如下:
@WebFilter(urlPatterns = "/*", filterName = "authFilter")
public class AuthFilter implements Filter {
private final Logger logger = LoggerFactory.getLogger(AuthFilter.class);
@Autowired
private GeneralConfigService gcService;
@Autowired
private WxUserVerify wxUserVerify;
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain chain)
throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) servletRequest;
HttpServletResponse response = (HttpServletResponse) servletResponse;
try {
String url = request.getRequestURI();
logger.info("URL:" + url);
// 1.请求内容为WEB静态内容时直接放行
if (StringUtil.separatorEndWith(gcService.getStaticResourceConfig(), url)) {
chain.doFilter(servletRequest, servletResponse);
return;
}
// 2.不是静态资源进行验证
if(!checkUrl(request, response, url)){
return;
}
chain.doFilter(servletRequest, servletResponse);
return;
} catch (FilterException e1) {
//自定义异常,这里可以直接把错误信息抛出去,不需要记录日志,因为这里是业务异常。
ResponseUtil.error(response, e1.getMessage());
} catch (Exception e2) {
//无法捕捉的异常,不能直接把错误信息抛出去,需要包装下错误信息
logger.error(e2.getMessage(), e2);
ResponseUtil.error(response, "系统异常,请稍后再试...");
}
}
/**
* 与数据库配置url进行匹配
* @param request
* @param response
* @param url
* @return
*/
private boolean checkUrl(HttpServletRequest request, HttpServletResponse response, String url) {
boolean passFlag=true;
//与数据库进行匹配
Map urlConfig = getUrlConfig();
//比较匹配项
String substring = url.substring(url.lastIndexOf("/") + 1);
if (!urlConfig.containsKey(substring)) {
return passFlag;
}
CallBackMsg msg = wxUserVerify.doVerify(request, response);
if (msg.getResultCode().equals(CallBackMsg.WX_VALID_CONTINUE)) {
passFlag=false;
} else if (msg.getResultCode().equals(Const.ERROR_CODE)) {
throw new FilterException(FilterExceptionEnum.ERROR_WX_VALID_FAILE);
}
return passFlag;
}
/**
* 获取地址配置信息
* @return
*/
private Map getUrlConfig() {
// 3.获取需要验证用户的请求配置
Map configAddressList = gcService.getConfigAddressList();
logger.info(configAddressList.toString());
if (configAddressList.isEmpty()) {
logger.error("urlValidConfig为空,请检查!");
throw new FilterException(FilterExceptionEnum.ERROR_VALIDCONF_NULL);
}
return configAddressList;
}
@Override
public void destroy() {
}
}
通过创建一个类来实现Filter过滤器进行验证,@WebFilter(urlPatterns = "/*", filterName = "authFilter")对所有请求进行过滤拦截,其中封装的方法当是特定请求时会被拉取申请授权
private CallBackMsg impower(HttpServletRequest request, HttpServletResponse response) {
CallBackMsg msg = new CallBackMsg();
String code = request.getParameter("code");
try {
// 从配置获取access_token
String appId = getWxParams("wxAppid");
String secret = getWxParams("wxSecret");
String accessTokenUrl = gcService.getUrlWxFwGetToken() + "?appid=" + appId + "&secret=" + secret + "&code="
+ code + "&grant_type=authorization_code";
logger.info("accessTokenUrl:" + accessTokenUrl);
String accessTokenResult = "";
if (StringUtil.isEmpty(proxyFlag)) {
proxyFlag = gcService.getProxyFlag();
}
if (Const.PROXY_FLAG_USED.equals(proxyFlag)) {
accessTokenResult = HttpUtils.sendPostHttpsViaProxy(accessTokenUrl, "", gcService.getProxyIp(),
gcService.getProxyPort());
} else {
accessTokenResult = HttpUtils.sendPostHttps(accessTokenUrl, "");
}
logger.info("accessTokenResult:" + accessTokenResult);
JSONObject accessTokenJson = JSONObject.parseObject(accessTokenResult);
String accessToken = accessTokenJson.getString("access_token");
logger.info("accessToken:" + accessToken);
String openid = accessTokenJson.getString("openid");
logger.info("openid:" + openid);
// 查询数据库中的用户数据
String reqUrl = gcService.getSkylarkServiceUrl() + "wechat/searchwxuserinfo";
ResponseDto responseQuery = this.restInvoke(reqUrl, getQueryParam(openid, null, 1));
String userInfoDb = responseQuery.getRspMesg();
JSONObject userInfoDbJson = JSONObject.parseObject(userInfoDb);
String uuid = "";
// 数据库不存在该用户则保存数据库,存在则返回已存在的UUID
if (StringUtils.isEmpty(userInfoDbJson)) {
// 查询微信中的用户数据
uuid = UUIDGenerator.genUUID();
// 获取用户信息
String userInfoUrl = gcService.getUrlWxFwGetUser() + "?access_token=" + accessToken + "&openid="
+ openid + "&lang=zh_CN";
logger.info("userInfoUrl:" + userInfoUrl);
String userInfoResult = "";
if (Const.PROXY_FLAG_USED.equals(proxyFlag)) {
userInfoResult = HttpUtils.sendPostHttpsViaProxy(userInfoUrl, "", gcService.getProxyIp(),
gcService.getProxyPort());
} else {
userInfoResult = HttpUtils.sendPostHttps(userInfoUrl, "");
}
logger.info("userInfoResult:" + userInfoResult);
JSONObject userInfoJson = JSONObject.parseObject(userInfoResult);
// 固化用户
String addReq = gcService.getSkylarkServiceUrl() + "wechat/addwxuserinfo";
ResponseDto addResp = this.restInvoke(addReq,
getEntityParam(uuid, openid, userInfoJson));
// 失败另处理
if (!(Const.SUCCESS_CODE).equals(addResp.getRspCode())) {
// 保存数据失败,认证失败
msg.setResultCode(Const.ERROR_CODE);
return msg;
}
} else {
uuid = userInfoDbJson.getString("id");
}
// 权限:本项目中的类都可以访问该cookie,存储到客户端
Cookie cookie = new Cookie(Const.TOKEN, uuid);
cookie.setPath("/");
response.addCookie(cookie);
//将token存入session
HttpSession session = request.getSession();
session.setAttribute("authToken",uuid);
} catch (IOException e) {
logger.error(e.getMessage());
}
msg.setResultCode(Const.SUCCESS_CODE);
return msg;
}
因为项目中使用了代理服务器,不需要本地开发,所以方法有点乱,但是大体思路是客户端发起带有特殊后缀的请求时,被过滤器拦截接下来,与微信服务器进行交互授权的过程。授权成功后,向微信提供回调地址,并将微信带回的用户信息保存在表中。同时,当前用户在库表中生成的唯一标识标识存储在cookie域中。
这样就可以将获取到的openid放入cookie中了。那么这个openId就可以固化了,也就是存储在数据库中。在会话中,如果点击某个页面需要使用微信授权,可以先从cookie域中获取。如果cookie不可用,则向微信发起授权请求,获取openid后,存储cookie和curing。有一些步骤可以实现这一点。微信开放平台上有相关的demo。如果您需要我提供它们,请留言。后续授权码我会贴出来供大家参考。