微信公众平台开发的部分知识

最近项目开发的时候涉及微信公众平台的开发,在这个学习过程中遇到了很多问题,了解清楚这部分,大概用了一个多星期,中间遇到很多问题,比较痛苦,网上的资料比较杂乱,导致查阅的时候遇到很多障碍,但是还是耐着性子完成了项目中涉及到的内容,在此记录并分享,为了以后回顾,也希望能够帮助到你。

微信公众平台

首先让我介绍一下微信公众平台,这里是微信公众平台的官方文档
微信公众平台是腾讯基于微信公众号推出的相关业务,我们可以借助该平台更好的运营公众号内容,我参与的项目是微信H5页面项目,主要涉及获取微信公众平台的AccessToken;获取微信用户的基本信息。
在讲述这部分内容之前,让我们先看一下看似无关的内容。

java.net

因为之前的项目中,都是自己自足,最多调用第三方的sdk,从未涉及在项目中调用其他项目的接口,因此,刚开始在理解微信接口调用的时候就花了很多时间,这里简单讲解一下,尽量压缩这个时间。
首先看一下我的项目中的一个比较核心的代码块:

public String surfingTheInternet(String url,String method){
        String message = null;
        try {
            URL urlGet = new URL(url);
            HttpURLConnection http = (HttpURLConnection) urlGet.openConnection();
            //请求方法
            http.setRequestMethod(method);
            http.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
            http.setDoOutput(true);
            http.setDoInput(true);
            http.connect();

            InputStream is = http.getInputStream();
            int size = is.available();
            byte[] jsonBytes = new byte[size];
            is.read(jsonBytes);
            message = new String(jsonBytes, "UTF-8");
        }catch (Exception e){
            e.printStackTrace();
        }
        return message;
    }

让我们看一下这个代码块实现的功能是什么。

首先,我们定义了一个URL类。

URL urlGet = new URL(url);

这个类是java.net包中的类,对于这个类的介绍,这里就不赘述了,可以直接查看API源码,也可以百度相关内容,这里只需知道,它代表着一种资源位置即可。
接下来,我们定义了另一个类

HttpURLConnection http = (HttpURLConnection) urlGet.openConnection();

API文档中的介绍

A URLConnection with support for HTTP-specific features.
这是一个支持HTTP特定特点的URL连接。

当然还介绍了它的一些使用的注意事项等,这里不提;我们可以看到,这是一个连接。那么这就和网络传输有直接联系了,这里建议去看一下关于Request和Response的相关知识,这里的请求头和请求体,还有响应头及响应体是关键点。

接下来的内容

 //请求方法
            http.setRequestMethod(method);
            http.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
            http.setDoOutput(true);
            http.setDoInput(true);

我们相当于设置请求头部分的信息,首先设置了请求方法,然后设置了Content-Type,即传输格式;然后打开了输入输出。

紧接着

http.connect();

我们建立了HTTP的连接,这部分涉及网络连接的知识,建议去了解socket,对于这里的理解会有所帮助。

然后我们就可以从响应体中取到我们想要的内容了,鉴于这部分涉及的都是微信公众平台的接口,返回数据格式都是json。

InputStream is = http.getInputStream();
            int size = is.available();
            byte[] jsonBytes = new byte[size];
            is.read(jsonBytes);
            message = new String(jsonBytes, "UTF-8");

这里首先就是获得输入流,然后对输入流大小就行预估,之后读取输入流并进行String转化,最后获取到我们想要的json格式数据。

微信公众平台的对接及使用

首先在使用微信公众平台的时候,我们需要申请一个微信公众平台的账号,鉴于个人测试,所以选择对应的个人即可,但是相应的接口权限会很少,想要获取接口权限就需要花钱认证。不过腾讯考虑到我们开发者的测试用途,所以可以直接申请测试号进行测试开发,最后转移到有相应权限的公众号即可。
如下图所示就是获取测试号的整个流程
首先通过微信授权登陆个人注册的微信公众平台
然后进入到开发模块,选择开发者工具
这里写图片描述
接着在其中找到测试号
这里写图片描述
接下来,测试号也需要有相应的微信授权,通过授权后进入
这里写图片描述
这里就有我们想要的appID以及appsecret,这两个参数是很重要的,参与到后面的AccessToken获取。
之后在进行接口开发和调用前,我们首先需要和测试号这面对接。
这里的url就是http://ip:port/xxxx/**,一般我的习惯命名都是http://ip:port/wx,然后这个对接的接口怎么写呢?
让我们看一下下面的代码块

 /**
     * 和微信接入
     * */
    @RequestMapping(value = "/wx",method = RequestMethod.GET)
    public void get(HttpServletRequest request, HttpServletResponse response){


        // 微信加密签名,signature结合了开发者填写的token参数和请求中的timestamp参数、nonce参数。
        String signature = request.getParameter("signature");
        //时间戳
        String timestamp = request.getParameter("timestamp");
        //随机数
        String nonce = request.getParameter("nonce");
        //随机字符串
        String echostr = request.getParameter("echostr");

        PrintWriter out = null;

        try{
            out = response.getWriter();
            // 通过检验signature对请求进行校验,若校验成功则原样返回echostr,否则接入失败
            if(SignUtil.checkSignature(signature,timestamp,nonce)){
                out.print(echostr);
            }
        }catch (IOException e){
            e.printStackTrace();
        }finally {
            out.close();
            out = null;
        }

    }

