JAVA微信服务号开发简记

现在微信公众平台的开发已经越来越普遍,这次开发需要用到微信公众平台。所以这边做一个简单的记录,也算是给那些没踩过坑的童鞋一些启示吧。我将分几块来简单的描述一下,之后会做详细的说明。

基本认证信息说明

首先。微信分为了几个不同的号。有订阅号、服务号、企业号。

其中订阅号是免费的。其他都是要钱的。免费的基本上只能发发信息。

要钱的功能比较全面。基本上微信的接口都能调用了。这里我也主要开发的是服务号。

如果要申请服务号微信也是明码标价,300块一次,一年一次。(收钱真狠)

简单的说,如果你没有交钱那么很多功能是无法使用的。

功能性说明

微信提供了许许多多的接口供用户使用,开发者可以根据这些接口获取用户的信息。

普遍的有用户的昵称性别地理位置等。

其他我就不一一列举了,总之需要根据你的实际业务需求来进行使用。

你需要什么就去看看微信是否提供了相应的接口供你使用。

微信接口文档地址路径:https://mp.weixin.qq.com/wiki/home/index.html

接口调用说明

微信的接口有几种不同的类型:

1、get请求,微信会提供一个地址,然后你直接在这个地址后面加上参数即可调用接口

2、post请求,微信提供一个地址,你需要向这个接口地址传入相应的参数。

3、回调请求,微信在收到用户消息或者别的触发条件成立时调用用户设置的URL地址。

请求格式包括XML和json,需要根据接口文档来。任何语言均可,JS有微信专门的JS-SDK。

我使用的是JAVA

关键词说明

OpenID:用户在你这个公众号上的唯一标识(我这边的处理是与项目中用户的UserId进行绑定,这样也就实现了用户在你的平台进行绑定)

access_token :注意,这里我把这个名字换一下,“ACCESS_TOKEN”为了和之后的分开。这个东西是一个全局量,当你需要调用特定的微信接口时,微信会验证你是否有权限,你就需要传这个值给微信。注意!!获取access_token是有次数限制的,但是access_token 的过期时间很长,所以在没有过期的情况下请勿重复获取。

access_token:这个和我称作小写的access_token和大写的不一样,这个是用户鉴权时使用,获取这个小写的access_token是没有次数限制的,这个东西能只是用于获取用户openId时使用。(新手千万要区分不同的access_token以免出现问题)

开发者填写的URL:接口文档出现这个的时候一开始我也很懵比,这里的URL是指,当用户如果进行特定的操作,如:发送给公众号消息,进入公众号,等等,微信会把一些参数传入这个URL。举个例子,如果用户发送给公众号一个字符。那么公众号会把这个字符转发给你这个URL,然后你这个URL接到微信给你的发的信息,你就可以返回给用户信息了。

这个URL是在这里设置的:

这里的服务器地址URL就是

其中微信有两个信息比较重要,第一微信会告诉你这是用户的什么事件,第二是哪个用户也就是用户的openId

网页授权域名:

在这里必须设置网页的授权域名,否则用户在微信公众号中对于你的域名下的地址将不信任。

开发三步走

这是建立在你已经做好准备的情况下是三步,如果你连公众号服务器域名都没有的话就不只了。

第一步、网页授权域名

下载对应文件,配置在tomcat你的项目的根目录下。

保证:域名/下载的文件名。这个地址能访问到,能在浏览器中显示文字即可。

第二步、获取openId

文档中在:https://mp.weixin.qq.com/wiki/17/c0f37d5704f0b64713d5d2c37b468d75.html

1、生成地址

https://open.weixin.qq.com/connect/oauth2/authorize?(这个是不动的)
appid=xxxxxxxxxx(这个是你的appid)
&redirect_uri=http%3a%2f%2fwww.xxxxxxx.com%xxxxxxxx%2fweixxxxxit%2fxxxxx(这个是你的回调地址,需要URL编码)
&response_type=code(不动)
&scope=snsapi_base(如果只要获取openId就不动,需要别的参考文档)
#wechat_redirect(不动)

最终地址类似:

https://open.weixin.qq.com/connect/oauth2/authorize?appid=xxxxxxxxxx&redirect_uri=http%3a%2f%2fwww.xxxxxxx.com%xxxxxxxx%2fweixxxxxit%2fxxxxx&response_type=code&scope=snsapi_base#wechat_redirect

用户访问这个地址,微信就会回调你的回调地址,你在回调地址中就可以获取到openId了

回调地址中springMVC的写法:

/**
     * 微信获取openId
     */
    @RequestMapping(value="/weiXin", method=RequestMethod.GET)
    public String weiXin(HttpServletRequest request)
    {
        //获取用户openId
        String openId = "";

        //用户同意授权,获取返回值code和state
        String code = request.getParameter("code");
        //如果code为空则(请自己实现判断,这里是简写)
        if(code == null){
            return "index";
        }

        //获取openid的地址
        String getOpenidUri = "https://api.weixin.qq.com/sns/oauth2/access_token"
                + "?appid=" + AppID
                + "&secret=" + AppSecret
                + "&code=" + code
                + "&grant_type=authorization_code";
        try{
             //发送请求获取返回参数,使用java原生实现,可以使用httpclient或者第三方库实现
               URL getOpenidUrl = new URL(getOpenidUri);
             HttpURLConnection openidConnection = (HttpURLConnection) getOpenidUrl.openConnection();
             openidConnection.connect();

             BufferedReader openidReader = new BufferedReader(new InputStreamReader(openidConnection.getInputStream()));
             StringBuffer openidStringBuffer = new StringBuffer();
             String openidLines;
             while ((openidLines = openidReader.readLine()) != null) {
                 openidStringBuffer.append(openidLines);
             }
             openidReader.close();
             openidConnection.disconnect(); 

             //返回的参数是json格式
               JSONObject openidJson = JSONObject.parseObject(openidStringBuffer.toString());
             if(openidJson.containsKey("openid")){
                 openId = openidJson.getString("openid");
             }
        }catch(Exception e){
            //连接异常,请求异常,或者json返回值异常均需要处理
        }

        //需要对openId判空,请自行处理
         System.out.println(openId);
        return "index";
    }

当微信回调你这个地址之后按照上述代码执行后即可获取到openId,上述代码只是参考,实际中需要改进很多判断以及对很多错误情况的预判。你也可以根据接口文档详细书写。

第三步、微信调用接口设计

checkoutURL

之前说过,在设置URL的时候会有一个核对的过程,你需要给出你的接口地址,然后返回一个参数验证过后微信才会同意你修改这个URL

下面是checkout的代码,因为涉及到加密所以需要多多注意

import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
//工具类,之后会被调用
public class CheckoutUtil {
    // 与接口配置信息中的Token要一致
    private static String token = "xxxxxxxxxxx";

