java微信开发

原创 2017年04月21日 14:22:12

再一次接入微信开放平台,只是此次以消息为主,不涉及支付,如需看微信支付,请参考另一篇博文。

此篇博文涉及了微信公众号开发的基础功能部分,包括了服务器校验、获取access_token 、创建自定义菜单,接收微信公众平台的消息,并回复消息,主动推送模版消息等,在此基础上,可以按需回复或者推送各类消息。代码注释比较全,所以直接上代码:


首先是controller(实际应用中很多功能入口不是controller,此处是做测试而已),其中/wechat/index的get方法是做服务器验证,post方法是接收微信服务器发送过来的消息


package com.dnkx.controller.wechat;


import com.dnkx.bo.WeChatTextMessageBO;
import com.dnkx.util.WechatUtil;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.SystemUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.net.URLEncoder;
import java.util.Map;

/**
 * Created by 李小拐 on 2017/4/18.
 */
@Controller
@RequestMapping("/wechat")
public class WeChatController {
    private final static Logger logger= LoggerFactory.getLogger(WeChatController.class);
    @Resource
    private HttpServletRequest request;
    @Value("${appId}")
    private String appId;
    @Value("${appSecret}")
    private String appSecret;

    /**
     * 服务器签名验证
     */
    @ResponseBody
    @RequestMapping(value = "/index",method = RequestMethod.GET)
    public String checkSignature(){
        try {
            // 接收微信服务器以Get请求发送的4个参数
            String signature = request.getParameter("signature");
            String timestamp = request.getParameter("timestamp");
            String nonce = request.getParameter("nonce");
            String echoStr = request.getParameter("echostr");

            if (WechatUtil.checkSignature(signature,timestamp,nonce)){
                logger.info("echoStr:{}",echoStr);
                return echoStr;
            }
        }catch (Exception e){
            logger.error(e.getMessage(),e);
        }
        return "";
    }


    /**
     * 接收微信消息
     */
    @ResponseBody
    @RequestMapping(value = "/index",method = RequestMethod.POST,produces = "text/xml; charset=utf-8")
    public String receiveMessage(){
        String message = null;
        try {
            request.setCharacterEncoding("utf-8");
            Map<String, String> map = WechatUtil.xmlToMap(request.getInputStream());
            String toUserName = map.get("ToUserName");//微信公众号
            String fromUserName = map.get("FromUserName");//openid
            String msgType = map.get("MsgType");//接收到消息类型
            String MsgId = map.get("MsgId");//消息id,64位整型

            //消息类型:text(文本消息)、image(图片消息)、voice(语音)、
            // video(视频)、shortvideo(小视频)、location(位置信息)、link(链接)、event(事件)
            switch (msgType){
                case "text":
                    //接收到的消息内容(文本消息特有字段)
                    String content = map.get("Content");

                    break;
                case "event":
                    //事件类型:subscribe(关注)、unsubscribe(取消关注)、SCAN(扫描)、LOCATION(上报位置)、
                    // CLICK(点击自定义菜单拉取消息)、VIEW(点击自定义菜单跳转链接)
                    String event = map.get("Event");
                    switch (event){
                        case "subscribe":
                            //关注后的推送欢迎消息
                            WeChatTextMessageBO bo=new WeChatTextMessageBO();
                            bo.setToUserName(fromUserName);
                            bo.setFromUserName(toUserName);
                            bo.setCreateTime(System.currentTimeMillis());
                            bo.setContent("欢迎关注小妮驿站,我们将竭诚为您服务!");
                            message=WechatUtil.BeanToXml(bo);
                            break;
                        case "unsubscribe":
                            //取消关注后的相关操作
                            logger.info("{}取消了关注",fromUserName);
                            break;
                        case "CLICK":
                            //获取事件的key(可以根据key值做后续操作)
                            String eventKey = map.get("EventKey");

                            break;
                        case "VIEW":
                            //获取事件KEY值,设置的跳转URL
                            String eventKey2 = map.get("EventKey");
                            logger.info("{}点击了view:{}",fromUserName,eventKey2);
                            break;
                        case "TEMPLATESENDJOBFINISH"://模版消息推送状态
                            logger.info("模版消息推送给 {} 的结果:{}",fromUserName,map.get("Status"));
                            break;
                        default:
                            break;
                    }
                    break;
                default:
                    break;
            }
            // 将回应发送给微信服务器
            if (message==null)message="success";
        }catch (Exception e){
            logger.error(e.getMessage(),e);
        }
        return message;
    }

     /**
     * 获取access_token
     */
    @RequestMapping(value = "/getAccessToken",method = RequestMethod.GET)
    public void getAccessToken(){
        WechatUtil.getAccessToken();
    }