这里是按照官网文档内容设计的。逻辑是:我们通过该接口接受微信公众平台给我们发送的数据,然后通过相应的工具类把各个内容拆解验证,最后返回给微信公众平台echostr字段,如果验证成功,那么对接就此成功。

接下来我们看一下使用的工具类

package util;


import constant.WeChatConstant;

import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Arrays;
import java.util.Timer;

/**
 * Created by yubotao on 2017/11/15.
 */
public class SignUtil {


    /**
     *验证签名
     * @param signature
     * @param timestamp
     * @param nonce
     */
    public static boolean checkSignature(String signature,String timestamp,String nonce){
        //第一个参数是token
        String[] arr = new String[]{WeChatConstant.token,timestamp,nonce};
        //将token、timestamp、nonce三个参数进行字典排序
        Arrays.sort(arr);

        StringBuilder content = new StringBuilder();

        for(int i =0; i < arr.length; i++){
            content.append(arr[i]);
        }

        MessageDigest md = null;
        String tmpStr = null;

        try{
            md = MessageDigest.getInstance("SHA-1");
            // 将三个参数字符串拼接成一个字符串进行sha1加密
            byte[] digest = md.digest(content.toString().getBytes());
            tmpStr = byteToStr(digest);
        }catch (NoSuchAlgorithmException e){
            e.printStackTrace();
        }

        content = null;
        // 将sha1加密后的字符串可与signature对比
        return tmpStr != null ? tmpStr.equals(signature.toUpperCase()):false;

    }


    /**
     * 将字节数组转换为十六进制字符串
     * @param byteArray
     * @return
     */
    private static String byteToStr(byte[] byteArray){
        String strDigest = "";
        for(int i = 0; i < byteArray.length; i++){
            strDigest += byteToHexStr(byteArray[i]);
        }

        return strDigest;
    }


    /**
     * 将字节转换为十六进制字符串
     * @param mByte
     * @return
     */
    private static String byteToHexStr(byte mByte){
        char[] Digit = {'0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'};
        char[] tempArr = new char[2];
        tempArr[0] = Digit[(mByte >>> 4) & 0X0F];
        tempArr[1] = Digit[mByte & 0X0F];

        String s = new String(tempArr);
        return s;
    }

}

这个工具类的主要作用就是:将WeChatConstant.tokentimestampnonce三个参数进行SHA-1加密,然后和一同传输过来的signature对比,成功后返回true,否则返回false。
下面两个方法只是对于16进制的转换和处理。

当对接成功后,会有相应的提示。

接下来,我们就可以开发相应的接口来实现获取AccessToken了。

获取AccessToken

先看我的代码是如何获取AccessToken的

@Autowired
    GetSomethingUtil token;

    /**
     *获取2小时的AccessToken
     */
    @RequestMapping(value = "/a/wx/accessToken",method = RequestMethod.GET)
    public String getAccessToken(HttpServletRequest request, HttpServletResponse response, Model model){

        List<String> tokenList = token.getAccessToken();

        log.info("tokenList : " + tokenList);

        String accessToken = tokenList.get(0);
        String expiresIn = tokenList.get(1);

        model.addAttribute("accessToken",accessToken);
        model.addAttribute("expiresIn",expiresIn);

        return "tokenTest";
    }

这里进行了工具类的封装,然后再看一下工具类。

public List<String> getAccessToken(){
        String url = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=" + WeChatConstant.AppID + "&secret=" + WeChatConstant.AppSecret;

        String accessToken = null;
        String expriesIn = null;
        List<String> another = new ArrayList<String>();

        String message = this.surfingTheInternet(url,"GET");

        Gson gson = new Gson();
        AccessToken token = gson.fromJson(message, AccessToken.class);

        accessToken = token.getAccess_token();
        expriesIn = token.getExpires_in();
        log.info("accessToken : " + accessToken);
        log.info("expiresIn : " + expriesIn);

        another.add(accessToken);
        another.add(expriesIn);

        return another;
    }

这里的工具类方法,完成了对微信接口的请求,并对返回的json格式数据进行了处理,最后返回给Controller接口,并展示到相应的jsp页面。

该接口运行效果图:
这里写图片描述

通过AccessToken及OpenId获取用户信息