    /**
     * 验证签名
     *
     * @param signature
     * @param timestamp
     * @param nonce
     * @return
     */
    public static boolean checkSignature(String signature, String timestamp, String nonce) {
        String[] arr = new String[] { token, timestamp, nonce };
        // 将token、timestamp、nonce三个参数进行字典序排序
        // Arrays.sort(arr);
        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;
    }
    public static void sort(String a[]) {
        for (int i = 0; i < a.length - 1; i++) {
            for (int j = i + 1; j < a.length; j++) {
                if (a[j].compareTo(a[i]) < 0) {
                    String temp = a[i];
                    a[i] = a[j];
                    a[j] = temp;
                }
            }
        }
    }
}
@RequestMapping(value="/weixinTest")
    public void weixinTest(HttpServletRequest request) throws Exception{
        boolean isGet = request.getMethod().toLowerCase().equals("get");
        PrintWriter print;
        if (isGet) {
            // 微信加密签名
            String signature = request.getParameter("signature");
            // 时间戳
            String timestamp = request.getParameter("timestamp");
            // 随机数
            String nonce = request.getParameter("nonce");
            // 随机字符串
            String echostr = request.getParameter("echostr");
            // 通过检验signature对请求进行校验,若校验成功则原样返回echostr,表示接入成功,否则接入失败
            if (signature != null && CheckoutUtil.checkSignature(signature, timestamp, nonce)) {
                try {
                    print = response.getWriter();
                    print.write(echostr);
                    print.flush();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

URL:http://www.xxxxxxxx.com/weixinTest/

Token:xxxxxxxxxxxx

类似上面

测试时使用兼容模式,等测试完成后使用安全模式,微信提供了AES的加密解密工具类

https://mp.weixin.qq.com/wiki/1/5dc395cdeb98e9d23a8541cf0bab38ad.html

最后微信调用URL

需要解析XML,也就顺带加个工具类需要使用相应dom4j的jar

import java.io.InputStream;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.servlet.http.HttpServletRequest;

import org.dom4j.Document;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;

public class MessageUtil {
    public static Map parseXml(HttpServletRequest request) throws Exception {
        // 将解析结果存储在HashMap中
        Map map = new HashMap();

        // 从request中取得输入流
        InputStream inputStream = request.getInputStream();

        // 读取输入流
        SAXReader reader = new SAXReader();
        Document document = reader.read(inputStream);
        // 得到xml根元素
        Element root = document.getRootElement();
        // 得到根元素的所有子节点
        List<Element> elementList = root.elements();

        // 遍历所有子节点
        for (Element e : elementList)
            map.put(e.getName(), e.getText());

        // 释放资源
        inputStream.close();
        inputStream = null;

        return map;
    }
}

这边以获取地理位置为例(需要在微信后台手动开启这个功能哦)

@RequestMapping(value="/weixinTest")
    public void weixinTest(HttpServletRequest request) throws Exception{
        // 调用parseXml方法解析请求消息
        Map<String, String> requestMap = MessageUtil.parseXml(request);
        // 发送方帐号
        String fromUserName = requestMap.get("FromUserName");
        log.error("----------------------------------------------" + fromUserName);
        // 开发者微信号
        String toUserName = requestMap.get("ToUserName");
        // 消息类型
        String msgType = requestMap.get("MsgType");

        if ("event".equals(msgType)){
            log.error("----------------------------------------------" +msgType);
            String Latitude = requestMap.get("Latitude");
            log.error("----------------------------------------------" +Latitude);
            String Longitude = requestMap.get("Longitude");
            log.error("----------------------------------------------" +Longitude);

        }
}

需要注意的点

1、看清接口文档,仔细理解

2、checkout不要删除,修改地址时需要使用,当地址稳定后项目上线之后直接删除。

3、微信调用URL会被用户平凡访问,向我这样写肯定是不对的,如果你的用户量大负载均衡肯定是必须的。

4、微信接口虽然稳定,但是还是要注意处理意外请求,处理一些可能会出现的错误情况。否则当出现没有这个参数而你去取,很容易导致异常。

5、所有测试均需要在服务器上完成,也就需要外网可以被访问的域名和地址。

6、微信给的权限很多,需要自己看清楚,有的权限是有次数限制的。

7、尽可能把一些参数封装一下,避免代码冗长。

8、所有测试完成后请换成密文形式,不然用户的信息可能泄露。

9、以上所有代码仅供参考,实际中还需要添砖加瓦,大改特改。

10、服务器上调试日志很重要。

时间: 2024-12-27 23:59:59

JAVA微信服务号开发简记的相关文章

微信服务号开发-获取用户位置信息

微信服务号开发-获取用户位置信息 在微信公众号开发的中,获取用户位置信息是非常常见的功能需求,通过用户的位置信息,可以做一些地图导航,以及基于LBS的营销活动.下面将介绍微信服务号获取用户位置信息的原理与步骤. 原理 1. 位置信息获取流程 2. 位置信息报文 <xml><ToUserName><![CDATA[gh_public_member_account]]></ToUserName> <FromUserName><![CDATA[o

《初识Java微信公众号开发》 学习中遇到的困难

前一段时间无聊的时候,在慕课网上自学了一点点微信公众号开发(受学姐威胁). 慕课网教程的地址:http://www.imooc.com/learn/368 毕竟是免费的课程,不可能讲的那么详细.所以我吧我遇到的问题跟大家分享一下. 这是我照着课程敲的代码(IDE是Eclipse): https://github.com/Zuosy/WeiXin 发到github上面了. 为了这个,我还专门到廖大的网站上学了一遍Git. 廖大的Git教程传送们:https://www.liaoxuefeng.co

JAVA微信公众号开发入门

好久没进博客,最近应某人的要求,要每周写一篇博客,故此今天就把我这周刚学的微信公众号开发的知识在此总结总结,以供后人查阅.我写博客都是简要白话文,勿喷. 1.为什么要学微信公众号开发 微信的用户量庞大,微信公众号的完善和不断的开放力度,企业市场对公众号开发的需求不断增加 2.如何搭建微信开发平台 开发工具什么的就不说了,因为微信是要外网访问的,所有你必须有个网上的服务器资源,而且你必须申请一个微信公众号. 2.1申请微信公众号 公众号有3种:订阅号,服务号,企业号 区别是企业号适合内部人使用,因

微信服务号开发笔记

原理 微信服务号的原理比较简单.从请求响应角度来看,逻辑是: 用户微信客户端 <—> 微信服务器 <—> 微信服务号后台程序 <—> 数据库或Web Service 也就是,用户的各种请求先经过微信的服务器,微信服务器将请求转发给微信服务号后台程序. 既然是微信服务器把用户请求数据转发给我们开发的微信服务号后台程序,那么在启用服务号的开发模式时就需要提供一个URL.另外为了安全 考虑,还需要提供一个token,用来校验请求是否来自微信服务器.校验的方法见微信开发者文档.

微信服务号开发-商城微信登录

最近帮朋友写了个微信服务号,服务号名字叫做十四行诗.没错是卖月饼的商城. 简单介绍下微信登录,与官方文档不同,简单画了一下UML图 简单的说就是先建立了一个index.php(直接拍域名就过去了.),然后传一个appid,微信公众号后台能拿到 <?php $appid = ''; header('location:https://open.weixin.qq.com/connect/oauth2/authorize?appid='.$appid.'&redirect_uri=http://w

微信 服务号开发

tp5.0  一个入口文件,一个wx类 //WxAction.php 入口 <?php /** * Created by PhpStorm. * User: lxd * Date: 17/10/31 * Time: 18:01 * 微信请求统一入口,单独的类,无需基础其他类 */ namespace app\index\controller; use telerr\Chaoxin; use think\Config; use think\Request; use wx\Wx; class WxA

Java微信公众号开发

微信公众平台是腾讯为了让用户申请和管理微信公众账号而推出的一个web平台.微信公众账号的种类可以分为3种,并且一旦选定不可更改.按照功能的限制从小到大依次为:订阅号.服务号.企业号.个人只能注册订阅号.注册地址:https://mp.weixin.qq.com/. 开发环境的准备 微信公众号 外网映射工具(开发调试) 与微信的对接的URL应该满足以下的条件: 在公网上能够访问 只支持80端口 映射工具有很多,例如花生壳,ngrok可以将内网映射到公网上面,这样就可以使用公网访问本机的网络服务.下

Java微信公众号开发-外网映射工具配置

一.开发环境准备 1.一个微信公众号 2.外网映射工具(开发调试)如花生壳.ngrok工具 注:与微信对接的URL要具备以下条件a:在公网上能够访问 b:端口只支持80端口 这里使用ngrok.cc: ngrok.cc它的服务基于ngrok实现,防止ngrok官网被墙不能访问,把服务器架设在了香港,不仅大大加快了大家的访问速度,还不怕被墙了,而且完全免费.官网地址::http://ngrok.cc/ 搭建步骤: 1.下载客户端 下载链接:http://www.ngrok.cc/index.php

Java微信公众号开发----关键字自动回复消息

在配置好开发者配置后,本人第一个想要实现的是自动回复消息的功能,说明以下几点: 1. url 仍然不变,还是开发配置里的url 2. 微信采用 xml 格式传输数据 3.微信服务器传给我们的参数主要有(如图): 附上解析xml类的依赖: 1 <!-- dom对象读取写入xml文件 --> 2 <dependency> 3 <groupId>dom4j</groupId> 4 <artifactId>dom4j</artifactId>