微信公众号踩坑之旅

2019-02-20 15:26:27 By Magina

开发过程中使用的是微信测试公众号

在线申请:

https://mp.weixin.qq.com/debug/cgi-bin/sandbox?t=sandbox/login

第二步准备工作.获取开发环境的影射外网地址:ngrok百度自行下载,

打开ngrok.exe -> ngrok http 8080  // 此处同样可以指定其他端口.例如8087

此时:外网穿透已经成功.http和https理论上都可以使用.我使用的是http.

下面去测试号 页面查找网页授权域名: 注意.不带http://

再设置js接口域名

此时页面的配置基本完成.我们需要接入到    微信接口配置信息:

微信提供一系列的规则.测试号相对简单,只需提供接口url和token,token自定义.需要和我们后台项目设定的一致.url指向后台项目的接口校验.

项目搭建不多做介绍.

官方提供信息:

开发者提交信息后,微信服务器将发送GET请求到填写的服务器地址URL上,GET请求携带参数如下表所示:

signature

微信加密签名,signature结合了开发者填写的token参数和请求中的timestamp参数、nonce参数。

timestamp

时间戳

nonce

随机数

echostr

随机字符串

开发者通过检验signature对请求进行校验(下面有校验方式)。若确认此次GET请求来自微信服务器,请原样返回echostr参数内容,则接入生效,成为开发者成功,否则接入失败。加密/校验流程如下:

1)将token、timestamp、nonce三个参数进行字典序排序

2)将三个参数字符串拼接成一个字符串进行sha1加密

3)开发者获得加密后的字符串可与signature对比,标识该请求来源于微信

项目代码controller:

//    token
private final String token = "cass";@GetMapping(value = "/checkSignature")
public void testInfo(HttpServletRequest request, HttpServletResponse response) throws Exception {log.info("=============================进入Wechat==================================");log.info(request.toString());System.out.println("开始签名校验");String signature = request.getParameter("signature");String timestamp = request.getParameter("timestamp");String nonce = request.getParameter("nonce");String echostr = request.getParameter("echostr");//排序String sortString = sort(token, timestamp, nonce);//按照微信官方文档提供的规则 组合String//加密String mytoken = Decript.SHA1(sortString);//校验签名if (mytoken != null && mytoken != "" && mytoken.equals(signature)) {System.out.println("签名校验通过。");response.getWriter().print(echostr); //如果检验成功输出echostr,微信服务器接收到此输出,才会确认检验完成。}
}
public static String sort(String token, String timestamp, String nonce) {String[] strArray = {token, timestamp, nonce};Arrays.sort(strArray);StringBuilder sbuilder = new StringBuilder();for (String str : strArray) {sbuilder.append(str);}return sbuilder.toString();
}

Decript(sha1加密类):