    /**
     * 创建自定义菜单
     * @return
     * @throws Exception
     */
    @ResponseBody
    @RequestMapping(value = "/createMenu",method = RequestMethod.GET)
    public String createMenu() throws Exception{
        String redirectUri=URLEncoder.encode("http://test-admin.xxxx.cn/wechat/redirect","utf-8");
        String url="https://open.weixin.qq.com/connect/oauth2/authorize?appid="+appId+"&redirect_uri="+redirectUri+"&response_type=code&scope=snsapi_base&state=dnkx#wechat_redirect";
        String json="{\"button\":[{\"type\":\"view\",\"name\":\"查询快递\",\"url\":\"http://www.xxxx.cn/\"},{\"name\":\"会员中心\",\"sub_button\":[{\"type\":\"view\",\"name\":\"会员福利\",\"url\":\"http://www.xxxx.cn/b2b/h5/vip.html\"},{\"type\":\"view\",\"name\":\"绑定手机\",\"url\":\""+url+"\"}]},{\"name\":\"关于我们\",\"sub_button\":[{\"type\":\"view\",\"name\":\"关于我们\",\"url\":\"http://xxxxx.cn/weixin/h5/aboutUs.html\"},{\"type\":\"view\",\"name\":\"app下载\",\"url\":\"http://a.app.qq.com/o/simple.jsp?pkgname=com.happy.activity\"},{\"type\":\"view\",\"name\":\"公司招聘\",\"url\":\"http://xxxx.cn/weixin/h5/employ.html\"}]}]}";
        if (WechatUtil.createMenu(json)){
            return "create menu success";
        }else {
            return "create menu fail";
        }
    }

    /**
     * 获取网页授权(此处即网页授权第1步里填写回调URL地址)
     * @return
     */
    @ResponseBody
    @RequestMapping(value = "/redirect")
    public String redirect(){
        String code = request.getParameter("code");
        if (StringUtils.isBlank(code)){
            return "fail";
        }
        Map map=WechatUtil.getAccessTokenByCode(code);
        if (map!=null&&map.containsKey("access_token")){
            String openId=map.get("openid").toString();//获取到的openId
//            String accessToken=map.get("access_token").toString();//获取到的accessToken
            request.setAttribute("openId",openId);
            return openId;
        }
        return "fail";
    }

    /**
     * 推送模版消息
     */
    @ResponseBody
    @RequestMapping(value = "/sendMessage")
    public String sendMessage(){
        WechatUtil.sendMessage();
        return "finish";
    }
}



然后是主要的工具类:

package com.dnkx.util;

import com.dnkx.common.bo.HttpExecResponse;
import com.dnkx.common.http.HttpClientExecutor;
import com.dnkx.common.utils.StrUtils;
import com.thoughtworks.xstream.XStream;
import org.apache.commons.codec.digest.DigestUtils;

import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.ByteArrayEntity;
import org.dom4j.Document;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import java.io.InputStream;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.StampedLock;


/**
 * Created by 李小拐 on 2017/4/18.
 */
@Component
public class WechatUtil {
    private final static Logger logger= LoggerFactory.getLogger(WechatUtil.class);
    private final static String accessToken="WECHAT_ACCESS_TOKEN";

    @Resource
    private RedisTemplate tokenRedisTemplate;
    @Value("${token}")
    private String token;
    @Value("${appId}")
    private String appId;
    @Value("${appSecret}")
    private String appSecret;

    private static WechatUtil wechatUtil;
    @PostConstruct
    public void init() {
        wechatUtil = this;
        wechatUtil.token=this.token;
        wechatUtil.appId=this.appId;
        wechatUtil.appSecret=this.appSecret;
        wechatUtil.tokenRedisTemplate = this.tokenRedisTemplate;
    }

    /**
     * 签名验证
     * @param signature
     * @param timestamp
     * @param nonce
     * @return
     */
    public static boolean checkSignature(String signature,String timestamp,String nonce){
        String[] arr = new String[] { wechatUtil.token, timestamp, nonce };
        // 排序
        Arrays.sort(arr);
        // 生成字符串
        StringBuilder content = new StringBuilder();
        for (int i = 0; i < arr.length; i++) {
            content.append(arr[i]);
        }
        // sha1加密
        String temp = DigestUtils.sha1Hex(content.toString());
        logger.info("接收到的signature:{}",signature);
        logger.info("参数签名后的signature:{}",temp);
        return temp.equals(signature); // 与微信传递过来的签名进行比较
    }

