微信开发-初级接入微信公众平台MP

来源:互联网 发布:java清除缓存代码 编辑:程序博客网 时间:2024/05/01 11:02

微信公众平台,简称weixinMP, 微信公众平台发布以前叫媒体平台,提供给合作方与用户互动,MP是media platform的简写。

说难也难,说容易也容易,看微信接入文档,会让人一头雾水,蒙逼的感觉,因为官方文档都是晦涩难懂的,显的逼格很高,下面用普通语言走一遍,让我们开始微信接入之旅吧。

1.   首先,微信服务器使用的是必需是80端口,而我们常常使用的是tomcat是8080端口,当然我们可以修改端口,而本机的80端口被浏览器占用,是不可能把tomcat改在80端口的,而且微信服务器会向我们自己的服务器以验证请求,需要内网穿透,所以大家可能看看我写的ngrok内网穿透的文章:微信开发-ngrok内网穿透部署 

2.   开发者未必有自己的微信公众号,微信官方考虑好这一点,所以提供了微信测试号,在测试号上有些功能限制,但接入以及走完部分流程是没问题的,首先当然是申请测试号了,百度“微信   测试号”或打开https://mp.weixin.qq.com/debug/cgi-bin/sandbox?t=sandbox/login


用我信自己的微信号扫一扫后在微信界面点确认登录。


其中appID,appsecret是我们微信公众号接入的关键信息。





其中下方需要我们配置自己服务器的URl和JS接口安全域名。那URL填写注意:

1.   必需是80端口

2.   是自己的服务器的地址,其中该地址是微信服务器向我们自己服务器发送的数据统一入口,每次微信服务器给我们服务器推送消息时都推送到该url地址,根据消息类型的事件类型选择不同的handler去处理不同推送消息。稍后会写个统一入口,这里暂不写

js域名是我们服务器域名,注意不带http://等协议头。授权回调域名配置规范为全域名,意思是,比如我现在填chenyuanx.tunnel.2bdata.com,那么该域名下的所以请求都可以进行OAuth2.0授权。

3.   写个自己的服务器接收微信服务器推送消息的统一入口。

代码如下:

/** * 微信公众号webservice主服务接口,提供与微信服务器的信息交互 *  * @param request * @param response * @throws Exception */@RequestMapping(value = "protal")public void wechatCore(HttpServletRequest request, HttpServletResponse response) throws Exception {response.setContentType("text/html;charset=utf-8");response.setStatus(HttpServletResponse.SC_OK);String signature = request.getParameter("signature");String nonce = request.getParameter("nonce");String timestamp = request.getParameter("timestamp");if (!wxMpService.checkSignature(timestamp, nonce, signature)) {// 消息签名不正确,说明不是公众平台发过来的消息response.getWriter().println("非法请求");return;}String echoStr = request.getParameter("echostr");if (StringUtils.isNotBlank(echoStr)) {// 说明是一个仅仅用来验证的请求,回显echostrString echoStrOut = String.copyValueOf(echoStr.toCharArray());response.getWriter().println(echoStrOut);return;}String encryptType = StringUtils.isBlank(request.getParameter("encrypt_type")) ? "raw": request.getParameter("encrypt_type");if ("raw".equals(encryptType)) {// 明文传输的消息WxMpXmlMessage inMessage = WxMpXmlMessage.fromXml(request.getInputStream());// 微信消息路由 把相关类型的消息交给对应的handler去处理WxMpXmlOutMessage outMessage = this.weixinService.route(inMessage);if (null != outMessage) {response.getWriter().write(outMessage.toXml());}return;}if ("aes".equals(encryptType)) {// 是aes加密的消息String msgSignature = request.getParameter("msg_signature");WxMpXmlMessage inMessage = WxMpXmlMessage.fromEncryptedXml(request.getInputStream(), configStorage,timestamp, nonce, msgSignature);this.logger.debug("\n消息解密后内容为:\n{} ", inMessage.toString());WxMpXmlOutMessage outMessage = this.weixinService.route(inMessage);this.logger.info(response.toString());response.getWriter().write(outMessage.toEncryptedXml(configStorage));return;}response.getWriter().println("不可识别的加密类型");return;}

当我们填写好url和token(开发者自己随机写)点提交时,微信服务器就以get方式推送消息到填写的url上去,携带

参数

描述

signature

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

timestamp

时间戳

nonce

随机数

echostr

随机字符串

 

加密/校验流程如下:

1. tokentimestampnonce三个参数进行字典序排序

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

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

若确认此次GET请求来自微信服务器,请原样返回echostr参数内容,则接入生效,成为开发者成功,否则接入失败。

我们不用自己去加密然后比较,有个weixin-java-mp.jar包提供了wxMpService.checkSignature(timestamp,nonce, signature)方法,只要传入参数就帮我们做好验证,以后还会介绍该jar包,它包装了很好url及方法供开发者使用。成功返回echostr则网页会提示“配置成功”,至此服务器配置成功,该url是核心接口,是接收微信服务器推送消息的总控。

1.   获取用户信息。

有2种access_token,一种是使用AppID和AppSecret获取的access_token,常常用在关注公众号,一种是OAuth2.0授权中产生的access_token,常用在打开页面需要用户点击授权时

4.1.全局票据access_token。

 access_token出现的原因是什么呢?appId和appSecret是定位我们公众号的2个关键数据,其实appid已经可以唯一确定一个公众号,但为了安全考虑加个appsecret,用于验证公众号相关权限信息。如果每次都用2个参数唯一定位公众号不仅麻烦,而且也不安全,容易将消息暴露在公开环境,故微信出于安全及方便考虑,让开发者用appid和appsecret去拿access_token,即用access_token就可定位一个公众号,不仅安全,而且方便,当然也可以有其它的深意,这里不做深入研究。获得access_token的方式:

用get方式请求:

https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=myappid&secret=mysappsecret

ps:微信相关的请求都是https而不是http,目的是为了安全)grant_type固定是client_credential,appid和appsecret是我们自己的消息。

比如我的测试号的请求返回的结果:


在jar里可以使用WxMpService.getAccessToken()方法获得,

源码:

@Override  public String getAccessToken(boolean forceRefresh) throws WxErrorException {//获得锁    Lock lock = this.wxMpConfigStorage.getAccessTokenLock();    try {      lock.lock();//如果是强制刷新则强制将access token过期掉      if (forceRefresh) {        this.wxMpConfigStorage.expireAccessToken();      }//如果accesstoken已经过期,则重新请求并保存到wxMpconfigStorage,否则则从//wxMpConfigStorage里取      if (this.wxMpConfigStorage.isAccessTokenExpired()) {        String url = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential" +          "&appid=" + this.wxMpConfigStorage.getAppId() + "&secret="          + this.wxMpConfigStorage.getSecret();        try {          HttpGet httpGet = new HttpGet(url);          if (this.httpProxy != null) {            RequestConfig config = RequestConfig.custom().setProxy(this.httpProxy).build();            httpGet.setConfig(config);          }          try (CloseableHttpResponse response = getHttpclient().execute(httpGet)) {            String resultContent = new BasicResponseHandler().handleResponse(response);            WxError error = WxError.fromJson(resultContent);            if (error.getErrorCode() != 0) {              throw new WxErrorException(error);            }            WxAccessToken accessToken = WxAccessToken.fromJson(resultContent);            this.wxMpConfigStorage.updateAccessToken(accessToken.getAccessToken(),              accessToken.getExpiresIn());          } finally {            httpGet.releaseConnection();          }        } catch (IOException e) {          throw new RuntimeException(e);        }      }    } finally {      lock.unlock();    }    return this.wxMpConfigStorage.getAccessToken();  }

已经帮我们封装了很多方法,不用我们亲自去写url以及亲自使用httpClient去请求,如果缓存里有accessToken且没过期,否则刷新请求新的accessToken.(测试号一天可以请求2000)。

拿到accessToken就代表拿到了公众号,就可以拿到用户信息。

获得用户基本信息(包括昵称、头像、性别、所在城市、语言和关注时间。注意是拿不到用户的微信名的(openId只能算是用户标识符并不是用户名),只提供基本信息):

http请求方式:

GET https://api.weixin.qq.com/cgi-bin/user/info?access_token=ACCESS_TOKEN&openid=OPENID&lang=zh_CN

参数解释:access_token代表公众号,openid代表用户。lang代表语言,默认中文zh_CN。解释一下为什么要传递公众号,按正常理解传递用户号即可以拿到基本信息,和关注的公众号没关系,还是那句话,安全,安全,安全。微信服务器为了限制和管理公众号以及安全考虑,是不会无限制的提供服务的。



上图即是获得的用户信息。weixin-java-mp.jar提供了WxMpUserService.userinfo(openid,lang);获得用户基本信息。注意并没有传入access_token,因为access_token交给框架去管理生命周期,当缓存没有或已经过期再请求,如果存在则直接返回,还是那句话,微信服务器并发量很多,出支效率和安全考虑会限制token的请求次数。

1.2.       授权access_token。

当通过OAuth2.0方式弹出需要网页授权页面想获得用户基本信息时才需要用到该token,比如打开朋友圈分享的链接,该页面需要获得微信用户基本信息时使用,因为这时候用户并不有关注该公众号。

我还需要配置回调安全域名,否则会报出错了或未授权等错误提示:



找开微信 公众号后台管理,找到网页授权获取用户基本信息,如下图


点修改,添加安全域名,我的是


点确认,再弹出授权页面时就不会报相关错误了。

一般常见的OAuth2.0弹出授权页面出下:


 第一步:用户同意授权,获取code

https://open.weixin.qq.com/connect/oauth2/authorize?appid=APPID&redirect_uri=REDIRECT_URI&response_type=code&scope=SCOPE&state=STATE#wechat_redirect

appid:公众号唯一标识号,redirect_uri就是当用户点“确认登录”时回到的链接,response_type固定写code,scope有2种,snsapi_base (不弹出授权页面,直接跳转,只能获取用户openid),snsapi_userinfo (弹出授权页面,可通过openid拿到昵称、性别、所在地。并且,即使在未关注的情况下,只要用户授权,也能获取其信息),state值随便写。

当用户确认登录后,微信服务器会将包括code值的参数传到上面redirect_uri的地址上,如下:redirect_uri/?code=CODE&state=STATE。当然若用户禁止授权,则重定向后不会带上code参数,仅会带上state参数redirect_uri?state=STATE。

注意参数顺序必需一致。

比如我的请求:https://open.weixin.qq.com/connect/oauth2/authorize?appid=wxc5b2995ebfba4cf1& redirect_uri= http://chenyuanx.tunnel.2bdata.com /wx/weixin/home&response_type=code&scope=snsapi_userinfo &state=”11”#wechat_redirect.

回调打开链接地址:

http://chenyuanx.tunnel.2bdata.com /wx/weixin/home?code=061oz8Kb1DlFvt0eOcLb1piRJb1oz8Kg& state=”11”

 

第二步:通过code换取网页授权access_token

当后台通过回调地址获取code后,请求以下链接获取access_token:

https://api.weixin.qq.com/sns/oauth2/access_token?appid=APPID&secret=SECRET&code=CODE&grant_type=authorization_code

注意这里需要填appsecret

第三步:拉取用户信息(需scope为 snsapi_userinfo)

http:GET(请使用https协议)

https://api.weixin.qq.com/sns/userinfo?access_token=ACCESS_TOKEN&openid=OPENID&lang=zh_CN

注意请求的地址和全局access_token请求地址是不一样的,

https://api.weixin.qq.com/cgi-bin/user/info。

 

这些请求如果使用sdk是不用自己写的。

第二步可以用:wxMpserver. oauth2getAccessToken(cdoe).

看看源码:

 

@Override public WxMpOAuth2AccessToken oauth2getAccessToken(Stringcode) throws WxErrorException {   StringBuilder url = new StringBuilder();   url.append("https://api.weixin.qq.com/sns/oauth2/access_token?");   url.append("appid=").append(this.wxMpConfigStorage.getAppId());   url.append("&secret=").append(this.wxMpConfigStorage.getSecret());   url.append("&code=").append(code);   url.append("&grant_type=authorization_code");    return this.getOAuth2AccessToken(url);  }

第三步可以用:wxMpServer.oauth2getUserInfo(WxMpOAuth2AccessToken oAuth2AccessToken, String lang);

源码如下:

@Override public WxMpUser oauth2getUserInfo(WxMpOAuth2AccessTokenoAuth2AccessToken, String lang) throws WxErrorException {   StringBuilder url = new StringBuilder();   url.append("https://api.weixin.qq.com/sns/userinfo?");   url.append("access_token=").append(oAuth2AccessToken.getAccessToken());   url.append("&openid=").append(oAuth2AccessToken.getOpenId());   if (lang == null) {     url.append("&lang=zh_CN");   } else {     url.append("&lang=").append(lang);   }    try {     RequestExecutor<String, String> executor = new SimpleGetRequestExecutor();     String responseText = executor.execute(getHttpclient(), this.httpProxy, url.toString(), null);     return WxMpUser.fromJson(responseText);   } catch (IOException e) {     throw new RuntimeException(e);   }  }


至此初步接入微信成功了。

第一, 向微信服务器配置了统一的推送消息入口。一切的推送消息都从该入口进去

第二, 填写了正确的JS回调安全域名,是不带http://等协议头的

第三, 拿到用户基本信息。分2种,一种是当我们关注了公众号,公众号即有权拿到用户信息,另一个是Oauth2.0方式的授权页面获得用户基本信息。

 

5.公众号自定义菜单生成。

微信公众号自定义菜单规则:

自定义菜单最多包括3个一级菜单,每个一级菜单最多包含5个二级菜单。

一级菜单最多4个汉字,二级菜单最多7个汉字,多出来的部分将会以“...”代替。

比如我的测试号:


那这么些自定义菜单是如何生成的呢,代码如下Main.java:

/** * 自定义菜单 *  * @author lilw * */public class MenuConfig {private static final String wx_appid = "wxc5b2995ebfba4cXXXX";private static final String wx_appsecret = "61ef8021ff4d1376c096ade1a0XXXXX";private static final String wx_token = "20170502aaAAXXXX";public static final String prefix_url = "http://chenyuanxXXXXXX.tunnel.2bdata.com";protected static WxMenu getMenu() {WxMenu menu = new WxMenu();WxMenuButton button1 = new WxMenuButton();button1.setName("我要洗衣");button1.setType(WxConsts.BUTTON_VIEW);button1.setUrl(mainConfig.wxMpService().oauth2buildAuthorizationUrl(prefix_url + "/wx/weixin/home", "snsapi_userinfo", "123"));WxMenuButton button2 = new WxMenuButton();button2.setName("优惠活动");WxMenuButton button21 = new WxMenuButton();button21.setType(WxConsts.BUTTON_VIEW);button21.setName("分享有礼");button21.setUrl(prefix_url + "/mortgage/weixin/loan/needLoan");WxMenuButton button22 = new WxMenuButton();button22.setType(WxConsts.BUTTON_VIEW);button22.setName("我在抵扣券");button22.setUrl(prefix_url + "/mortgage/weixin/member/toLogin");button2.getSubButtons().add(button21);button2.getSubButtons().add(button22);WxMenuButton button3 = new WxMenuButton();button3.setName("我的");WxMenuButton button31 = new WxMenuButton();button31.setType(WxConsts.BUTTON_VIEW);button31.setName("我的订单");button31.setUrl(prefix_url + "/wx/weixin/myOrd");WxMenuButton button32 = new WxMenuButton();button32.setType(WxConsts.BUTTON_VIEW);button32.setName("我的帐户");button32.setUrl(prefix_url + "/mortgage/weixin/introduce");WxMenuButton button33 = new WxMenuButton();button33.setType(WxConsts.BUTTON_VIEW);button33.setName("联系我们");button33.setUrl(prefix_url + "/wx/page/joinUs.html");WxMenuButton button34 = new WxMenuButton();button34.setType(WxConsts.BUTTON_VIEW);button34.setName("司机管理");button34.setUrl("http://xd.sdaishu.com:6868/index.php?m=user&a=login");WxMenuButton button35 = new WxMenuButton();button35.setType(WxConsts.BUTTON_VIEW);button35.setName("帮助与投诉");button35.setUrl(prefix_url + "/mortgage/weixin/contactme");button3.getSubButtons().add(button31);button3.getSubButtons().add(button32);button3.getSubButtons().add(button33);button3.getSubButtons().add(button34);button3.getSubButtons().add(button35);menu.getButtons().add(button1);menu.getButtons().add(button2);menu.getButtons().add(button3);return menu;}private static MainConfig mainConfig;public static void main(String[] args) {mainConfig = new MainConfig(wx_appid, wx_appsecret, wx_token);WxMpService wxMpService = mainConfig.wxMpService(); // 获取微信创建订单的servicetry {wxMpService.getMenuService().menuCreate(getMenu());System.out.println("success");} catch (WxErrorException e) {e.printStackTrace();}}}

MainConfig.java:

/** * 微信的主要配置信息   由wx.properties注入 * */@Configurationpublic class MainConfig {    @Value("${wx_appid}")    public String appid;    @Value("${wx_appsecret}")    public String appsecret;    @Value("${wx_token}")    public String token;    public static String  prefix_url;    /**     * 如果出现 org.springframework.beans.BeanInstantiationException     * https://github.com/Wechat-Group/weixin-java-tools-springmvc/issues/7     * 请添加以下默认无参构造函数     */     protected MainConfig(){}        /**     * 为了生成自定义菜单使用的构造函数,其他情况Spring框架可以直接注入     *     * @param appid     * @param appsecret     * @param token     * @param aesKey     */    protected MainConfig(String appid, String appsecret, String token) {        this.appid = appid;        this.appsecret = appsecret;        this.token = token;    }    @Bean    public WxMpConfigStorage wxMpConfigStorage() {        WxMpInMemoryConfigStorage configStorage = new WxMpInMemoryConfigStorage();        configStorage.setAppId(this.appid);        configStorage.setSecret(this.appsecret);        configStorage.setToken(this.token);        return configStorage;    }    @Bean    public WxMpService wxMpService() {        WxMpService wxMpService = new WxMpServiceImpl();        wxMpService.setWxMpConfigStorage(wxMpConfigStorage());        return wxMpService;    }}

执行main方法返回结果:

[URL]: https://api.weixin.qq.com/cgi-bin/menu/create[PARAMS]:{"button":[{"type":"view","name":"我要洗衣","url":"https://open.weixin.qq.com/connect/oauth2/authorize?appid=wxc5b2995ebfba4cf1&redirect_uri=http%3A%2F%2Fchenyuanx.tunnel.2bdata.com%2Fwx%2Fweixin%2Fhome&response_type=code&scope=snsapi_userinfo&state=123#wechat_redirect"},{"name":"优惠活动","sub_button":[{"type":"view","name":"分享有礼","url":"http://chenyuanx.tunnel.2bdata.com/mortgage/weixin/loan/needLoan"},{"type":"view","name":"我在抵扣券","url":"http://chenyuanx.tunnel.2bdata.com/mortgage/weixin/member/toLogin"}]},{"name":"我的","sub_button":[{"type":"view","name":"我的订单","url":"http://chenyuanx.tunnel.2bdata.com/wx/weixin/myOrd"},{"type":"view","name":"我的帐户","url":"http://chenyuanx.tunnel.2bdata.com/mortgage/weixin/introduce"},{"type":"view","name":"联系我们","url":"http://chenyuanx.tunnel.2bdata.com/wx/page/joinUs.html"},{"type":"view","name":"司机管理","url":"http://xd.sdaishu.com:6868/index.php?m=user&a=login"},{"type":"view","name":"帮助与投诉","url":"http://chenyuanx.tunnel.2bdata.com/mortgage/weixin/contactme"}]}]}[RESPONSE]:{"errcode":0,"errmsg":"ok"}15:42:01.473 [main] DEBUGme.chanjar.weixin.mp.api.impl.WxMpMenuServiceImpl - 创建菜单:{"button":[{"type":"view","name":"我要洗衣","url":"https://open.weixin.qq.com/connect/oauth2/authorize?appid=wxc5b2995ebfba4cf1&redirect_uri=http%3A%2F%2Fchenyuanx.tunnel.2bdata.com%2Fwx%2Fweixin%2Fhome&response_type=code&scope=snsapi_userinfo&state=123#wechat_redirect"},{"name":"优惠活动","sub_button":[{"type":"view","name":"分享有礼","url":"http://chenyuanx.tunnel.2bdata.com/mortgage/weixin/loan/needLoan"},{"type":"view","name":"我在抵扣券","url":"http://chenyuanx.tunnel.2bdata.com/mortgage/weixin/member/toLogin"}]},{"name":"我的","sub_button":[{"type":"view","name":"我的订单","url":"http://chenyuanx.tunnel.2bdata.com/wx/weixin/myOrd"},{"type":"view","name":"我的帐户","url":"http://chenyuanx.tunnel.2bdata.com/mortgage/weixin/introduce"},{"type":"view","name":"联系我们","url":"http://chenyuanx.tunnel.2bdata.com/wx/page/joinUs.html"},{"type":"view","name":"司机管理","url":"http://xd.sdaishu.com:6868/index.php?m=user&a=login"},{"type":"view","name":"帮助与投诉","url":"http://chenyuanx.tunnel.2bdata.com/mortgage/weixin/contactme"}]}]},结果:{"errcode":0,"errmsg":"ok"}success


说明生成菜单成功,可以取消公众号关注再关注即可看到最新结果。

自定义菜单官方API:https://mp.weixin.qq.com/wiki/10/0234e39a2025342c17a7d23595c6b40a.html

 

微信接入已经成功,当然这只是一小部分,还有很多功能需要开发,不过这只是相关API 的事了,至少,我们找到了入口。下一篇会详细介绍weixin-java-mp.jar工具包。



2 1
原创粉丝点击