public class Decript {public static String SHA1(String decript) {try {MessageDigest digest = MessageDigest.getInstance("SHA-1");digest.update(decript.getBytes());byte messageDigest[] = digest.digest();// Create Hex StringStringBuffer hexString = new StringBuffer();// 字节数组转换为 十六进制 数for (int i = 0; i < messageDigest.length; i++) {String shaHex = Integer.toHexString(messageDigest[i] & 0xFF);if (shaHex.length() < 2) {hexString.append(0);}hexString.append(shaHex);}return hexString.toString();} catch (NoSuchAlgorithmException e) {e.printStackTrace();}return "";}
}

很简单理解.微信官方根据你给的token会生成一个签名.然后参数给开发者.开发者也必须生存一个签名.签名equals相等.ok.你即可以成为开发者

项目url:http://1527ade6.ngrok.io/Wechat/checkSignature

坑: 这个url里面一定不能带端口号.就算用nginx配置过也需要改掉.项目里被坑过

token:cass

此时.开发者账号已经接入成功了.

我们可以先通过微信官方的api接口 测试一些功能.比如自定义菜单跳转后台

接口api:https://mp.weixin.qq.com/debug/cgi-bin/apiinfo?

https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=wx00ab3357d8686b43&secret=39d83b4cad24fdcf519bb998c9b9e30f
返回结果:
* 200 OK
* Connection: close
* Date: Wed, 20 Feb 2019 07:36:19 GMT
* Content-Type: application/json; encoding=utf-8
* Content-Length: 173
*
{"access_token": "18_NWcKxgaJP6EnY7QO-QuJSZlxIxpAt6ekwIHm-B7xRkHuKJwOwYra6YW2qRQc_9rCrm2d1o7yURT3Yw5H8ClA6gIfg2wSa9pZAcWsx-NEi5am2T6WU_kDKMk9TYoQZEiAGAHQV","expires_in": 7200
}

如果返回非200就是有问题.这步很简单.如有问题百度

https://api.weixin.qq.com/cgi-bin/menu/get?access_token=18_NWcKxgaJP6EnY7QO-QuJSZlxIxpAt6ekwIHm-B7xRkHuKJwOwYra6YW2qRQc_9rCrm2d1o7yURT3Yw5H8ClA6gIfg2wSa9pZAcWsx-NEi5am2T6WU_kDKMk9TYoQZEiAGAHQV
返回结果:
* 200 OK
* Connection: keep-alive
* Date: Wed, 20 Feb 2019 08:10:35 GMT
* Content-Type: application/json; encoding=utf-8
* Content-Length: 408
*
{"menu": {"button": [{"type": "view","name": "测试案例","url": "http://87af154f.ngrok.io/Wechat/testData","sub_button": [ ]},{"type": "view","name": "摇珠计划","url": "https://open.weixin.qq.com/connect/oauth2/authorize?appid=WWWWWredirect_uri=http://87af154f.ngrok.io/Wechat/tologin/userinforesponse_type=codescope=snsapi_userinfostate=STATE#wechat_redirect","sub_button": [ ]}]}}
提示:
Request successful

这是我的正常返回值.查看菜单接口

下面来自定义菜单.拼接menu的json:

{"button": [{"type": "view","name": "测试十三","url": "http://1527ade6.ngrok.io/Wechat/testData","sub_button": [ ]},{"type": "view","name": "计划十三","url": "https://open.weixin.qq.com/connect/oauth2/authorize?appid=wx00ab3357d8686b43&redirect_uri=http://1527ade6.ngrok.io/Wechat/tologin/userinfo&response_type=code&scope=snsapi_userinfo&state=STATE#wechat_redirect","sub_button": [ ]}]}

来说下这边的坑:

1.json不需要带menu层.只需要button这层

2:url最好带上http.这里的http只得是本身跳转的url和rediect_uri的url.微信的是https不需要管

3.下面的url等下讲解

http://1527ade6.ngrok.io/Wechat/testData 对应的是项目接口的localhost:8080/Wechat/testData

代码:

@GetMapping(value = "/testData")
public String testInfo() throws Exception {log.info("测试通过了啊");return "测试通过了啊";
}

自定义菜单创建成功.ok这时候我们拿自己的微信去扫二维码关注看下结果

这是我手机关注后的菜单显示.

简单的自定义菜单已经完成.

下面说下"计划十三"这个button的url:https://open.weixin.qq.com/connect/oauth2/authorize?appid=wx00ab3357d8686b43&redirect_uri=http://1527ade6.ngrok.io/Wechat/tologin/userinfo&response_type=code&scope=snsapi_userinfo&state=STATE#wechat_redirect

这个是微信网页授权的url.访问微信的接口,会回调到http://1527ade6.ngrok.io/Wechat/tologin/userinfo我们这个接口.在这个接口里面.我们回得到当前用户的微信基本信息.从授权说起.

官方文档:https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421140842

想从微信按钮跳转到我们自定义网页(第三方).需要用户对此网页授权.才能继续.微信提供静默授权和手动授权.很简单.静默就是你点击了按钮.微信后台自动帮你授权了.再跳转第三方网页.手动就会弹出一个授权的页面点击授权才能跳转第三方网页.官方给出2中的url:

scope为snsapi_base静默授权
https://open.weixin.qq.com/connect/oauth2/authorize?appid=wx3431726cd6f932f4&redirect_uri=http://pds.movitech.cn:8686/Wechat/tologin/userinfo&response_type=code&scope=snsapi_base&state=123#wechat_redirect
scope为snsapi_userinfo手动授权.
https://open.weixin.qq.com/connect/oauth2/authorize?appid=wx3431726cd6f932f4&redirect_uri=http://pds.movitech.cn:8686/Wechat/tologin/userinfo&response_type=code&scope=snsapi_userinfo&state=STATE#wechat_redirect
只需要把appid和rediect_uri替换调就可以

接口实例,我的项目例子是这样:前后端分离.此接口既可以给前端调用,也可以给微信调用.调用授权跳转接口.如果此用户不再项目系统里面用手机号码注册过.就返回前端一个false.否则就查找出planList业务数据给前端.

@GetMapping("/tologin/userinfo")@ApiOperation(value = "传入code登录微信接口", notes = "前端传入code,调微信接口,获取微信用户信息,返回值:status为true则是此微信号绑定过,返回planList计划列表.status为flase" +"微信号尚未绑定手机号码,需要跳转手机验证码页面")public Response check(HttpServletRequest request, HttpSession session, Map<String, Object> map) throws BusinessException {log.info("=======================进入tologin/userinfo");//首先判断一下session中,是否有保存着的当前用户的信息,有的话,就不需要进行重复请求信息WeiXinUser weiXinUser = null;if (session.getAttribute("currentUser") != null) {weiXinUser = (WeiXinUser) session.getAttribute("currentUser");} else {/*** 进行获取openId,必须的一个参数,这个是当进行了授权页面的时候,再重定向了我们自己的一个页面的时候,* 会在request页面中,新增这个字段信息,要结合这个ProjectConst.Get_WEIXINPAGE_Code这个常量思考*/String code = request.getParameter("code");
//             code = "021Ct0F42we4lP0ohqI42664F42Ct0Fg";try {//得到当前用户的信息(具体信息就看weixinUser这个javabean)weiXinUser = getTheCode(session, code);//将获取到的用户信息,放入到session中session.setAttribute("currentUser", weiXinUser);} catch (Exception e) {e.printStackTrace();}}if(ObjectUtils.isEmpty(weiXinUser)){return Response.fail("3999","查找微信用户失败!");}List planList = wechatService.getPlanListByOpenId(weiXinUser.getOpenId());if(CollectionUtils.isEmpty(planList) && null != planList){map.put("status",true);map.put("planList",planList);}else{map.put("status",false);}map.put("weiXinUser", weiXinUser);return Response.succeed(map);}

看到这里很疑惑.为什么直接从String code = request.getParameter("code");

这个是当进行了授权页面的时候,再重定向了我们自己的一个页面的时候,

微信会在request页面中,新增这个字段信息.不信的话我们可以测试:

打开微信开发者工具.1获取token.2.获取button.3复制button的url:https://open.weixin.qq.com/connect/oauth2/authorize?appid=wx00ab3357d8686b43&redirect_uri=http://1527ade6.ngrok.io/Wechat/tologin/userinfo&response_type=code&scope=snsapi_userinfo&state=STATE#wechat_redirect

粘贴到地址栏enter.演示利用微信工具获取code.最后一个接口的请求参数里面可以看到

看到上图console.回调地址里面其实就是?code=XXXXXXXXXXXXXXXXXXX去请求我们的接口

其他相关代码:

WeiXinUser


/**
* @desc 对于微信用户本身存在的信息的一个javabean,不需要在数据库中进行处理
**/
@Setter
@Getter
public class WeiXinUser {// 用户的标识private String openId;// 关注状态(1是关注,0是未关注),未关注时获取不到其余信息private int subscribe;// 用户关注时间,为时间戳。如果用户曾多次关注,则取最后关注时间private String subscribeTime;// 昵称private String nickname;// 用户的性别(1是男性,2是女性,0是未知)private int sex;// 用户所在国家private String country;// 用户所在省份private String province;// 用户所在城市private String city;// 用户的语言,简体中文为zh_CNprivate String language;// 用户头像private String headImgUrl;@Overridepublic String toString() {return "WeiXinUser{" +"openId='" + openId + '\'' +", 昵称='" + nickname + '\'' +", 性别=" + (sex == 1? "男" : "女") +", 国籍='" + country + '\'' +", 省份='" + province + '\'' +", 城市='" + city + '\'' +'}';}
}

AccessToken


@Getter
@Setter
public class AccessToken {// 错误codeprivate Integer errcode;// 错误msgprivate String errmsg;// 获取到的凭证private String access_token;// 凭证有效时间,单位:秒private Long expires_in;@Overridepublic String toString() {return "AccessToken{" +"errcode='" + errcode + '\'' +", errmsg='" + errmsg + '\'' +", access_token='" + access_token + '\'' +", expires_in=" + expires_in +'}';}
}

ProjectConst:

/**
* @author Magina
* @create 2019-02-20 17:36:12 By Magina
* @desc 项目相关的静态量
**/
public class ProjectConst {/*** 用于获取当前与微信公众号交互的用户信息的接口(一般是用第一个接口地址)*/public static final String GET_WEIXIN_USER_URL = "https://api.weixin.qq.com/cgi-bin/user/info?access_token=ACCESS_TOKEN&openid=OPENID";public final static String GetPageUsersUrl = "https://api.weixin.qq.com/sns/userinfo?access_token=ACCESS_TOKEN&openid=OPENID&lang=zh_CN";/*** 用于进行网页授权验证的接口URL,通过这个才可以得到opendID等字段信息*/public final static String GET_WEBAUTH_URL = "https://api.weixin.qq.com/sns/oauth2/access_token?appid=APPID&secret=SECRET&code=CODE&grant_type=authorization_code";/*** 用于进行当点击按钮的时候,能够在网页授权之后获取到code,再跳转到自己设定的一个URL路径上的接口,这个主要是为了获取之后于* 获取openId的接口相结合* 注意:参数:toselfURL  表示的是当授权成功后,跳转到的自己设定的页面,所以这个要根据自己的需要进行修改*/public final static String Get_WEIXINPAGE_Code = "https://open.weixin.qq.com/connect/oauth2/authorize?appid=APPID&redirect_uri=toselfURL&response_type=code&scope=snsapi_userinfo&state=1#wechat_redirect";/*** 获取access_token的URL*/public static final String ACCESS_TOKEN_URL = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=APPID&secret=APPSECRET";/*** SMS服务的参数*/
//    public static final String SMS_ACCOUNT_SID = "AC83609469e581a0349a3aa499a55938b9";
//    public static final String SMS_AUTH_TOKEN = "f7020503662698bce7e3e5465278c31e";}

oauth2GetOpenid:

/**
* 进行用户授权,获取到需要的授权字段,比如openId
* @param code 识别得到用户id必须的一个值
* 得到网页授权凭证和用户id
* @return
*/
@Override
public Map<String, String> oauth2GetOpenid(String code) {//自己的配置appid(公众号进行查阅)String appid = projectAppid;//自己的配置APPSECRET;(公众号进行查阅)String appsecret = projectAppsecret;//拼接用户授权接口信息String requestUrl = ProjectConst.GET_WEBAUTH_URL.replace("APPID", appid).replace("SECRET", appsecret).replace("CODE", code);//存储获取到的授权字段信息Map<String, String> result = new HashMap<String, String>();try {JSONObject OpenidJSONO = WeiXinUtils.doGetStr(requestUrl);//OpenidJSONO可以得到的内容:access_token expires_in  refresh_token openid scopeString Openid = String.valueOf(OpenidJSONO.get("openid"));System.out.println("openId:"+Openid);String AccessToken = String.valueOf(OpenidJSONO.get("access_token"));//用户保存的作用域String Scope = String.valueOf(OpenidJSONO.get("scope"));String refresh_token = String.valueOf(OpenidJSONO.get("refresh_token"));result.put("Openid", Openid);result.put("AccessToken", AccessToken);result.put("scope", Scope);result.put("refresh_token", refresh_token);} catch (Exception e) {e.printStackTrace();}return result;
}

wechatService.getAuthInfo(code);

/**
* 获取到微信用户的唯一的OpendID
* @param code  这是要获取OpendId的必须的一个参数
* @return
*/
@Override
public Map<String , String> getAuthInfo(String code) {//进行授权验证,获取到OpenID字段等信息Map<String, String> result = oauth2GetOpenid(code);// 从这里可以得到用户openidString openId = result.get("Openid");return result;
}

wechatService.getUserInfo(accessToken, openId):

@Override
public WeiXinUser getUserInfo(String accessToken, String openId) {WeiXinUser weixinUserInfo = null;// 拼接获取用户信息接口的请求地址String requestUrl = ProjectConst.GET_WEIXIN_USER_URL.replace("ACCESS_TOKEN", accessToken).replace("OPENID", openId);// 获取用户信息(返回的是Json格式内容)JSONObject jsonObject = WeiXinUtils.doGetStr(requestUrl);if (null != jsonObject) {try {//封装获取到的用户信息weixinUserInfo = new WeiXinUser();// 用户的标识weixinUserInfo.setOpenId(jsonObject.getString("openid"));// 昵称weixinUserInfo.setNickname(jsonObject.getString("nickname"));// 用户的性别(1是男性,2是女性,0是未知)weixinUserInfo.setSex(jsonObject.getInt("sex"));// 用户所在国家weixinUserInfo.setCountry(jsonObject.getString("country"));// 用户所在省份weixinUserInfo.setProvince(jsonObject.getString("province"));// 用户所在城市weixinUserInfo.setCity(jsonObject.getString("city"));// 用户头像weixinUserInfo.setHeadImgUrl(jsonObject.getString("headimgurl"));} catch (Exception e) {if (0 == weixinUserInfo.getSubscribe()) {System.out.println("用户并没有关注本公众号");} else {int errorCode = jsonObject.getInt("errcode");String errorMsg = jsonObject.getString("errmsg");System.out.println("由于"+errorCode +"错误码;错误信息为:"+errorMsg+";导致获取用户信息失败");}}}return weixinUserInfo;
}

WeiXinUtils


/**
* @desc 用户获取access_token,众号调用各接口时都需使用access_token
**/
public class WeiXinUtils {/*** Get请求,方便到一个url接口来获取结果** @param url* @return*/public static JSONObject doGetStr(String url) {DefaultHttpClient defaultHttpClient = new DefaultHttpClient();HttpGet httpGet = new HttpGet(url);JSONObject jsonObject = null;try {HttpResponse response = defaultHttpClient.execute(httpGet);HttpEntity entity = response.getEntity();if (entity != null) {String result = EntityUtils.toString(entity, "UTF-8");jsonObject = JSONObject.fromObject(result);}} catch (ClientProtocolException e) {e.printStackTrace();} catch (IOException e) {e.printStackTrace();}return jsonObject;}/*** 获取access_token* @return*/public static AccessToken getAccessToken(String projectAppId,String projectAppsecret){AccessToken accessToken = new AccessToken();String appid = projectAppId;//自己的配置APPSECRET;(公众号进行查阅)String appsecret = projectAppsecret;String url = ProjectConst.ACCESS_TOKEN_URL.replace("APPID" ,appid).replace("APPSECRET",appsecret);JSONObject jsonObject = doGetStr(url);if(jsonObject !=null){accessToken.setAccess_token(jsonObject.getString("access_token"));accessToken.setExpires_in(jsonObject.getLong("expires_in"));}return accessToken;}public static String authorizeUrl(String callbackUrl,String projectAppid){StringBuilder sb = new StringBuilder();try {sb.append("https://open.weixin.qq.com/connect/oauth2/authorize?appid=").append(projectAppid).append("&redirect_uri=").append(URLEncoder.encode(callbackUrl,"UTF-8")).append("&response_type=code").//append("&scope=snsapi_base ").append("&scope=snsapi_userinfo").append("&state=STATE").append("#wechat_redirect");} catch (UnsupportedEncodingException e) {e.printStackTrace();}return sb.toString();}
}

基本代码都再上面.

微信公众号测试号开发小结相关推荐

  1. 微信公众号Java开发-笔记02【开发接入准备、开发接入】

    学习视频网址:哔哩哔哩网站 微信公众号开发-Java版 [P01-P02]微信公众号Java开发-笔记01[微信公众号介绍.开发环境搭建] [P03-P04]微信公众号Java开发-笔记02[开发接入 ...

  2. 前端对接微信公众号网页开发流程,前期配置

    微信公众号网页开发,其实就是我们开发的h5网页需要放到微信浏览器环境中使用,但是需要对接公众号授权,授权之后可以获取到用户的个人信息,以及可以使用公众号提供的一些API,如:图片上传.图片预览.获取位 ...

  3. 使用神卓互联内网穿透实现微信公众号开发:介绍了如何通过神卓互联实现微信公众号的开发和调试。

    注册微信公众平台账号,并创建一个测试号. 1.在神卓互联控制台上,将公网IP地址和本地的80端口进行映射.同时,将80端口映射到本地开发2.环境的端口(如localhost:3000). 3.在本地开 ...

  4. 微信公众号测试号配置,群发消息(go实现)

    配置服务器(以本地服务器127.0.0.1:8080为例) ⑴新建项目(这里用的goframe框架) 路由: var (Main = gcmd.Command{Name: "main&quo ...

  5. 使用微信公众号测试号如何进行自定义菜单的设定

    问题描述 在进行微信公众号业务开发的时候,微信公众号的自定义菜单是非常重要的一环,然而在开发中我们是使用不到正式的公众号进行菜单栏的自定义的,都是通过我们个人申请的测试公众号来完成我们的功能开发和测试 ...

  6. ios系统web(微信公众号)开发遇到的问题及解决方案

    ios系统web(微信公众号)开发遇到的问题及解决方案 参考文章: (1)ios系统web(微信公众号)开发遇到的问题及解决方案 (2)https://www.cnblogs.com/clj2017/ ...

  7. Yii2.0实现微信公众号后台开发

    2019独角兽企业重金招聘Python工程师标准>>> 研读 微信公众平台开发者文档 ,然后再阅读本文,效果更佳! 接入微信 Yii2后台配置 1.在app/config/param ...

  8. 微信公众号Java开发-笔记01【微信公众号介绍、开发环境搭建】

    学习网址:哔哩哔哩网站 微信公众号开发-Java版 微信公众号Java开发-笔记01[微信公众号介绍.开发环境搭建] 微信公众号Java开发-笔记02[] 微信公众号Java开发-笔记03[] 微信公 ...

  9. 手把手教你最近很火的 微信公众号测试号推送消息

    最近有很多小伙伴在尝试做消息推送,今天详细教程它来啦!!! 过程不太复杂,跟着一步一步做就可以实现. 没时间的话,先收藏,等有时间了慢慢学! 第一步:注册微信公众号测试号 1.利用下面的链接注册一个微 ...

最新文章

  1. java gson使用_Java 中 Gson的使用
  2. IT人士有哪些保健建议
  3. 2021夏季每日一题 【week7 完结】
  4. NSString 用法总结与陷阱
  5. 前端利用JS导出数据到Excel表 数字是文本类型 无法计算
  6. 同源策略_如何支持跨域
  7. Linux系统【四】进程间通信-管道
  8. 关于推荐的一个算法工程师访谈,有一些内容值得看看
  9. 百度的一道 java 高频面试题的多种解法
  10. Java连接SAP ————JCO 3.0技术详解
  11. 在黑客攻击之前软件安全检测是重点,软件安全检测机构怎么找?
  12. 增广矩阵and线性方程组----线性代数
  13. React Antd4 CRA / Next.js / Vite 按需加载组件的 CSS / Less
  14. 深圳华睿彩印高温玻璃打印机介绍
  15. 记录:【DIY】一块SSD拯救了我的笔记本
  16. 计算机一个小键盘按不出来怎么办,电脑小键盘打不出字怎么办?
  17. Eclipse Neno.3 + Jrebel 7.0.9 图文在线安装
  18. 手机共享笔记本wifi热点
  19. Retrofit 大体框架
  20. BP神经网络能做什么?

热门文章

  1. 红黑二叉树原理和分析
  2. hostname修改
  3. 【操作系统⑥】——进程联系与临界区管理【同步与互斥 Dekker算法 TS指令 SWAP指令】
  4. 一文解决,IntelliJ IDEA 2018 关闭代码重复波浪线
  5. Python 正则表达式查找字符串中特定字符
  6. 如何从0开始开展UI自动化测试
  7. 单文件组件.vue 文件
  8. 微信小程序 上拉加载更多
  9. CDH安装phoenix
  10. 获取下一个工作日/休息日的方法 本地基础搭建 Springboot 法定节假日/休息日/调休日 均可 不用调三方