1.首先需要注册,https://mp.weixin.qq.com/,然后设置公众号信息什么的,默认是使用的微信公众号自己的后台,如果只是需要做一些简单了的操作,比如固定的关键字自动回复等,没有复杂的需求,就可以直接使用微信公众号本身的功能,比较方便,如果是比较复杂的,就需要接入自己的服务器,自己后台连上微信服务器获取数据,处理得到结果后再返回给微信服务器,微信服务器再发给手机

2.注册后,在开发 -> 基本配置里配置相关信息,开发者密码要记住,在后面的获取AccessToken时要用到

如图所示,主要配置这几项,服务器地址要填写到具体的Servlet,此时的WxServlet是用于专门验证的,修改配置参数后需要验证,就访问这个Servlet看是否可用,访问方式是 get ,所以要写在doGet() 里面,而具体处理微信服务器传过来的数据是 post 方式,所以在doPost() 里面处理

java 创建公众号菜单 java的公众号_服务器

package cn.ihsuzi.weixin;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Arrays;
import java.util.Map;
public class WxServlet extends HttpServlet
{
    /**
     * Token可由开发者可以任意填写,用作生成签名(该Token会和接口URL中包含的Token进行比对,从而验证安全性)
     * 比如这里我将Token设置为purple
     */
    private final String TOKEN = "purple";
    /**
     * 处理微信服务器发来的消息
     */
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
    {
        // TODO 接收、处理、响应由微信服务器转发的用户发送给公众帐号的消息
        // 将请求、响应的编码均设置为UTF-8(防止中文乱码)
        request.setCharacterEncoding("UTF-8");
        response.setCharacterEncoding("UTF-8");
        System.out.println("请求进入");
        String result = "";
        try {
            Map<String,String> map = MessageHandlerUtil.parseXml(request);
            System.out.println("开始构造消息");
            result = MessageHandlerUtil.buildXml(map);
            System.out.println(result);
            if(result.equals("")){
                result = "未正确响应";
            }
        } catch (Exception e) {
            e.printStackTrace();
            System.out.println("发生异常:"+ e.getMessage());
        }
        response.getWriter().println(result);
    }
    protected void doGet(HttpServletRequest request,
            HttpServletResponse response) throws ServletException, IOException
    {
        System.out.println("开始校验签名");
        /**
         * 接收微信服务器发送请求时传递过来的4个参数
         */
        String signature = request.getParameter("signature");// 微信加密签名signature结合了开发者填写的token参数和请求中的timestamp参数、nonce参数。
        String timestamp = request.getParameter("timestamp");// 时间戳
        String nonce = request.getParameter("nonce");// 随机数
        String echostr = request.getParameter("echostr");// 随机字符串
        // 排序
        String sortString = sort(TOKEN, timestamp, nonce);
        // 加密
        String mySignature = sha1(sortString);
        // 校验签名
        if (mySignature != null && mySignature != ""
                && mySignature.equals(signature))
        {
            System.out.println("签名校验通过。");
            // 如果检验成功输出echostr,微信服务器接收到此输出,才会确认检验完成。
            // response.getWriter().println(echostr);
            response.getWriter().write(echostr);
        } else
        {
            System.out.println("签名校验失败.");
        }
    }
    /**
     * 排序方法
     *
     * @param token
     * @param timestamp
     * @param nonce
     * @return
     */
    public String sort(String token, String timestamp, String nonce)
    {
        try
        {
            String[] strArray =
            {token, timestamp, nonce};
            Arrays.sort(strArray);
            StringBuilder sb = new StringBuilder();
            for (String str : strArray)
            {
                sb.append(str);
            }
            return sb.toString();
        } catch (NullPointerException e)
        {
            return "";
        }
    }
    /**
     * 将字符串进行sha1加密
     *
     * @param str
     *            需要加密的字符串
     * @return 加密后的内容
     */
    public String sha1(String str)
    {
        try
        {
            MessageDigest digest = MessageDigest.getInstance("SHA-1");
            digest.update(str.getBytes());
            byte messageDigest[] = digest.digest();
            // Create Hex String
            StringBuffer hexString = new StringBuffer();
            // 字节数组转换为 十六进制 数
            for (int i = 0; i < messageDigest.length; i++)
            {
                String shaHex = Integer.toHexString(messageDigest[i] & 0xFF);
                if (shaHex.length() < 2)
                {
                    hexString.append(0);
                }
                hexString.append(shaHex);
            }
            return hexString.toString();
        } catch (NoSuchAlgorithmException e)
        {
            e.printStackTrace();
        }
        return "";
    }
}