    /**
     * 获取AccessToken
     * @return
     */
    public static void getAccessToken(){
        try {
            if (!wechatUtil.tokenRedisTemplate.hasKey(accessToken)) {
                String url = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=" + wechatUtil.appId + "&secret=" + wechatUtil.appSecret;
                HttpGet httpGet = new HttpGet(url);
                HttpExecResponse executeResult = HttpClientExecutor.execute(httpGet);
                if (null != executeResult && executeResult.isSuccess()) {
                    String result = executeResult.getData();
                    logger.info("get AccessToken result:{}", result);
                    Map map = StrUtils.fromJson(result, Map.class);
                    if (map.containsKey("access_token")) {
                        wechatUtil.tokenRedisTemplate.opsForValue().set(accessToken, map.get("access_token").toString(), 7000, TimeUnit.SECONDS);
                    }
                }
            }
        }catch (Exception e){
            logger.error("getAccessToken error",e);
        }
    }

    /**
     * 创建自定义菜单
     * @param jsonContent json格式字符串,具体请参照开发文档
     * @return
     */
    public static boolean createMenu(String jsonContent){
        if (!wechatUtil.tokenRedisTemplate.hasKey(accessToken)){
            WechatUtil.getAccessToken();
        }
        String url="https://api.weixin.qq.com/cgi-bin/menu/create?access_token="+wechatUtil.tokenRedisTemplate.opsForValue().get(accessToken);
        try {
            HttpPost httpPost = new HttpPost(url);
            httpPost.setEntity(new ByteArrayEntity(jsonContent.getBytes()));
            HttpExecResponse executeResult = HttpClientExecutor.execute(httpPost, 1000, 2000);
            if (null != executeResult && executeResult.isSuccess()) {
                String result = executeResult.getData();
                logger.info("createMenu result:{}",result);
                Map map=StrUtils.fromJson(result,Map.class);
                if (map.get("errmsg").toString().equals("ok")){
                    return true;
                }
            }
        }catch (Exception e){
            logger.error("createMenu error",e);
        }
        return false;
    }

    /**
     * 网页授权:通过code换取网页授权access_token和用户openId
     * @param code
     * @return
     */
    public static Map getAccessTokenByCode(String code){
        String url = "https://api.weixin.qq.com/sns/oauth2/access_token?appid="+wechatUtil.appId+"&secret="+wechatUtil.appSecret+"&code="+code+"&grant_type=authorization_code";
        HttpGet httpGet = new HttpGet(url);
        HttpExecResponse executeResult = HttpClientExecutor.execute(httpGet);
        if (null != executeResult && executeResult.isSuccess()) {
            String result = executeResult.getData();
            Map map = StrUtils.fromJson(result, Map.class);
            return map;
        }
        return null;
    }

    /**
     * 发送模版消息
     * @return
     */
    public static boolean sendMessage(){
        if (!wechatUtil.tokenRedisTemplate.hasKey(accessToken)){
            WechatUtil.getAccessToken();
        }
        String url="https://api.weixin.qq.com/cgi-bin/message/template/send?access_token="+wechatUtil.tokenRedisTemplate.opsForValue().get(accessToken);
        String content="{\"touser\":\"o94Cev7D_e6vzjZ-qzZfUHAUFst4\",\"template_id\":\"4JkY9NUvF8tXI7fAPfTXUUnCE-C6AD4KhgQqEymqgEI\",\"data\":{\"first\":{\"value\":\"恭喜你购买成功!\",\"color\":\"#173177\"}}}";
        try {
            HttpPost httpPost = new HttpPost(url);
            httpPost.setEntity(new ByteArrayEntity(content.getBytes()));
            HttpExecResponse executeResult = HttpClientExecutor.execute(httpPost, 1000, 2000);
            if (null != executeResult && executeResult.isSuccess()) {
                String result = executeResult.getData();
                Map map=StrUtils.fromJson(result,Map.class);
                if (map.get("errmsg").toString().equals("ok")){
                    return true;
                }
            }
        }catch (Exception e){
            logger.error("sendMessage error",e);
        }
        return false;
    }

    /**
     * xml转map
     * @param ins
     * @return
     * @throws Exception
     */
    public static Map xmlToMap(InputStream ins) throws Exception{
        Map<String, String> map = new HashMap<>();
        SAXReader reader = new SAXReader();            // 使用dom4j解析xml
        Document doc = reader.read(ins);
        Element root = doc.getRootElement();         // 获取根元素
        List<Element> list = root.elements();        // 获取所有节点
        for (Element e : list) {
            map.put(e.getName(), e.getText());
            //logger.info(e.getName() + ":" + e.getText());
        }
        logger.info("{}",map);
        return map;
    }

