课程表

Spring Boot课程

工具箱
速查手册

Boot 微信公众号开发

当前位置:免费教程 » Java相关 » Spring Boot
注意:本页面内容为W3xue原创,未经授权禁止转载,违者必究!
来源:W3xue  发布时间:2020/9/22 16:42:11

微信公众号的开发是很常见的开发任务。如果大家用过其他语言开发,就可以很自然的转到Spring Boot的微信开发。这里给大家整理一条最便捷的方式,让大家迅速入门,避免踩坑。

微信开发官方文档的链接:

https://developers.weixin.qq.com/doc/

在开始微信开发的时候,官方文档可以随时作为查阅和参考资料。

本章节介绍的内容有:1、基本的微信端访问验证;2、公众号关注验证;3、自定义微信分享图标。由于本章节主要是基于上一章节的redis开发,所以除此之外,还大致介绍一下使用数据库进行微信开发的基本思路,具体的代码这里就省略了。


一、基本原理

微信公众号的开发原理是,首先,我们在页面程序上进行判断,有没有地址栏参数“code”,如果没有,我们就把相关参数带上,然后跳转到下面的这个地址获取code:

  1. https://open.weixin.qq.com/connect/oauth2/authorize?appid=【你的微信公众号开发AppId】&redirect_uri=【你的页面地址】&response_type=code&scope=snsapi_base&state=STATE#wechat_redirect

微信公众号平台的这个接口地址,会根据你传递的AppId,跳转到你给的页面地址,但是它附带了一个code参数,类似这样:

  1. https://www.w3xue.com/test?code=【微信给你的code】

这个code是干嘛的呢?我们可以用这个code,再加上 AppId 和AppSecret ,来取访问页面的该微信用户对于本公众号的唯一OpenID。这个OpenID 就是一个微信用户在一个公众号的身份ID,它在某一个微信公众号上具有唯一性,一个用户只对应一个OpenID。

然后,我们使用AppId 和 AppSecret 去获取 AccessToken,这里要注意,一个微信公众号获取其 AccessToken 的次数是有限制的(以前上限是2000次/天,现在政策可能变了,不同账号次数不同,但仍然有限制),因此,这是我们在开发过程中需要注意的一个重点。

有了AccessToken,再加上上一步获得的OpenID,我们的程序拿着它们,就可以发送给微信公众号平台,查询一个用户的是否关注等信息了。

另外,我们本章节还将学习如何给页面加上图标,就是用户在分享本页面后,在朋友圈或者聊天记录中显示的那个图标:

示意图.jpg

在上古时代,微信会获取一个页面中的第一个图片作为图标,现在不行了,需要自定义了。其基本的原理是,在页面引用微信官方的一个JS,然后自定义相关的图标和地址,并且最重要的是,要生成一个签名,这样才能自定义成功。

了解完这些基本原理后,就可以思考开发工作了。


二、准备工作

在正式开发之前,我们要确认公众号具有相关权限,并有正确的设置,然后新建相关的工具类,以便引入使用。

1、一个拥有开发权限的微信公众号,以及其开发用的 AppId 和 AppSecret。当然,还需要进行一些设置:

a.在“公众号设置->功能设置”设置“网页授权域名”和“JS接口安全域名”。把你的程序所在域名(二级域名就用二级域名)加入这2个名单备用,设置这2个可能需要管理员扫码。

b.设置IP白名单,在“开发配置->基本配置->IP白名单把你的服务器IP加进你的微信公众号IP白名单。

2、在utils文件夹新建2个类:HttpClientUtil类Encrypt类HttpClientUtil类用来处理相关与 http请求相关的业务。Encrypt类用来提供几个加密相关的函数。代码提供了下载链接,但具体就不贴出来了(如有红字,用Alt+Enter引入包即可):

HttpClientUtil类和Encrypt类下载