首先正常的流程是,每当一个用户进入对应的微信H5界面,会通过前端配置返回一个code值,而后端通过该code值及相应的接口信息获取openId,之后在通过对应的信息获取到用户的基本信息。
但是由于本人能力有限且时间紧迫,js配置获取code的部分我就没有实现了,感兴趣的小伙子可以去微信公众平台官方文档查阅相关内容。

那么是不是就没办法做了呢?
当然不是,这里我使用了一种取巧的方式,来验证如何使用AccessToken和openId获取用户的基本信息。

首先我们查阅官方文档可以发现有一个可以获取关注该微信公众号的所有用户OpenId的接口,那么我们就可以通过这个获取OpenId并且验证用户信息是否能正确取到。
相应的接口方法

/**
     *通过AccessToken获取关注的用户的openId列表
     *  @param accessToken
     */
    @RequestMapping(value = "/a/wx/openId",method = RequestMethod.GET)
    public String getOpenId(HttpServletRequest request,HttpServletResponse response,Model model,String accessToken){
        List<String> data = token.getOpenIdList(accessToken);

        log.info("data : " + data);

        model.addAttribute("data",data);

        return "openIdTest";
    }
 public List<String> getOpenIdList(String accessToken){
        String url = "https://api.weixin.qq.com/cgi-bin/user/get?access_token=" + accessToken + "&next_openid=";

        String message = this.surfingTheInternet(url,"GET");
        log.info("message : " + message);

        Gson gson = new Gson();
        OpenId openId = gson.fromJson(message, OpenId.class);

        String total = openId.getTotal();
        log.info("total : " + total);
        String count = openId.getCount();
        log.info("count : " + count);
        List<String> data = openId.getData().getOpenid();
        log.info("data : " + data);
        String next_openid = openId.getNext_openid();
        log.info("next_openid : " + next_openid);

        return data;
    }

这里和前面获取AccessToken的处理雷同,不在赘述。
之后获取到的OpenId效果图如下
这里写图片描述
这里取到OpenId后,我们就可以验证是否可以取到用户的基本信息了。

相关接口方法

 /**
     *通过AccessToken和openId获取用户的基本信息
     * @param accessToken
     * @param openId
     */
    @RequestMapping(value = "/a/wx/person",method = RequestMethod.GET)
    public String getPerson(HttpServletRequest request,HttpServletResponse response,Model model,String accessToken,String openId){
        String message = token.getPersonByOpenId(accessToken,openId);

        log.info("message : " + message);

        Gson gson = new Gson();
        Person person = gson.fromJson(message, Person.class);

        String subscribe = person.getSubscribe();
        String openid = person.getOpenid();
        String nickname = person.getNickname();
        String sex = person.getSex();
        String language = person.getLanguage();
        String city = person.getCity();
        String province = person.getProvince();
        String country = person.getCountry();
        String headimgurl = person.getHeadimgurl();
        String subscribe_time = person.getSubscribe_time();
        String unionid = person.getUnionid();
        String remark = person.getRemark();
        String groupid = person.getGroupid();
        List<String> tagid_list = person.getTagid_list();

        model.addAttribute("subscribe",subscribe);
        model.addAttribute("openid",openid);
        model.addAttribute("nickname",nickname);
        model.addAttribute("sex",sex);
        model.addAttribute("language",language);
        model.addAttribute("city",city);
        model.addAttribute("province",province);
        model.addAttribute("country",country);
        model.addAttribute("headimgurl",headimgurl);
        model.addAttribute("subscribe_time",subscribe_time);
        model.addAttribute("unionid",unionid);
        model.addAttribute("remark",remark);
        model.addAttribute("groupid",groupid);
        model.addAttribute("tagid_list",tagid_list);

        return "personTest";
    }
public String getPersonByOpenId(String accessToken,String openId){
        String url = "https://api.weixin.qq.com/cgi-bin/user/info?access_token=" + accessToken + "&openid=" + openId+ "&lang=zh_CN";

        String message = this.surfingTheInternet(url,"GET");
        log.info("message : " + message);

        return message;
    }

然后获得的效果图如下
这里写图片描述
可以看到我们已经取到我的基本信息了,到此为止,我们就完成了获取用户基本信息的操作。


项目中还涉及到使用mediaId获取图片并实现图片从微信公众平台暂存转储到其他对象存储里,不过这里涉及到文件上传,第三方工具类调用等等内容,这里就不介绍了,这部分也是可以直接查看官方文档找到答案的。

至此,本文的所有内容就介绍完毕了,并没有涉及到消息自动回复以及菜单设计,这些内容再官方文档上都有答案,不过要注意的是XML包解析。
这里还要提的一点就是,AccessToken的有效时间只有2小时,所以需要定时刷新,并且官方推荐的方法就是创建一个中控来定时刷新,最关键的是获取AccessToken的接口每天有2000次的请求限制,所以定时刷新是必要的,不过这里暂不介绍,下一篇文章就是讲解定时任务。
如果本文有什么遗漏或者错误,还请不吝赐教。

GitHub项目地址

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值