Boot 微信公众号开发
注意:本页面内容为W3xue原创,未经授权禁止转载,违者必究!
来源:W3xue 发布时间:2020/9/22 16:42:11
微信公众号的开发是很常见的开发任务。如果大家用过其他语言开发,就可以很自然的转到Spring Boot的微信开发。这里给大家整理一条最便捷的方式,让大家迅速入门,避免踩坑。
微信开发官方文档的链接:
https://developers.weixin.qq.com/doc/
在开始微信开发的时候,官方文档可以随时作为查阅和参考资料。
本章节介绍的内容有:1、基本的微信端访问验证;2、公众号关注验证;3、自定义微信分享图标。由于本章节主要是基于上一章节的redis开发,所以除此之外,还大致介绍一下使用数据库进行微信开发的基本思路,具体的代码这里就省略了。
一、基本原理
微信公众号的开发原理是,首先,我们在页面程序上进行判断,有没有地址栏参数“code”,如果没有,我们就把相关参数带上,然后跳转到下面的这个地址获取code:
- https://open.weixin.qq.com/connect/oauth2/authorize?appid=【你的微信公众号开发AppId】&redirect_uri=【你的页面地址】&response_type=code&scope=snsapi_base&state=STATE#wechat_redirect
微信公众号平台的这个接口地址,会根据你传递的AppId,跳转到你给的页面地址,但是它附带了一个code参数,类似这样:
- https://www.w3xue.com/test?code=【微信给你的code】
这个code是干嘛的呢?我们可以用这个code,再加上 AppId 和AppSecret ,来取访问页面的该微信用户对于本公众号的唯一OpenID。这个OpenID 就是一个微信用户在一个公众号的身份ID,它在某一个微信公众号上具有唯一性,一个用户只对应一个OpenID。
然后,我们使用AppId 和 AppSecret 去获取 AccessToken,这里要注意,一个微信公众号获取其 AccessToken 的次数是有限制的(以前上限是2000次/天,现在政策可能变了,不同账号次数不同,但仍然有限制),因此,这是我们在开发过程中需要注意的一个重点。
有了AccessToken,再加上上一步获得的OpenID,我们的程序拿着它们,就可以发送给微信公众号平台,查询一个用户的是否关注等信息了。
另外,我们本章节还将学习如何给页面加上图标,就是用户在分享本页面后,在朋友圈或者聊天记录中显示的那个图标:
在上古时代,微信会获取一个页面中的第一个图片作为图标,现在不行了,需要自定义了。其基本的原理是,在页面引用微信官方的一个JS,然后自定义相关的图标和地址,并且最重要的是,要生成一个签名,这样才能自定义成功。
了解完这些基本原理后,就可以思考开发工作了。
二、准备工作
在正式开发之前,我们要确认公众号具有相关权限,并有正确的设置,然后新建相关的工具类,以便引入使用。
1、一个拥有开发权限的微信公众号,以及其开发用的 AppId 和 AppSecret。当然,还需要进行一些设置:
a.在“公众号设置->功能设置”设置“网页授权域名”和“JS接口安全域名”。把你的程序所在域名(二级域名就用二级域名)加入这2个名单备用,设置这2个可能需要管理员扫码。
b.设置IP白名单,在“开发配置->基本配置->IP白名单”把你的服务器IP加进你的微信公众号IP白名单。
2、在utils文件夹新建2个类:HttpClientUtil类和Encrypt类,HttpClientUtil类用来处理相关与 http请求相关的业务。Encrypt类用来提供几个加密相关的函数。代码提供了下载链接,但具体就不贴出来了(如有红字,用Alt+Enter引入包即可):
3、在utils文件夹下面新建一个Wechat类,用来向微信公众平台发送信息并获取相关信息:
- package com.w3xue.jiaocheng.utils;
- import com.alibaba.fastjson.JSON;
- import com.alibaba.fastjson.JSONObject;
- public class Wechat {
- //获取微信用户授权信息
- public static String getBaseInfo(String code, String appid, String secret)
- {
- return HttpClientUtil.getInstance().sendHttpPost("https://api.weixin.qq.com/sns/oauth2/access_token?appid=" + appid + "&secret=" + secret + "&code=" + code + "&grant_type=authorization_code", code);
- }
- //无时间间隔的获取全局access_token,每天有次数限制,慎用!
- public static String getAccessToken(String appid, String secret)
- {
- String sGetStr = HttpClientUtil.getInstance().sendHttpsGet("https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=" + appid + "&secret=" + secret);
- JSONObject jsonResult = JSON.parseObject(sGetStr);
- return jsonResult.getString("access_token");
- }
- //无时间间隔的获取jsapi_ticket,有次数限制,慎用!
- public static String getJsapiTicket(String access_token)
- {
- String sGetStr = HttpClientUtil.getInstance().sendHttpPost("https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token=" + access_token + "&type=jsapi", "");
- JSONObject jsonResult = JSON.parseObject(sGetStr);
- return jsonResult.getString("ticket");
- }
- //获取用户常用信息,部分公众号没有此权限
- public static String getUserSetInfo(String access_token, String openid)//string appid, string secret,
- {
- return HttpClientUtil.getInstance().sendHttpsGet("https://api.weixin.qq.com/sns/userinfo?access_token="+access_token+"&openid="+openid+"&lang=zh_CN");
- }
- //获取用户综合信息
- public static String getUserInfo(String access_token, String openid)//string appid, string secret,
- {
- return HttpClientUtil.getInstance().sendHttpsGet("https://api.weixin.qq.com/cgi-bin/user/info?access_token=" + access_token + "&openid=" + openid+"&lang=zh_CN");//
- }
- //在获取用户综合信息的基础上获取关注标识(1:已关注 0:未关注)
- public static String getSubscribe(String access_token, String openid) //string appid, string secret,
- {
- String sGetStr = getUserInfo(access_token, openid);
- JSONObject jsonResult = JSON.parseObject(sGetStr);
- return jsonResult.getString("subscribe");
- }
- }
这里的方法基本机构我已经标出来了,基本的微信开发需要的方法都在这里。
4、在MainController类中添加如下方法(需要引用 com.w3xue.jiaocheng.utils.HttpClientUtil类):
- final String strAppid="wx8bda2539bda48d1a";
- final String strAppSecret="506044e1986e2576b0ef4de3f8e3ceea";
- @Autowired
- StringRedisTemplate redisTemplate;//引入redis
- @RequestMapping(value = "/getToken2Ticket")
- public String getToken2Ticket(String key) {
- String reStr = null;
- if (redisTemplate.hasKey(key)) {
- reStr = redisTemplate.opsForValue().get(key);
- } else {
- if (key.equals("ticketKey")) {
- String tmpToken=getToken2Ticket("cgi-token");
- String sGetStr = HttpClientUtil.getInstance().sendHttpPost("https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token=" + tmpToken + "&type=jsapi", "");
- JSONObject result2 = JSON.parseObject(sGetStr);
- if (result2.containsKey("ticket")) {
- reStr = result2.getString("ticket");
- redisTemplate.opsForValue().set(key, reStr);
- redisTemplate.expire(key, 2000, TimeUnit.SECONDS);
- }
- }
- else {
- String sGetStr = HttpClientUtil.getInstance().sendHttpsGet("https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=" + strAppid + "&secret=" + strAppSecret);
- JSONObject result2 = JSON.parseObject(sGetStr);
- if (result2.containsKey("access_token")) {
- reStr = result2.getString("access_token");
- redisTemplate.opsForValue().set(key, reStr);
- redisTemplate.expire(key, 2000, TimeUnit.SECONDS);
- }
- }
- }
- return reStr;
- }
因为上一节中我们已经介绍了如何添加StringRedisTemplate对象,这里就不多解释了。
这个方法的作用,是把AccessToken和Ticket的值,存在Redis里面,时间间隔设置为2000秒。这样,当这个值过期之后,该方法就会调用微信服务器的接口获取数据,并将其保存在Redis中。因为代码很简单,这里就不再详细说明。
5、添加模板文件wechat.html。代码如下:
- <!DOCTYPE html>
- <html>
- <head>
- <meta charset="utf-8">
- <title>首页 - w3xue教程,Spring Boot微信开发</title>
- <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no, minimal-ui">
- <script src="https://cdn.bootcss.com/jquery/2.1.2/jquery.js"></script>
- </head>
- <body>
- <p th:utext="${openid}"></p>
- <p th:utext="${token}"></p>
- <p th:utext="${guanzhu}"></p>
- </body>
- </html>
三、获取OpenID和关注信息
在做好上一节的基本工作后,我们就可以获取一个用户在某个公众号的OpenID了。之前说过,OpenID就是一个微信账号在某个微信公众号的唯一标识码,它是不会重复的,因此可以用在程序逻辑上,作为身份验证的工具。此外,获取AccessToken后,将OpenID、AccessToken一起发送到微信接口,就可以取得该用户的关注信息(即其是否关注了本公众号)。
有了这些理论基础,我就不再多BB了,直接上代码,一会来解释某些细节:
- @RequestMapping("/wechat")
- 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) {
- //微信相关开始
- StringBuilder sbUrl=new StringBuilder();
- if (!(tdsourcetag+"dsa").equals("dsa")) //tdsourcetag为360浏览器常见的自带参数,此处判断其若不为空
- sbUrl.append("?tdsourcetag="+tdsourcetag);
- else { //from、isappinstalled都是微信端常见的自添加参数,这是微信为了统计网页来源添加的,但会影响程序图标的使用
- if (!(from + "dsa").equals("dsa") && (isappinstalled + "dsa").equals("dsa")) //from不为空,isappinstalled为空
- sbUrl.append("?from=" + from);
- else if (!(from + "dsa").equals("dsa") && !(isappinstalled + "dsa").equals("dsa")) //都不为空
- sbUrl.append("?from=" + from + "&isappinstalled=" + isappinstalled);
- else if ((from + "dsa").equals("dsa") && !(isappinstalled + "dsa").equals("dsa"))
- sbUrl.append("?isappinstalled=" + isappinstalled);
- }
- final String strUrl="http://www.xxx.com/jiaocheng/wechat/"+sbUrl.toString(); //请把引号里的内容替换成你服务器上本页面的网址,注意路径后面带斜杠
- String OpenID="";
- try { //OpenID不为空
- OpenID = session.getAttribute("openid").toString();
- }
- catch (Exception e)
- { //OpenID为空
- if((pCode+"dsa").equals("dsa")) {//code为空
- 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");
- }
- else
- { //code不为空
- String sBaseInfo = Wechat.getBaseInfo(pCode, strAppid, strAppSecret);
- JSONObject jsonResult = JSON.parseObject(sBaseInfo);
- OpenID = jsonResult.getString("openid");
- session.setAttribute("openid",OpenID);
- return new ModelAndView("redirect:/wechat/"); //注意路径后面带斜杠
- }
- }
- String sAccessToken=getToken2Ticket("cgi-token"); //如果这个方法放在其他类里面,有可能因为自动装配不能使用
- session.setAttribute("token",sAccessToken);
- String guanzhu=Wechat.getSubscribe(sAccessToken,OpenID); //取关注信息
- mv.addObject("openid", OpenID);
- mv.addObject("token", sAccessToken);
- mv.addObject("guanzhu", guanzhu);
- //微信相关结束
- mv.setViewName("wechat");
- return mv;
- }
如果你关注了该公众号,guanzhu这个变量的值就是1,否则就是0。这里分别打印了用户的openid、用户获取的accessToken、还有用户关注信息。使用二维码生成工具,把该路由地址生成二维码,用微信扫描后,如果一切正常,会出现类似如下的内容:
- oWaL9jek7hDlGSei-PlU75jBT1mE
- 18_Jzbf1VC_L6Jx0F84ERWEknaryTp3cg_loap8btVO-rTq0R45cEKSf4Lj8NzTchS4y_L9E6LsAygaLETEkon5OgRYl9EJP0GyZPBApJ_q2-5A1-mh4JU-c_M1gAxOFfLnEM6mcz4-NLfTyySZCRcAAAEHW
- 1
四、自定义微信分享图标
如果我们点击微信右上角的分享图标会发现,分享出去的图标,是很丑的别针图标,这是微信通用的链接图标。我们如何更改呢?
首先,我们在的<head>标签内引用微信官方的JS库:
- <script src="http://res.wx.qq.com/open/js/jweixin-1.2.0.js"></script>
然后,我们在刚才的引用代码下面添加如下代码:
- <script th:inline="javascript">
- var shareImgUrl = "http://替换成你服务器上的绝对图片地址.png"; //注意必须是绝对路径
- var shareLink = "http://www.xxx.com/jiaocheng/wechat/"; //同样,必须是绝对路径,替换成你服务器上的分享出去的地址,注意网址后面带斜杠
- var shareDes = 'w3xue教程,让人人享有平等学习机会。'; //分享给朋友或朋友圈时的文字简介
- var shareTitle = document.title; //分享title
- var appid = ''; //apiID,可留空
- wx.config({
- debug: false,
- appId:/*[[${appid}]]*/ null,
- timestamp: 1553071742, /*生成签名的时间戳 */
- nonceStr: 'W3FlX1tOkHGP9zga', /*生成签名的随机串 */
- signature: /*[[${qianming}]]*/ null,
- jsApiList: [ /**/
- 'checkJsApi',
- 'onMenuShareTimeline',
- 'onMenuShareAppMessage',
- 'onMenuShareQQ',
- 'onMenuShareWeibo',
- 'onMenuShareQZone'
- ]
- });
- wx.ready(function(){
- wx.onMenuShareTimeline({
- title:shareTitle, // 分享标题
- link: shareLink, // 分享链接,该链接域名或路径必须与当前页面对应的公众号JS安全域名一致
- imgUrl: shareImgUrl, // 分享图标
- success: function () {
- // 用户确认分享后执行的回调函数
- },
- cancel: function () {
- // 用户取消分享后执行的回调函数
- }
- });
- wx.onMenuShareAppMessage({
- title:shareTitle, // 分享标题
- desc: shareDes, // 分享描述
- link: shareLink, // 分享链接,该链接域名或路径必须与当前页面对应的公众号JS安全域名一致
- imgUrl:shareImgUrl, // 分享图标
- type: '', // 分享类型,music、video或link,不填默认为link
- dataUrl: '', // 如果type是music或video,则要提供数据链接,默认为空
- success: function () {
- // 用户确认分享后执行的回调函数
- },
- cancel: function () {
- // 用户取消分享后执行的回调函数
- }
- });
- });
- wx.error(function(res){
- alert('分享出错!'); //错误提示
- });
- </script>
wx.onMenuShareTimeline方法是分享到朋友圈的自定义方法,wx.onMenuShareAppMessage是分享给微信朋友的自定义方法。
这里需要注意,这段JS代码用了 th:inline 标签,里面出现了2个变量,一个是appid,另一个是qianming。appid只需要将我们的开发appid输入即可。但qianming变量,则是需要一套加密方法进行加密的。需要首先取得ticket信息,在之前我们已经在MainController类中添加了getToken2Ticket方法,这里直接取就可以了。取得ticket后,要对信息进行排列,然后使用Encrypt类里面的方法进行SHA1加密,具体请见如下代码:
- //微信分享图标开始
- String sTicket =getToken2Ticket("ticketKey"); //取得ticket
- String sTimestamp = "1553071742"; //时间戳,跟前端页面保持一致
- String sNonceStr = "W3FlX1tOkHGP9zga"; //生成签名的随机串,跟前端页面保持一致
- String string1 = "jsapi_ticket=" + sTicket + "&noncestr=" + sNonceStr + "×tamp=" + sTimestamp + "&url=" + strUrl; //排列字符串
- mv.addObject("appid", strAppid);
- String sQianming=Encrypt.makeSHA1(string1).toUpperCase(); // 进行SHA1加密
- mv.addObject("qianming", sQianming); //绑定签名
- //微信分享图标结束
把这段代码加在wechat路由方法底下。这样,我们打包上传到服务器,然后再分享给朋友,或者分享到朋友圈,就会出现你的自定义图标了。会出现如下图的效果:
注意:本页面内容为W3xue原创,未经授权禁止转载,违者必究!
来源:W3xue 发布时间:2020/9/22 16:42:11