先写好 WxServlet,然后验证,只有验证成功才能正常实现其他功能

3.AccessToken处理
正常进行数据交换处理等需要AccessToken,但只有两个小时的有效期,一天最多请求2000次,所以需要专门处理,比如可以设置7000s请求一次
实现三个类:AccessToken,AccessTokenInfo,AccessTokenServlet

package cn.ihsuzi.weixin;
public class AccessToken
{
    //获取到的凭证
    private String accessToken;
    //凭证有效时间,单位:秒
    private int expiresin;
    public String getAccessToken() {
        return accessToken;
    }
    public void setAccessToken(String accessToken) {
        this.accessToken = accessToken;
    }
    public int getExpiresin() {
        return expiresin;
    }
    public void setExpiresin(int expiresin) {
        this.expiresin = expiresin;
    }
}
package cn.ihsuzi.weixin;
public class AccessTokenInfo
{
    //注意是静态的
    public static AccessToken accessToken = null;
}
package cn.ihsuzi.weixin;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
public class AccessTokenServlet extends HttpServlet
{
    @Override
    public void init() throws ServletException
    {
        System.out.println("启动WebServlet");
        super.init();
        final String appId = "xxxxxx";
        final String appSecret = "xxxxxxxx";
        // 开启一个新的线程
        new Thread(new Runnable()
        {
            @Override
            public void run()
            {
                while (true)
                {
                    try
                    {
                        // 获取accessToken
                        AccessTokenInfo.accessToken = getAccessToken(appId,
                                appSecret);
                        // 获取成功
                        if (AccessTokenInfo.accessToken != null)
                        {
                            // 获取到access_token 休眠7000秒,大约2个小时左右
                            Thread.sleep(7000 * 1000);
                            // Thread.sleep(10 * 1000);//10秒钟获取一次
                        } else
                        {
                            // 获取失败
                            Thread.sleep(1000 * 3); // 获取的access_token为空 休眠3秒
                        }
                    } catch (Exception e)
                    {
                        System.out.println("发生异常:" + e.getMessage());
                        e.printStackTrace();
                        try
                        {
                            Thread.sleep(1000 * 10); // 发生异常休眠1秒
                        } catch (Exception e1)
                        {
                        }
                    }
                }
            }
        }).start();
    }
    /**
     * 获取access_token
     *
     * @return AccessToken
     */
    private AccessToken getAccessToken(String appId, String appSecret)
    {
        NetWorkHelper netHelper = new NetWorkHelper();
        /**
         * 接口地址为https://api.weixin.qq.com/cgi-bin/token?grant_type=
         * client_credential
         * &appid=APPID&secret=APPSECRET,其中grant_type固定写为client_credential即可。
         */
        String Url = String
                .format("https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=%s&secret=%s";,
                        appId, appSecret);
        // 此请求为https的get请求,返回的数据格式为{"access_token":"ACCESS_TOKEN","expires_in":7200}
        String result = netHelper.getHttpsResponse(Url, "");
        System.out.println("获取到的access_token=" + result);
        // 使用FastJson将Json字符串解析成Json对象
        JSONObject json = JSON.parseObject(result);
        AccessToken token = new AccessToken();
        token.setAccessToken(json.getString("access_token"));
        token.setExpiresin(json.getInteger("expires_in"));
        return token;
    }
}

然后就是处理微信服务器传过来的数据了,数据格式是xml格式的,所以需要解析xml,先写一个专门的类来处理请求