3、在utils文件夹下面新建一个Wechat类,用来向微信公众平台发送信息并获取相关信息:

  1. package com.w3xue.jiaocheng.utils;
  2.  
  3. import com.alibaba.fastjson.JSON;
  4. import com.alibaba.fastjson.JSONObject;
  5.  
  6. public class Wechat {
  7.     //获取微信用户授权信息
  8.  public static String getBaseInfo(String code, String appid, String secret)
  9.     {
  10.         return HttpClientUtil.getInstance().sendHttpPost("https://api.weixin.qq.com/sns/oauth2/access_token?appid=" + appid + "&secret=" + secret + "&code=" + code + "&grant_type=authorization_code", code);
  11.     }
  12.  
  13.     //无时间间隔的获取全局access_token,每天有次数限制,慎用!
  14.  public static String getAccessToken(String appid, String secret)
  15.     {
  16.         String sGetStr = HttpClientUtil.getInstance().sendHttpsGet("https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=" + appid + "&secret=" + secret);
  17.         JSONObject jsonResult = JSON.parseObject(sGetStr);
  18.         return jsonResult.getString("access_token");
  19.      }
  20.  
  21.     //无时间间隔的获取jsapi_ticket,有次数限制,慎用!
  22.  public static String getJsapiTicket(String access_token)
  23.     {
  24.         String sGetStr = HttpClientUtil.getInstance().sendHttpPost("https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token=" + access_token + "&type=jsapi", "");
  25.         JSONObject jsonResult = JSON.parseObject(sGetStr);
  26.         return jsonResult.getString("ticket");
  27.     }
  28.  
  29.     //获取用户常用信息,部分公众号没有此权限
  30.     public static String getUserSetInfo(String access_token, String openid)//string appid, string secret,
  31.     {
  32.         return HttpClientUtil.getInstance().sendHttpsGet("https://api.weixin.qq.com/sns/userinfo?access_token="+access_token+"&openid="+openid+"&lang=zh_CN");
  33.     }
  34.  
  35.     //获取用户综合信息
  36.      public static String getUserInfo(String access_token, String openid)//string appid, string secret,
  37.      {
  38.         return HttpClientUtil.getInstance().sendHttpsGet("https://api.weixin.qq.com/cgi-bin/user/info?access_token=" + access_token + "&openid=" + openid+"&lang=zh_CN");//
  39.      }
  40.  
  41.     //在获取用户综合信息的基础上获取关注标识(1:已关注 0:未关注)
  42.      public static String getSubscribe(String access_token, String openid) //string appid, string secret,
  43.      {
  44.         String sGetStr = getUserInfo(access_token,  openid);
  45.         JSONObject jsonResult = JSON.parseObject(sGetStr);
  46.         return jsonResult.getString("subscribe");
  47.      }
  48.  
  49. }

这里的方法基本机构我已经标出来了,基本的微信开发需要的方法都在这里。

4、在MainController类中添加如下方法(需要引用 com.w3xue.jiaocheng.utils.HttpClientUtil类):

  1. final String strAppid="wx8bda2539bda48d1a";
  2. final String strAppSecret="506044e1986e2576b0ef4de3f8e3ceea";
  3. @Autowired
  4. StringRedisTemplate redisTemplate;//引入redis
  5. @RequestMapping(value = "/getToken2Ticket")
  6. public String getToken2Ticket(String key) {
  7.     String reStr = null;
  8.     if (redisTemplate.hasKey(key)) {
  9.         reStr = redisTemplate.opsForValue().get(key);
  10.     } else {
  11.         if (key.equals("ticketKey")) {
  12.             String tmpToken=getToken2Ticket("cgi-token");
  13.             String sGetStr = HttpClientUtil.getInstance().sendHttpPost("https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token=" + tmpToken + "&type=jsapi", "");
  14.             JSONObject result2 = JSON.parseObject(sGetStr);
  15.             if (result2.containsKey("ticket")) {
  16.                 reStr = result2.getString("ticket");
  17.                 redisTemplate.opsForValue().set(key, reStr);
  18.                 redisTemplate.expire(key, 2000, TimeUnit.SECONDS);
  19.             }
  20.         }
  21.         else {
  22.             String sGetStr = HttpClientUtil.getInstance().sendHttpsGet("https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=" + strAppid + "&secret=" + strAppSecret);
  23.             JSONObject result2 = JSON.parseObject(sGetStr);
  24.             if (result2.containsKey("access_token")) {
  25.                 reStr = result2.getString("access_token");
  26.                 redisTemplate.opsForValue().set(key, reStr);
  27.                 redisTemplate.expire(key, 2000, TimeUnit.SECONDS);
  28.             }
  29.         }
  30.     }
  31.     return reStr;
  32. }