    /**
     * Bean转xml
     * @param t
     * @param <T>
     * @return
     */
    public static <T> String BeanToXml(T t){
        try {
            XStream xstream = new XStream();
            xstream.alias("xml", t.getClass());
            String xml = xstream.toXML(t);
            return xml;
        }catch (Exception e){
            logger.error(e.getMessage(),e);
            return null;
        }
    }
}

下面是回复文本消息用到的bo对象,如果需要回复其他类型的消息,构建相应bo对象就行了:


package com.dnkx.bo;

/**
 * Created by 李小拐 on 2017/4/20.
 * 回复微信消息
 */
public class WeChatBaseMessageBO {
    private String ToUserName;
    private String FromUserName;
    private Long CreateTime;
    private String MsgType;

    public String getToUserName() {
        return ToUserName;
    }

    public void setToUserName(String toUserName) {
        ToUserName = toUserName;
    }

    public String getFromUserName() {
        return FromUserName;
    }

    public void setFromUserName(String fromUserName) {
        FromUserName = fromUserName;
    }

    public Long getCreateTime() {
        return CreateTime;
    }

    public void setCreateTime(Long createTime) {
        CreateTime = createTime;
    }

    public String getMsgType() {
        return MsgType;
    }

    public void setMsgType(String msgType) {
        MsgType = msgType;
    }
}

package com.dnkx.bo;

/**
 * Created by 李小拐 on 2017/4/20.
 */
public class WeChatTextMessageBO extends WeChatBaseMessageBO{

    public WeChatTextMessageBO(){
        this.setMsgType("text");
    }

    private String Content;

    public String getContent() {
        return Content;
    }

    public void setContent(String content) {
        Content = content;
    }
}

基本功能都在代码里了,只是测试代码,但都亲测通过,基于此代码加上实际需要的业务逻辑就行了(我接下来是基于此做模版消息推送,自动回复,和客服消息)。

最后,仅供参考!仅供参考!仅供参考!


版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/ytTea/article/details/70314162

Kingofark的人工智能启示录

K ][ N G of A R K ™s The Revelation Of AIK ][ N G of A R K ™的人工智能启示录Whats It Mean to Be Human, Anywa...
  • kingofark
  • kingofark
  • 2003-10-27 02:08:00
  • 5301

JAVA微信开发-新手接入指南

相信很多人对微信开发已经不那么陌生,我也是从一个微信开发的菜鸟经过各种问题的折磨,然后去搜索引擎搜索各种文章阅读,但是基本都是零散的资料,没有一个统一、系统的阐述微信应用如何开发。作者结合自己的实际开...
  • postfxj
  • postfxj
  • 2016-10-05 10:00:53
  • 1494

Java微信公众号开发(附源码!!!)

本文中采用原生servlet和jdbc实现微信公众号开发,附源码,源码链接在文章最后。...
  • fanguoddd
  • fanguoddd
  • 2017-02-03 15:20:21
  • 30794

Java微信jar

  • 2017年07月26日 18:29
  • 9.06MB
  • 下载

微信登录开发-java

微信登录: 1、申请微信开放平台,获取微信登录接口 2、测试登录接口是否正常 redirect_uri重定向地址(微信申请的http://+‘授权回调域’),需要进行UrlEncode。 https:...
  • shashaquanquan
  • shashaquanquan
  • 2015-11-17 17:01:47
  • 4404

java版微信开发全套

  • 2017年11月17日 14:18
  • 7.62MB
  • 下载

java 微信搭建实例

java web 项目 使用微信 实例: 1.准备条件 一个微信开发者账号,如果你没有,请注册一个(如何注册,请百度) 注册后 申请开发者 ,找到开发者中心。 记录下面的AppID和 AppSecre...
  • u013929107
  • u013929107
  • 2015-11-23 15:39:25
  • 3567

微信java通用版

  • 2013年01月05日 16:29
  • 845KB
  • 下载

Java实现微信授权登录

这篇文章出了介绍微信授权登录之外,还简单介绍QQ登录的相关流程。 首先要开发微信授权登录操作,必须有一个微信公众平台注册一个账号,具体网址在微信公众平台。在这里仅对于个人开发者而言,订阅号应该是个人开...
  • StarDreamWorkStation
  • StarDreamWorkStation
  • 2017-04-13 16:07:33
  • 7283

java 微信接入 关注和取消关注

//最基础的servlet 进行接收package com.cq.wechat; import java.io.IOException; import java.io.PrintWriter; im...
  • textalign
  • textalign
  • 2017-03-04 15:31:01
  • 1519
收藏助手
不良信息举报
您举报文章:java微信开发
举报原因:
原因补充:

(最多只允许输入30个字)