package cn.ihsuzi.weixin;
importjavax.net.ssl.*;
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
importjava.net.URL;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
public class NetWorkHelper
{
    /**
     * 发起Https请求
     * @param reqUrl 请求的URL地址
     * @param requestMethod
     * @return 响应后的字符串
     */
    public String getHttpsResponse(String reqUrl, String requestMethod) {
        URL url;
        InputStream is;
        String resultData = "";
        try {
            url = new URL(reqUrl);
            HttpsURLConnection con = (HttpsURLConnection) url.openConnection();
            TrustManager[] tm = {xtm};
            SSLContext ctx = SSLContext.getInstance("TLS");
            ctx.init(null, tm, null);
            con.setSSLSocketFactory(ctx.getSocketFactory());
            con.setHostnameVerifier(new HostnameVerifier() {
                @Override
                public boolean verify(String arg0, SSLSession arg1) {
                    return true;
                }
            });
            con.setDoInput(true); //允许输入流,即允许下载
            //在android中必须将此项设置为false
            con.setDoOutput(false); //允许输出流,即允许上传
            con.setUseCaches(false); //不使用缓冲
            if (null != requestMethod && !requestMethod.equals("")) {
                con.setRequestMethod(requestMethod); //使用指定的方式
            } else {
                con.setRequestMethod("GET"); //使用get请求
            }
            is = con.getInputStream();   //获取输入流,此时才真正建立链接
            InputStreamReader isr = new InputStreamReader(is);
            BufferedReader bufferReader = new BufferedReader(isr);
            String inputLine;
            while ((inputLine = bufferReader.readLine()) != null) {
                resultData += inputLine + "\n";
            }
            System.out.println(resultData);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return resultData;
    }
    X509TrustManager xtm = new X509TrustManager() {
        @Override
        public X509Certificate[] getAcceptedIssuers() {
            return null;
        }
        @Override
        public void checkServerTrusted(X509Certificate[] arg0, String arg1)
                throws CertificateException {
        }
        @Override
        public void checkClientTrusted(X509Certificate[] arg0, String arg1)
                throws CertificateException {
        }
    };
}

然后就是对收到的消息根据类别等来处理了,下面用到了短信通知(阿里云短信服务),微信公众号订阅用户回复消息不会被通知,只能自己去后台看

package cn.ihsuzi.weixin;
import org.dom4j.Document;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
import com.aliyuncs.DefaultAcsClient;
import com.aliyuncs.IAcsClient;
import com.aliyuncs.dysmsapi.model.v20170525.SendSmsRequest;
import com.aliyuncs.dysmsapi.model.v20170525.SendSmsResponse;
import com.aliyuncs.http.MethodType;
import com.aliyuncs.profile.DefaultProfile;
import com.aliyuncs.profile.IClientProfile;
import javax.servlet.http.HttpServletRequest;
import java.io.InputStream;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* 消息处理工具类 Created by xdp on 2016/1/26.
*/
public class MessageHandlerUtil
{

    /*
     * 电话号码
     */
    private static final String PURPLE_TEL_NUMBER= "xxxx";
    private static final String SUZI_TEL_NUMBER= "xxxxx";

    /*
     * 特权码
     */
    private static final String LINSHI = "1314520001";
    private static final String LATIAO = "1314520002";
    private static final String SONG = "1314520003";
    private static final String GOLD = "1314520004";
    private static final String WHITE_GOLD = "1314520005";
    private static final String COLD_CLEAN = "1314520006";
    private static final String DIAMOND = "1314520";
    private static final String LIWU = "礼物";