因为上一节中我们已经介绍了如何添加StringRedisTemplate对象,这里就不多解释了。

这个方法的作用,是把AccessToken和Ticket的值,存在Redis里面,时间间隔设置为2000秒。这样,当这个值过期之后,该方法就会调用微信服务器的接口获取数据,并将其保存在Redis中。因为代码很简单,这里就不再详细说明。

5、添加模板文件wechat.html。代码如下:

  1. <!DOCTYPE html>
  2. <html>
  3. <head>
  4.     <meta charset="utf-8">
  5.     <title>首页 - w3xue教程,Spring Boot微信开发</title>
  6.     <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no, minimal-ui">
  7.     <script src="https://cdn.bootcss.com/jquery/2.1.2/jquery.js"></script>
  8. </head>
  9.  
  10. <body>
  11.  
  12. <p th:utext="${openid}"></p>
  13. <p th:utext="${token}"></p>
  14. <p th:utext="${guanzhu}"></p>
  15.  
  16. </body>
  17. </html>


三、获取OpenID和关注信息

在做好上一节的基本工作后,我们就可以获取一个用户在某个公众号的OpenID了。之前说过,OpenID就是一个微信账号在某个微信公众号的唯一标识码,它是不会重复的,因此可以用在程序逻辑上,作为身份验证的工具。此外,获取AccessToken后,将OpenID、AccessToken一起发送到微信接口,就可以取得该用户的关注信息(即其是否关注了本公众号)。

有了这些理论基础,我就不再多BB了,直接上代码,一会来解释某些细节:

  1. @RequestMapping("/wechat")
  2. public ModelAndView wechat(ModelAndView mv, @RequestParam(value="code",defaultValue = "",required = false) String pCode, @RequestParam(value="from",defaultValue = "",required = false) String from, @RequestParam(value="isappinstalled",defaultValue = "",required = false) String isappinstalled, @RequestParam(value="tdsourcetag",defaultValue = "",required = false) String tdsourcetag,HttpSession session) {
  3.  
  4.         //微信相关开始
  5.         StringBuilder sbUrl=new StringBuilder();
  6.         if (!(tdsourcetag+"dsa").equals("dsa")) //tdsourcetag为360浏览器常见的自带参数,此处判断其若不为空
  7.             sbUrl.append("?tdsourcetag="+tdsourcetag);
  8.         else { //from、isappinstalled都是微信端常见的自添加参数,这是微信为了统计网页来源添加的,但会影响程序图标的使用
  9.             if (!(from + "dsa").equals("dsa") && (isappinstalled + "dsa").equals("dsa")) //from不为空,isappinstalled为空
  10.                 sbUrl.append("?from=" + from);
  11.             else if (!(from + "dsa").equals("dsa") && !(isappinstalled + "dsa").equals("dsa")) //都不为空
  12.                 sbUrl.append("?from=" + from + "&isappinstalled=" + isappinstalled);
  13.             else if ((from + "dsa").equals("dsa") && !(isappinstalled + "dsa").equals("dsa"))
  14.                 sbUrl.append("?isappinstalled=" + isappinstalled);
  15.         }
  16.         final String strUrl="http://www.xxx.com/jiaocheng/wechat/"+sbUrl.toString();  //请把引号里的内容替换成你服务器上本页面的网址,注意路径后面带斜杠
  17.  
  18.         String OpenID="";
  19.         try { //OpenID不为空
  20.             OpenID = session.getAttribute("openid").toString();
  21.         }
  22.         catch (Exception e)
  23.         { //OpenID为空
  24.             if((pCode+"dsa").equals("dsa")) {//code为空
  25.                 return new ModelAndView("redirect:https://open.weixin.qq.com/connect/oauth2/authorize?appid=" + strAppid + "&redirect_uri=" + strUrl + "&response_type=code&scope=snsapi_base&state=STATE#wechat_redirect");
  26.             }
  27.             else
  28.             { //code不为空
  29.                 String sBaseInfo = Wechat.getBaseInfo(pCode, strAppid, strAppSecret);
  30.                 JSONObject jsonResult = JSON.parseObject(sBaseInfo);
  31.                 OpenID = jsonResult.getString("openid");
  32.                 session.setAttribute("openid",OpenID);
  33.                 return new ModelAndView("redirect:/wechat/"); //注意路径后面带斜杠
  34.             }
  35.         }
  36.         String sAccessToken=getToken2Ticket("cgi-token"); //如果这个方法放在其他类里面,有可能因为自动装配不能使用
  37.         session.setAttribute("token",sAccessToken);
  38.         String guanzhu=Wechat.getSubscribe(sAccessToken,OpenID); //取关注信息
  39.  
  40.         mv.addObject("openid", OpenID);
  41.         mv.addObject("token", sAccessToken);
  42.         mv.addObject("guanzhu", guanzhu);
  43.         //微信相关结束
  44.  
  45.         mv.setViewName("wechat");
  46.         return mv;
  47. }

如果你关注了该公众号,guanzhu这个变量的值就是1,否则就是0。这里分别打印了用户的openid、用户获取的accessToken、还有用户关注信息。使用二维码生成工具,把该路由地址生成二维码,用微信扫描后,如果一切正常,会出现类似如下的内容:

  1. oWaL9jek7hDlGSei-PlU75jBT1mE
  2.  
  3. 18_Jzbf1VC_L6Jx0F84ERWEknaryTp3cg_loap8btVO-rTq0R45cEKSf4Lj8NzTchS4y_L9E6LsAygaLETEkon5OgRYl9EJP0GyZPBApJ_q2-5A1-mh4JU-c_M1gAxOFfLnEM6mcz4-NLfTyySZCRcAAAEHW
  4.  
  5. 1

四、自定义微信分享图标

如果我们点击微信右上角的分享图标会发现,分享出去的图标,是很丑的别针图标,这是微信通用的链接图标。我们如何更改呢?

首先,我们在的<head>标签内引用微信官方的JS库:

  1. <script src="http://res.wx.qq.com/open/js/jweixin-1.2.0.js"></script>

然后,我们在刚才的引用代码下面添加如下代码:

  1. <script th:inline="javascript">
  2.     var shareImgUrl = "http://替换成你服务器上的绝对图片地址.png";  //注意必须是绝对路径
  3.     var shareLink = "http://www.xxx.com/jiaocheng/wechat/";   //同样,必须是绝对路径,替换成你服务器上的分享出去的地址,注意网址后面带斜杠
  4.     var shareDes = 'w3xue教程,让人人享有平等学习机会。'; //分享给朋友或朋友圈时的文字简介
  5.     var shareTitle = document.title;  //分享title
  6.     var appid = ''; //apiID,可留空
  7.     wx.config({
  8.         debug: false,
  9.         appId:/*[[${appid}]]*/ null,
  10.         timestamp: 1553071742,  /*生成签名的时间戳 */
  11.         nonceStr: 'W3FlX1tOkHGP9zga',  /*生成签名的随机串 */
  12.         signature: /*[[${qianming}]]*/ null,
  13.         jsApiList: [ /**/
  14.             'checkJsApi',
  15.             'onMenuShareTimeline',
  16.             'onMenuShareAppMessage',
  17.             'onMenuShareQQ',
  18.             'onMenuShareWeibo',
  19.             'onMenuShareQZone'
  20.         ]
  21.     });
  22.  
  23.     wx.ready(function(){
  24.         wx.onMenuShareTimeline({
  25.             title:shareTitle, // 分享标题
  26.             link: shareLink, // 分享链接,该链接域名或路径必须与当前页面对应的公众号JS安全域名一致
  27.             imgUrl: shareImgUrl, // 分享图标
  28.             success: function () {
  29.                 // 用户确认分享后执行的回调函数
  30.             },
  31.             cancel: function () {
  32.                 // 用户取消分享后执行的回调函数
  33.             }
  34.         });
  35.  
  36.         wx.onMenuShareAppMessage({
  37.             title:shareTitle, // 分享标题
  38.             desc: shareDes, // 分享描述
  39.             link: shareLink, // 分享链接,该链接域名或路径必须与当前页面对应的公众号JS安全域名一致
  40.             imgUrl:shareImgUrl, // 分享图标
  41.             type: '', // 分享类型,music、video或link,不填默认为link
  42.             dataUrl: '', // 如果type是music或video,则要提供数据链接,默认为空
  43.             success: function () {
  44.                 // 用户确认分享后执行的回调函数
  45.             },
  46.             cancel: function () {
  47.                 // 用户取消分享后执行的回调函数
  48.             }
  49.         });
  50.     });
  51.  
  52.     wx.error(function(res){
  53.         alert('分享出错!'); //错误提示
  54.     });
  55.  
  56. </script>