    /*
     * 请求次数
     */
    private static int linshi = 0;
    private static int latiao = 0;
    private static int song = 0;
    private static int gold = 0;
    private static int white_gold = 0;
    private static int cold_clean = 0;
    private static int diamond = 0;
    private static int liwu = 0;
    /**
     * 解析微信发来的请求(XML)
     *
     * @param request
     * @return map
     * @throws Exception
     */
    public static Map<String, String> parseXml(HttpServletRequest request)
            throws Exception
    {
        // 将解析结果存储在HashMap中
        Map<String, String> map = new HashMap();
        // 从request中取得输入流
        InputStream inputStream = request.getInputStream();
        System.out.println("获取输入流");
        // 读取输入流
        SAXReader reader = new SAXReader();
        Document document = reader.read(inputStream);
        // 得到xml根元素
        Element root = document.getRootElement();
        // 得到根元素的所有子节点
        List<Element> elementList = root.elements();
        // 遍历所有子节点
        for (Element e : elementList)
        {
            System.out.println(e.getName() + "|" + e.getText());
            map.put(e.getName(), e.getText());
        }
        // 释放资源
        inputStream.close();
        inputStream = null;
        return map;
    }
    // 根据消息类型 构造返回消息
    public static String buildXml(Map<String, String> map)
    {
        String result;
        String msgType = map.get("MsgType").toString();
        System.out.println("MsgType:" + msgType);
        if (msgType.toUpperCase().equals("TEXT"))
        {

            /*
             * 根据内容的不同回复不同的内容
             */
            String content = map.get("Content");
            switch (content)
            {
                case LINSHI :
                    if (linshi == 0)
                    {
                        result = buildTextMessage(map, "兑换成功,想要什么零食,可以回复具体的要求哦~~/::*/::*");
                        sendSMS(LINSHI, "零食即将送达~~",PURPLE_TEL_NUMBER);
                        sendSMS(LINSHI, "零食特权券兑换了!!!",SUZI_TEL_NUMBER);
                    }else {
                        result = buildTextMessage(map, "已经兑换了哦~~/:pig/:pig");
                    }
                    linshi ++;
                    break;
                case LATIAO :
                    if (latiao == 0)
                    {
                        result = buildTextMessage(map, "辣条兑换成功,可以回复具体的要求~~/::*");
                        sendSMS(LATIAO, "辣条即将送达~~",PURPLE_TEL_NUMBER);
                        sendSMS(LATIAO, "辣条特权券兑换了!!!",SUZI_TEL_NUMBER);
                    }else {
                        result = buildTextMessage(map, "已经兑换了哦~~/:pig/:pig");
                    }
                    latiao ++;    
                    break;
                case SONG :
                    if (song == 0)
                    {
                        result = buildTextMessage(map, "点击下面链接:\nhttp://www.jiangsaixian.cn \n/::*/::*";);
                    }else {
                        result = buildTextMessage(map, "已经兑换了哦~~/:pig/:pig");
                    }
                    song ++;
                    break;
                case GOLD :
                    if (gold == 0)
                    {
                        result = buildTextMessage(map, "【黄金特权券】兑换成功,可以实现一个小小的要求或行使其他特权,请回复要行使的特权的内容~~/::*/::*");
                        sendSMS(GOLD, "请尽快行使特权~~",PURPLE_TEL_NUMBER);
                        sendSMS(GOLD, "黄金特权券!!!",SUZI_TEL_NUMBER);
                    }else {
                        result = buildTextMessage(map, "已经兑换了哦~~/:pig/:pig");
                    }
                    gold ++;
                    break;
                case WHITE_GOLD :
                    if (white_gold == 0)
                    {
                        result = buildTextMessage(map, "【铂金特权券】兑换成功,可以实现一个大一点的要求或行使其他特权,请回复要行使的特权的内容~~/::*/::*");
                        sendSMS(WHITE_GOLD, "请尽快行使特权~~",PURPLE_TEL_NUMBER);
                        sendSMS(WHITE_GOLD, "铂金特权券!!!",SUZI_TEL_NUMBER);
                    }else {
                        result = buildTextMessage(map, "已经兑换了哦~~/:pig/:pig");
                    }
                    white_gold ++;
                    break;
                case COLD_CLEAN :
                    if (cold_clean == 0)
                    {
                        result = buildTextMessage(map, "兑换成功,/:li马上消除一切矛盾和不愉快!!/::*/::*/:heart/:heart");
                        sendSMS(GOLD, "马上消除一切矛盾和不愉快!!",PURPLE_TEL_NUMBER);
                        sendSMS(GOLD, "矛盾消除券!!!",SUZI_TEL_NUMBER);
                    }else {
                        result = buildTextMessage(map, "已经兑换了哦~~/:pig/:pig");
                    }
                    gold ++;
                    break;
                case DIAMOND :
                    if (diamond == 0)
                    {
                        result = buildTextMessage(map, "【钻石特权券】兑换成功!!可以实现任何要求,任何要求!\n请回复要行使的特权的内容~~");
                        sendSMS(DIAMOND, "请尽快行使特权~~",PURPLE_TEL_NUMBER);
                        sendSMS(DIAMOND, "钻石特权券!!!",SUZI_TEL_NUMBER);
                    }else {
                        result = buildTextMessage(map, "已经兑换了哦~~/:pig/:pig");
                    }
                    diamond ++;
                    break;

                case LIWU :
                    if (liwu <= 10)
                    {
                        result = buildTextMessage(map, "直接回复兑换券或特权券号码,兑换礼物~~/::*/::*\n(ps1:其中特权券用于行使一次特权,可以是实现一个要求,或者其他)\n(ps2:一经兑换,就此作废,请慎用)");
                    }else {
                        result = buildTextMessage(map, ":pig");
                    }
                    liwu ++;
                    break;

                case "我爱你":
                    result = buildTextMessage(map, "我也爱你");
                    break;

                case "傻逼":
                    result = buildTextMessage(map, "哼哼");
                    break;
                default :
                    if (content.contains("傻") || content.contains("混蛋") ||content.contains("哼") ||content.contains("蠢"))
                    {
                        result = buildTextMessage(map, "哼哼");
                    }else {
                        result = "success";
                    }
                    break;
            }


        } else if (msgType.toUpperCase().equals("EVENT"))
        {
            // 如果是被关注就回复欢迎消息
            result = buildTextMessage(map, "欢迎关注~~/:rose/:rose");
        } else
        {
            // 如果是其他类型的就不回复。设置为空字符串
            result = buildTextMessage(map, "/:rose");
        }
        return result;
    }
    /**
     * 构造文本消息
     *
     * @param map
     * @param content
     * @return
     */
    private static String buildTextMessage(Map<String, String> map,
            String content)
    {
        // 发送方帐号
        String fromUserName = map.get("FromUserName");
        // 开发者微信号
        String toUserName = map.get("ToUserName");
        /**
         * 文本消息XML数据格式 <xml> <ToUserName><![CDATA[toUser]]></ToUserName>
         * <FromUserName><![CDATA[fromUser]]></FromUserName>
         * <CreateTime>1348831860</CreateTime>
         * <MsgType><![CDATA[text]]></MsgType> <Content><![CDATA[this is a
         * test]]></Content> <MsgId>1234567890123456</MsgId> </xml>
         */
        return String.format("<xml>"
                + "<ToUserName><![CDATA[%s]]></ToUserName>"
                + "<FromUserName><![CDATA[%s]]></FromUserName>"
                + "<CreateTime>%s</CreateTime>"
                + "<MsgType><![CDATA[text]]></MsgType>"
                + "<Content><![CDATA[%s]]></Content>" + "</xml>", fromUserName,
                toUserName, getUtcTime(), content);
    }
    private static String getUtcTime()
    {
        Date dt = new Date();// 如果不需要格式,可直接用dt,dt就是当前系统时间
        DateFormat df = new SimpleDateFormat("yyyyMMddhhmm");// 设置显示格式
        String nowTime = df.format(dt);
        long dd = (long) 0;
        try
        {
            dd = df.parse(nowTime).getTime();
        } catch (Exception e)
        {
        }
        return String.valueOf(dd);
    }
    /**
     * 发送短信
     *
     * @param code
     * @param content
     */
    private static void sendSMS(String code, String content,String tel)
    {
        try
        {
            // 设置超时时间-可自行调整
            System.setProperty("sun.net.client.defaultConnectTimeout", "10000");
            System.setProperty("sun.net.client.defaultReadTimeout", "10000");
            // 初始化ascClient需要的几个参数
            final String product = "Dysmsapi";// 短信API产品名称(短信产品名固定,无需修改)
            final String domain = "dysmsapi.aliyuncs.com";// 短信API产品域名(接口地址固定,无需修改)
            // 替换成你的AK
            final String accessKeyId = "XXXXX";// 你的accessKeyId,参考本文档步骤2
            final String accessKeySecret = "XXXXXXX";// 你的accessKeySecret,参考本文档步骤2
            // 初始化ascClient,暂时不支持多region(请勿修改)
            IClientProfile profile = DefaultProfile.getProfile("cn-hangzhou",
                    accessKeyId, accessKeySecret);
            DefaultProfile.addEndpoint("cn-hangzhou", "cn-hangzhou", product,
                    domain);
            IAcsClient acsClient = new DefaultAcsClient(profile);
            // 组装请求对象
            SendSmsRequest request = new SendSmsRequest();
            // 使用post提交
            request.setMethod(MethodType.POST);
            // 必填:待发送手机号。支持以逗号分隔的形式进行批量调用,批量上限为1000个手机号码,批量调用相对于单条调用及时性稍有延迟,验证码类型的短信推荐使用单条调用的方式
            request.setPhoneNumbers(tel);
            // 必填:短信签名-可在短信控制台中找到
            request.setSignName("筱屋枫林");
            // 必填:短信模板-可在短信控制台中找到
            request.setTemplateCode("SMS_11XXXX4");
            // 可选:模板中的变量替换JSON串,如模板内容为"亲爱的${name},您的验证码为${code}"时,此处的值为
            // 友情提示:如果JSON中需要带换行符,请参照标准的JSON协议对换行符的要求,比如短信内容中包含\r\n的情况在JSON中需要表示成\\r\\n,否则会导致JSON在服务端解析失败
            String SMSContentString = "{\"code\":\"" + code + "\", \"content\":\"" +content+ "\"}";
            request.setTemplateParam(SMSContentString);
            // 可选-上行短信扩展码(扩展码字段控制在7位或以下,无特殊需求用户请忽略此字段)
            // request.setSmsUpExtendCode("90997");
            // 可选:outId为提供给业务方扩展字段,最终在短信回执消息中将此值带回给调用者
            request.setOutId("yourOutId");
            // 请求失败这里会抛ClientException异常
            SendSmsResponse sendSmsResponse = acsClient.getAcsResponse(request);
            if (sendSmsResponse.getCode() != null
                    && sendSmsResponse.getCode().equals("OK"))
            {
                System.out.println("短信发送成功!!!");
            }
        } catch (Exception e)
        {

        }
    }
}

如果不想回复,就直接回“success”,注意是将整个xml替换成“success”,而如果不做处理的话会在手机端报错,提示服务器出了问题

微信默认表情符号的代码对照表:
http://www.360doc.com/content/15/1105/11/15398874_510889274.shtml