wx.onMenuShareTimeline方法是分享到朋友圈的自定义方法,wx.onMenuShareAppMessage是分享给微信朋友的自定义方法。

这里需要注意,这段JS代码用了 th:inline 标签,里面出现了2个变量,一个是appid,另一个是qianming。appid只需要将我们的开发appid输入即可。但qianming变量,则是需要一套加密方法进行加密的。需要首先取得ticket信息,在之前我们已经在MainController类中添加了getToken2Ticket方法,这里直接取就可以了。取得ticket后,要对信息进行排列,然后使用Encrypt类里面的方法进行SHA1加密,具体请见如下代码:

  1. //微信分享图标开始
  2. String sTicket =getToken2Ticket("ticketKey");   //取得ticket
  3. String sTimestamp = "1553071742";  //时间戳,跟前端页面保持一致
  4. String sNonceStr = "W3FlX1tOkHGP9zga";  //生成签名的随机串,跟前端页面保持一致
  5. String string1 = "jsapi_ticket=" + sTicket + "&noncestr=" + sNonceStr + "&timestamp=" + sTimestamp + "&url=" + strUrl; //排列字符串
  6. mv.addObject("appid", strAppid);
  7. String sQianming=Encrypt.makeSHA1(string1).toUpperCase(); // 进行SHA1加密
  8. mv.addObject("qianming", sQianming); //绑定签名
  9. //微信分享图标结束

把这段代码加在wechat路由方法底下。这样,我们打包上传到服务器,然后再分享给朋友,或者分享到朋友圈,就会出现你的自定义图标了。会出现如下图的效果:

1.png


注意:本页面内容为W3xue原创,未经授权禁止转载,违者必究!
来源:W3xue  发布时间:2020/9/22 16:42:11
 友情链接:直通硅谷  点职佳  北美留学生论坛

本站QQ群:前端 618073944 | Java 606181507 | Python 626812652 | C/C++ 612253063 | 微信 634508462 | 苹果 692586424 | C#/.net 182808419 | PHP 305140648 | 运维 608723728

W3xue 的所有内容仅供测试,对任何法律问题及风险不承担任何责任。通过使用本站内容随之而来的风险与本站无关。
关于我们  |  意见建议  |  捐助我们  |  报错有奖  |  广告合作、友情链接(目前9元/月)请联系QQ:27243702 沸活量
皖ICP备17017327号-2 皖公网安备34020702000426号