现在的位置: 首页 > 微信开发 > 正文

java微信二次第三方开发,token认证,消息事件接收,图文素材库,自定义菜单等功能

2016年04月19日 微信开发 ⁄ 共 29221字 ⁄ 字号 评论关闭

 

来进行微信第三方开发,

微信二次开发,官方还是网上有很多介绍了,这里就不在进行讲述了 直接上干货。
\

 

首先 与微信对接,服务器配置,需要80端口和443端口开放的服务器,这里推荐 使用 python 的pagekite,一款反向代理的工具,具体安装百度搜,提供下配置放方法:http://jingyan.baidu.com/article/0eb457e52ca0af03f0a90568.html

配置好了之后,使用maven 或者 tomcat 等中间件 启动咱们的项目,访问之前注册的 pagekite地址,试一下,正常访问就进行下一步,

 

一、服务器配置认证

首先根据官方文档得知,

1)将token、timestamp、nonce三个参数进行字典序排序

2)将三个参数字符串拼接成一个字符串进行sha1加密

3)开发者获得加密后的字符串可与signature对比,标识该请求来源于微信

 

SHA1.java

package com.wx.util;

/*
 * 微信公众平台(JAVA) SDK
 *
 * Copyright (c) 2014, Ansitech Network Technology Co.,Ltd All rights reserved.
 * http://www.ansitech.com/weixin/sdk/
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
import java.security.MessageDigest;

/**
 * 

* Title: SHA1算法 *

* */ public final class SHA1 { private static final char[] HEX_DIGITS = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' }; /** * Takes the raw bytes from the digest and formats them correct. * * @param bytes * the raw bytes from the digest. * @return the formatted bytes. */ private static String getFormattedText(byte[] bytes) { int len = bytes.length; StringBuilder buf = new StringBuilder(len * 2); // 把密文转换成十六进制的字符串形式 for (int j = 0; j < len; j++) { buf.append(HEX_DIGITS[(bytes[j] >> 4) & 0x0f]); buf.append(HEX_DIGITS[bytes[j] & 0x0f]); } return buf.toString(); } public static String encode(String str) { if (str == null) { return null; } try { MessageDigest messageDigest = MessageDigest.getInstance("SHA1"); messageDigest.update(str.getBytes()); return getFormattedText(messageDigest.digest()); } catch (Exception e) { throw new RuntimeException(e); } } }

然后编写controller 来接收 微信的 认证请求

WxManagerController.java

package com.controller;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;

import com.wx.util.SHA1;


@Controller 
@RequestMapping( "/wx" )
public class WxManagerController {
	
	private String Token = "testfortoken";
	
	@RequestMapping(value = "", method = { RequestMethod.GET, RequestMethod.POST })
	@ResponseBody
	public void load(Model model, HttpServletRequest request, HttpServletResponse response) {
		//判断访问方式
		boolean isGet = request.getMethod().toLowerCase().equals("get");
		if (isGet) {
			//进行认证
			access(request, response);
		} else {
			//处理微信post请求
		}
	}
	
	/**
	 * 验证URL真实性
	 * 
	 * @param request
	 * @param response
	 * @return String
	 */
	private String access(HttpServletRequest request, HttpServletResponse response) {
		// 验证URL真实性
		String signature = request.getParameter("signature");// 微信加密签名
		String timestamp = request.getParameter("timestamp");// 时间戳
		String nonce = request.getParameter("nonce");// 随机数
		String echostr = request.getParameter("echostr");// 随机字符串
		List params = new ArrayList();
		params.add(Token);
		params.add(timestamp);
		params.add(nonce);
		// 1. 将token、timestamp、nonce三个参数进行字典序排序
		Collections.sort(params, new Comparator() {
			@Override
			public int compare(String o1, String o2) {
				return o1.compareTo(o2);
			}
		});
		// 2. 将三个参数字符串拼接成一个字符串进行sha1加密
		String temp = SHA1.encode(params.get(0) + params.get(1) + params.get(2));
		if (temp.equals(signature)) {
			try {
				response.getWriter().write(echostr);
				return echostr;
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
		return null;
	}

}

OK,到微信公众号后台,开发-基本配置-修改配置

\
token是项目里自己填写的参数,一致即可,

EncodingAESKey 随机生成即可,

点击提交。、

\
成功!

下面做一些功能,用户消息处理以及事件处理,

二、消息,事件处理

根据官方文档得知,处理微信的post请求,微信是以xml格式发送的,所以要解析xml并做处理,这里使用XStream

maven 使用的 oschina库 搜不到 xstream-1.4.8 版本 手动下载了一个 下载链接:http://pan.baidu.com/s/1dEAdDVb

其他的maven 可直接pom里配置。

pom.xml


		xstream
		xstream
		1.4.8
		system
		{project.basedir}/WebContent/WEB-INF/lib/xstream-1.4.8.jar
	
	
		xmlpull
		xmlpull
		1.1.3.1
	
	
		xpp3
		xpp3
		1.1.4c
	

 

xstream 下的 systemPath 填写项目里对应的jar位置。

开始编写代码

加入 CDATA 验证创建的@interface类

XStreamCDATA.java

package com.penuel.mythopoet.wx.manager;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.FIELD })
public @interface XStreamCDATA {

}

XStream工具类

SerializeXmlUtil.java
package com.wx.util;

import java.io.Writer;
import java.lang.reflect.Field;

import com.thoughtworks.xstream.XStream;
import com.thoughtworks.xstream.annotations.XStreamAlias;
import com.thoughtworks.xstream.core.util.QuickWriter;
import com.thoughtworks.xstream.io.HierarchicalStreamWriter;
import com.thoughtworks.xstream.io.xml.PrettyPrintWriter;
import com.thoughtworks.xstream.io.xml.XppDriver;

/**
 * xml 转换工具类
 */
public class SerializeXmlUtil {

	public static XStream createXstream() {
		return new XStream(new XppDriver() {
			@Override
			public HierarchicalStreamWriter createWriter(Writer out) {
				return new PrettyPrintWriter(out) {
					boolean cdata = false;
					Class targetClass = null;

					@Override
					public void startNode(String name, @SuppressWarnings("rawtypes") Class clazz) {
						super.startNode(name, clazz);
						// 业务处理,对于用XStreamCDATA标记的Field,需要加上CDATA标签
						if (!name.equals("xml")) {
							cdata = needCDATA(targetClass, name);
						} else {
							targetClass = clazz;
						}
					}

					@Override
					protected void writeText(QuickWriter writer, String text) {
						if (cdata) {
							writer.write("");
						} else {
							writer.write(text);
						}
					}
				};
			}
		});
	}

	private static boolean needCDATA(Class targetClass, String fieldAlias) {
		boolean cdata = false;
		// first, scan self
		cdata = existsCDATA(targetClass, fieldAlias);
		if (cdata)
			return cdata;
		// if cdata is false, scan supperClass until java.lang.Object
		Class superClass = targetClass.getSuperclass();
		while (!superClass.equals(Object.class)) {
			cdata = existsCDATA(superClass, fieldAlias);
			if (cdata)
				return cdata;
			superClass = superClass.getClass().getSuperclass();
		}
		return false;
	}

	private static boolean existsCDATA(Class clazz, String fieldAlias) {
		if ("MediaId".equals(fieldAlias)) {
			return true; // 特例添加 morning99
		}
		// scan fields
		Field[] fields = clazz.getDeclaredFields();
		for (Field field : fields) {
			// 1. exists XStreamCDATA
			if (field.getAnnotation(XStreamCDATA.class) != null) {
				XStreamAlias xStreamAlias = field.getAnnotation(XStreamAlias.class);
				// 2. exists XStreamAlias
				if (null != xStreamAlias) {
					if (fieldAlias.equals(xStreamAlias.value()))// matched
						return true;
				} else {// not exists XStreamAlias
					if (fieldAlias.equals(field.getName()))
						return true;
				}
			}
		}
		return false;
	}

}

微信误参数字典

 WxErrorCode.java

package com.wx.util;

import java.util.HashMap;
import java.util.Map;

@SuppressWarnings("unchecked")
public class WxErrorCode {

	@SuppressWarnings("rawtypes")
	public static final Map ERRORCODE=new HashMap();
	
	static{
		ERRORCODE.put(-1,"系统繁忙");
		ERRORCODE.put(0,"请求成功");
		ERRORCODE.put(40001,"获取access_token时AppSecret错误,或者access_token无效");
		ERRORCODE.put(40002,"不合法的凭证类型");
		ERRORCODE.put(40003,"不合法的OpenID");
		ERRORCODE.put(40004,"不合法的媒体文件类型");
		ERRORCODE.put(40005,"不合法的文件类型");
		ERRORCODE.put(40006,"不合法的文件大小");
		ERRORCODE.put(40007,"不合法的媒体文件id");
		ERRORCODE.put(40008,"不合法的消息类型");
		ERRORCODE.put(40009,"不合法的图片文件大小");
		ERRORCODE.put(40010,"不合法的语音文件大小");
		ERRORCODE.put(40011,"不合法的视频文件大小");
		ERRORCODE.put(40012,"不合法的缩略图文件大小");
		ERRORCODE.put(40013,"不合法的APPID");
		ERRORCODE.put(40014,"不合法的access_token");
		ERRORCODE.put(40015,"不合法的菜单类型");
		ERRORCODE.put(40016,"不合法的按钮个数");
		ERRORCODE.put(40017,"不合法的按钮个数");
		ERRORCODE.put(40018,"不合法的按钮名字长度");
		ERRORCODE.put(40019,"不合法的按钮KEY长度");
		ERRORCODE.put(40020,"不合法的按钮URL长度");
		ERRORCODE.put(40021,"不合法的菜单版本号");
		ERRORCODE.put(40022,"不合法的子菜单级数");
		ERRORCODE.put(40023,"不合法的子菜单按钮个数");
		ERRORCODE.put(40024,"不合法的子菜单按钮类型");
		ERRORCODE.put(40025,"不合法的子菜单按钮名字长度");
		ERRORCODE.put(40026,"不合法的子菜单按钮KEY长度");
		ERRORCODE.put(40027,"不合法的子菜单按钮URL长度");
		ERRORCODE.put(40028,"不合法的自定义菜单使用用户");
		ERRORCODE.put(40029,"不合法的oauth_code");
		ERRORCODE.put(40030,"不合法的refresh_token");
		ERRORCODE.put(40031,"不合法的openid列表");
		ERRORCODE.put(40032,"不合法的openid列表长度");
		ERRORCODE.put(40033,"不合法的请求字符,不能包含\\uxxxx格式的字符");
		ERRORCODE.put(40035,"不合法的参数");
		ERRORCODE.put(40038,"不合法的请求格式");
		ERRORCODE.put(40039,"不合法的URL长度");
		ERRORCODE.put(40050,"不合法的分组id");
		ERRORCODE.put(40051,"分组名字不合法");
		ERRORCODE.put(41001,"缺少access_token参数");
		ERRORCODE.put(41002,"缺少appid参数");
		ERRORCODE.put(41003,"缺少refresh_token参数");
		ERRORCODE.put(41004,"缺少secret参数");
		ERRORCODE.put(41005,"缺少多媒体文件数据");
		ERRORCODE.put(41006,"缺少media_id参数");
		ERRORCODE.put(41007,"缺少子菜单数据");
		ERRORCODE.put(41008,"缺少oauth code");
		ERRORCODE.put(41009,"缺少openid");
		ERRORCODE.put(42001,"access_token超时");
		ERRORCODE.put(42002,"refresh_token超时");
		ERRORCODE.put(42003,"oauth_code超时");
		ERRORCODE.put(43001,"需要GET请求");
		ERRORCODE.put(43002,"需要POST请求");
		ERRORCODE.put(43003,"需要HTTPS请求");
		ERRORCODE.put(43004,"需要接收者关注");
		ERRORCODE.put(43005,"需要好友关系");
		ERRORCODE.put(44001,"多媒体文件为空");
		ERRORCODE.put(44002,"POST的数据包为空");
		ERRORCODE.put(44003,"图文消息内容为空");
		ERRORCODE.put(44004,"文本消息内容为空");
		ERRORCODE.put(45001,"多媒体文件大小超过限制");
		ERRORCODE.put(45002,"消息内容超过限制");
		ERRORCODE.put(45003,"标题字段超过限制");
		ERRORCODE.put(45004,"描述字段超过限制");
		ERRORCODE.put(45005,"链接字段超过限制");
		ERRORCODE.put(45006,"图片链接字段超过限制");
		ERRORCODE.put(45007,"语音播放时间超过限制");
		ERRORCODE.put(45008,"图文消息超过限制");
		ERRORCODE.put(45009,"接口调用超过限制");
		ERRORCODE.put(45010,"创建菜单个数超过限制");
		ERRORCODE.put(45015,"回复时间超过限制");
		ERRORCODE.put(45016,"系统分组,不允许修改");
		ERRORCODE.put(45017,"分组名字过长");
		ERRORCODE.put(45018,"分组数量超过上限");
		ERRORCODE.put(46001,"不存在媒体数据");
		ERRORCODE.put(46002,"不存在的菜单版本");
		ERRORCODE.put(46003,"不存在的菜单数据");
		ERRORCODE.put(46004,"不存在的用户");
		ERRORCODE.put(47001,"解析JSON/XML内容错误");
		ERRORCODE.put(48001,"api功能未授权");
		ERRORCODE.put(50001,"用户未授权该api");
	}

}

消息工具类

WxMessageUtil.java

package com.wx.util;


/**
 * 消息工具类
 *
 */
public class WxMessageUtil {
	/**
	 * 返回消息类型:文本
	 */
	public static final String RESP_MESSAGE_TYPE_TEXT = "text";

	/**
	 * 返回消息类型:音乐
	 */
	public static final String RESP_MESSAGE_TYPE_MUSIC = "music";

	/**
	 * 返回消息类型:图文
	 */
	public static final String RESP_MESSAGE_TYPE_NEWS = "news";

	/**
	 * 请求消息类型:文本
	 */
	public static final String REQ_MESSAGE_TYPE_TEXT = "text";

	/**
	 * 请求消息类型:图片
	 */
	public static final String REQ_MESSAGE_TYPE_IMAGE = "image";

	/**
	 * 请求消息类型:链接
	 */
	public static final String REQ_MESSAGE_TYPE_LINK = "link";

	/**
	 * 请求消息类型:地理位置
	 */
	public static final String REQ_MESSAGE_TYPE_LOCATION = "location";

	/**
	 * 请求消息类型:音频
	 */
	public static final String REQ_MESSAGE_TYPE_VOICE = "voice";

	/**
	 * 请求消息类型:推送
	 */
	public static final String REQ_MESSAGE_TYPE_EVENT = "event";

	/**
	 * 事件类型:subscribe(订阅)
	 */
	public static final String EVENT_TYPE_SUBSCRIBE = "subscribe";

	/**
	 * 事件类型:unsubscribe(取消订阅)
	 */
	public static final String EVENT_TYPE_UNSUBSCRIBE = "unsubscribe";

	/**
	 * 事件类型:CLICK(自定义菜单点击事件)
	 */
	public static final String EVENT_TYPE_CLICK = "CLICK";

}

微信POST的XML数据包转换为消息接受对象

InputMessage.java

package com.penuel.mythopoet.wx.manager;

/*
 * 微信公众平台(JAVA) SDK
 *
 * Copyright (c) 2014, Ansitech Network Technology Co.,Ltd All rights reserved.
 * http://www.ansitech.com/weixin/sdk/
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

import java.io.Serializable;

import com.thoughtworks.xstream.annotations.XStreamAlias;

/**
 * POST的XML数据包转换为消息接受对象
 * 
 * 

* 由于POST的是XML数据包,所以不确定为哪种接受消息,
* 所以直接将所有字段都进行转换,最后根据MsgType字段来判断取何种数据 *

* */ @XStreamAlias("xml") public class InputMessage implements Serializable { /** * */ private static final long serialVersionUID = 1L; @XStreamAlias("ToUserName") private String ToUserName; @XStreamAlias("FromUserName") private String FromUserName; @XStreamAlias("CreateTime") private Long CreateTime; @XStreamAlias("MsgType") private String MsgType = "text"; @XStreamAlias("MsgId") private Long MsgId; // 文本消息 @XStreamAlias("Content") private String Content; // 图片消息 @XStreamAlias("PicUrl") private String PicUrl; // 位置消息 @XStreamAlias("LocationX") private String LocationX; @XStreamAlias("LocationY") private String LocationY; @XStreamAlias("Scale") private Long Scale; @XStreamAlias("Label") private String Label; // 链接消息 @XStreamAlias("Title") private String Title; @XStreamAlias("Description") private String Description; @XStreamAlias("Url") private String URL; // 语音信息 @XStreamAlias("MediaId") private String MediaId; @XStreamAlias("Format") private String Format; @XStreamAlias("Recognition") private String Recognition; // 事件 @XStreamAlias("Event") private String Event; @XStreamAlias("EventKey") private String EventKey; @XStreamAlias("Ticket") private String Ticket; 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; } public Long getMsgId() { return MsgId; } public void setMsgId(Long msgId) { MsgId = msgId; } public String getContent() { return Content; } public void setContent(String content) { Content = content; } public String getPicUrl() { return PicUrl; } public void setPicUrl(String picUrl) { PicUrl = picUrl; } public String getLocationX() { return LocationX; } public void setLocationX(String locationX) { LocationX = locationX; } public String getLocationY() { return LocationY; } public void setLocationY(String locationY) { LocationY = locationY; } public Long getScale() { return Scale; } public void setScale(Long scale) { Scale = scale; } public String getLabel() { return Label; } public void setLabel(String label) { Label = label; } public String getTitle() { return Title; } public void setTitle(String title) { Title = title; } public String getDescription() { return Description; } public void setDescription(String description) { Description = description; } public String getURL() { return URL; } public void setURL(String uRL) { URL = uRL; } public String getEvent() { return Event; } public void setEvent(String event) { Event = event; } public String getEventKey() { return EventKey; } public void setEventKey(String eventKey) { EventKey = eventKey; } public String getMediaId() { return MediaId; } public void setMediaId(String mediaId) { MediaId = mediaId; } public String getFormat() { return Format; } public void setFormat(String format) { Format = format; } public String getRecognition() { return Recognition; } public void setRecognition(String recognition) { Recognition = recognition; } public String getTicket() { return Ticket; } public void setTicket(String ticket) { Ticket = ticket; } }

xml输出配置

OutputMessage.java

package com.wx.util;

import com.thoughtworks.xstream.annotations.XStreamAlias;

@XStreamAlias("xml")
public class OutputMessage {

	@XStreamAlias("ToUserName")
	@XStreamCDATA
	private String ToUserName;

	@XStreamAlias("FromUserName")
	@XStreamCDATA
	private String FromUserName;

	@XStreamAlias("CreateTime")
	private Long CreateTime;

	@XStreamAlias("MsgType")
	@XStreamCDATA
	private String MsgType = "text";

	private ImageMessage Image;

	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;
	}

	public ImageMessage getImage() {
		return Image;
	}

	public void setImage(ImageMessage image) {
		Image = image;
	}

}

图片消息支持

MediaIdMessage.java

package com.wx.util;

import com.thoughtworks.xstream.annotations.XStreamAlias;

public class MediaIdMessage {
	@XStreamAlias("MediaId")
	@XStreamCDATA
	private String MediaId;

	public String getMediaId() {
		return MediaId;
	}

	public void setMediaId(String mediaId) {
		MediaId = mediaId;
	}

}

ImageMessage.java

package com.wx.util;

import com.thoughtworks.xstream.annotations.XStreamAlias;


@XStreamAlias("Image")
public class ImageMessage extends MediaIdMessage {
}

ok 以上配置完成后即可 编写post处理controller

WxManagerController.java 

修改后最终

package com.controller;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;

import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;

import com.thoughtworks.xstream.XStream;
import com.wx.util.ImageMessage;
import com.wx.util.InputMessage;
import com.wx.util.OutputMessage;
import com.wx.util.SHA1;
import com.wx.util.SerializeXmlUtil;
import com.wx.util.WxMessageUtil;


@Controller 
@RequestMapping( "/wx" )
public class WxManagerController {
	
	private String Token = "testfortoken";
	
	@RequestMapping(value = "", method = { RequestMethod.GET, RequestMethod.POST })
	@ResponseBody
	public void load(Model model, HttpServletRequest request, HttpServletResponse response) {
		//判断访问方式
		boolean isGet = request.getMethod().toLowerCase().equals("get");
		if (isGet) {
			//进行认证
			access(request, response);
		} else {
			try {
				//转码UTF-8,防止乱码
				request.setCharacterEncoding("utf-8");
				response.setCharacterEncoding( "utf-8" );
				//处理微信post请求
				acceptMessage(request,response);
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
	}
	
	/**
	 * 验证URL真实性
	 * 
	 * @param request
	 * @param response
	 * @return String
	 */
	private String access(HttpServletRequest request, HttpServletResponse response) {
		// 验证URL真实性
		String signature = request.getParameter("signature");// 微信加密签名
		String timestamp = request.getParameter("timestamp");// 时间戳
		String nonce = request.getParameter("nonce");// 随机数
		String echostr = request.getParameter("echostr");// 随机字符串
		List params = new ArrayList();
		params.add(Token);
		params.add(timestamp);
		params.add(nonce);
		// 1. 将token、timestamp、nonce三个参数进行字典序排序
		Collections.sort(params, new Comparator() {
			@Override
			public int compare(String o1, String o2) {
				return o1.compareTo(o2);
			}
		});
		// 2. 将三个参数字符串拼接成一个字符串进行sha1加密
		String temp = SHA1.encode(params.get(0) + params.get(1) + params.get(2));
		if (temp.equals(signature)) {
			try {
				response.getWriter().write(echostr);
				return echostr;
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
		return null;
	}
	
	private void acceptMessage(HttpServletRequest request, HttpServletResponse response) throws IOException {
		// 处理接收消息
		ServletInputStream in = request.getInputStream();
		// 将POST流转换为XStream对象
		XStream xs = SerializeXmlUtil.createXstream();
		xs.processAnnotations(InputMessage.class);
		xs.processAnnotations(OutputMessage.class);
		// 将指定节点下的xml节点数据映射为对象
		xs.alias("xml", InputMessage.class);
		// 将流转换为字符串
		StringBuilder xmlMsg = new StringBuilder();
		byte[] b = new byte[4096];
		for (int n; (n = in.read(b)) != -1;) {
			xmlMsg.append(new String(b, 0, n, "UTF-8"));
		}
		// 将xml内容转换为InputMessage对象
		InputMessage inputMsg = (InputMessage) xs.fromXML(xmlMsg.toString());

		String servername = inputMsg.getToUserName();// 服务端
		String custermname = inputMsg.getFromUserName();// 客户端
		long createTime = inputMsg.getCreateTime();// 接收时间
		Long returnTime = Calendar.getInstance().getTimeInMillis() / 1000;// 返回时间

		// 取得消息类型
		String msgType = inputMsg.getMsgType();
		// 根据消息类型获取对应的消息内容
		if (msgType.equals(WxMessageUtil.REQ_MESSAGE_TYPE_TEXT)) {
			// 文本消息
			StringBuffer str = new StringBuffer();
			str.append("");
			str.append("

启动测试,重新提交一下服务器认证。

效果:

\

完成,进行下一个功能 素材库管理

三、素材库

操作素材库需要 access_token 接口调用凭据,

下面编写 access_token 方法。

创建 AccessToken model

AccessToken.java

package com.wx.model;

public class AccessToken {
	// 获取到的凭证
	private String token;
	// 凭证有效时间,单位:秒
	private int expiresIn;

	public String getToken() {
		return token;
	}

	public void setToken(String token) {
		this.token = token;
	}

	public int getExpiresIn() {
		return expiresIn;
	}

	public void setExpiresIn(int expiresIn) {
		this.expiresIn = expiresIn;
	}
}

 

想微信发起post请求必须使用https方式,所以需要编写配置https请求方式,证书配置如下,

MyX509TrustManager.java

package com.wx.util;

import java.security.cert.X509Certificate;

import javax.net.ssl.X509TrustManager;

public class MyX509TrustManager implements X509TrustManager {
	public void checkClientTrusted(X509Certificate[] chain, String authType) {  

    }  

	public void checkServerTrusted(X509Certificate[] chain, String authType) {  

    }  

	public X509Certificate[] getAcceptedIssuers() {  

		return null;  

    }  
}

配置后编写处理https发起以及处理,和获取accesstoken方法

WxManagerUtil.java

package com.wx.util;

import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.ConnectException;
import java.net.URL;

import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManager;

import com.alibaba.fastjson.JSONException;
import com.alibaba.fastjson.JSONObject;
import com.wx.model.AccessToken;

public class WxManagerUtil {
	
	// 获取access_token的接口地址(GET) 限200(次/天)
	public final static String access_token_url = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=APPID&secret=APPSECRET";
	
	/**
	 * 发起https请求并获取结果
	 * 
	 * @param requestUrl 请求地址
	 * @param requestMethod 请求方式(GET、POST)
	 * @param outputStr 提交的数据
	 * @return JSONObject(通过JSONObject.get(key)的方式获取json对象的属性值)
	 */
	public static JSONObject httpRequest(String requestUrl, String requestMethod, String outputStr) {  

	    JSONObject jsonObject = null;  
	    StringBuffer buffer = new StringBuffer();  
	    try {  
			// 创建SSLContext对象,并使用我们指定的信任管理器初始化
			TrustManager[] tm = { new MyX509TrustManager() };  
			SSLContext sslContext = SSLContext.getInstance("SSL", "SunJSSE");  
			sslContext.init(null, tm, new java.security.SecureRandom());  
			// 从上述SSLContext对象中得到SSLSocketFactory对象
			SSLSocketFactory ssf = sslContext.getSocketFactory();  
			URL url = new URL(requestUrl);  
			HttpsURLConnection httpUrlConn = (HttpsURLConnection) url.openConnection();  
			httpUrlConn.setSSLSocketFactory(ssf);  
			httpUrlConn.setDoOutput(true);  
			httpUrlConn.setDoInput(true);  
			httpUrlConn.setUseCaches(false);  
			// 设置请求方式(GET/POST)
			httpUrlConn.setRequestMethod(requestMethod);  
			if ("GET".equalsIgnoreCase(requestMethod))  
			    httpUrlConn.connect();  
			// 当有数据需要提交时
			if (null != outputStr) {  
				OutputStream outputStream = httpUrlConn.getOutputStream();  
				// 注意编码格式,防止中文乱码
			    outputStream.write(outputStr.getBytes("UTF-8"));  
			    outputStream.close();  
			}  
			// 将返回的输入流转换成字符串
			InputStream inputStream = httpUrlConn.getInputStream();  
			InputStreamReader inputStreamReader = new InputStreamReader(inputStream, "utf-8");  
		    BufferedReader bufferedReader = new BufferedReader(inputStreamReader);  
		    String str = null;  
		    while ((str = bufferedReader.readLine()) != null) {  
		        buffer.append(str);  
		    }  
		    bufferedReader.close();  
		    inputStreamReader.close();  
		    // 释放资源
		    inputStream.close();  
		    inputStream = null;  
		    httpUrlConn.disconnect();  
		    jsonObject = JSONObject.parseObject(buffer.toString());  
	    } catch (ConnectException ce) {  
	    	System.out.println("微信服务器连接超时!");
	    } catch (Exception e) {  
	    	System.out.println("HTTPS请求错误,错误信息:\n" + e.getMessage());
	    }  

		return jsonObject;  
	}  

	
	/**
	 * 生成AccessToken
	 * @param appid
	 * @param appsecret
	 * @return
	 */
	public static AccessToken getAccessToken(String appid, String appsecret) {
		AccessToken accessToken = null;

		String requestUrl = access_token_url.replace("APPID", appid).replace(
				"APPSECRET", appsecret);
		JSONObject jsonObject = httpRequest(requestUrl, "GET", null);
		// 如果请求成功
		if (null != jsonObject) {
			try {
				accessToken = new AccessToken();
				accessToken.setToken(jsonObject.getString("access_token"));
				accessToken.setExpiresIn(jsonObject.getIntValue("expires_in"));
			} catch (JSONException e) {
				accessToken = null;
				// 获取token失败
				System.out.println("获取TOKEN失败("+jsonObject.getString("errcode")+")");
			}
		}
		return accessToken;
	}
}

创建素材库model

WxArticles.java

package com.wx.model;

public class WxArticles {
	private String title; //标题
	private String thumb_media_id;//图文消息的封面图片素材id
	private String author;//作者
	private String digest;//图文消息的摘要,仅有单图文消息才有摘要,多图文此处为空
	private String show_cover_pic;//是否显示封面,0为false,即不显示,1为true,即显示
	private String content;//	图文消息的具体内容,支持HTML标签,必须少于2万字符,小于1M,且此处会去除JS
	private String content_source_url;//图文消息的原文地址,即点击“阅读原文”后的URL
	public String getTitle() {
		return title;
	}
	public void setTitle(String title) {
		this.title = title;
	}
	public String getThumb_media_id() {
		return thumb_media_id;
	}
	public void setThumb_media_id(String thumb_media_id) {
		this.thumb_media_id = thumb_media_id;
	}
	public String getAuthor() {
		return author;
	}
	public void setAuthor(String author) {
		this.author = author;
	}
	public String getDigest() {
		return digest;
	}
	public void setDigest(String digest) {
		this.digest = digest;
	}
	public String getShow_cover_pic() {
		return show_cover_pic;
	}
	public void setShow_cover_pic(String show_cover_pic) {
		this.show_cover_pic = show_cover_pic;
	}
	public String getContent() {
		return content;
	}
	public void setContent(String content) {
		this.content = content;
	}
	public String getContent_source_url() {
		return content_source_url;
	}
	public void setContent_source_url(String content_source_url) {
		this.content_source_url = content_source_url;
	}
}

完成以上基本OK。

先写一个测试方法,提交图文素材 需要thumb_media_id 这个参数,即:图文消息的封面图片素材id(必须是永久mediaID)

我们现在公众号里上传一个图片素材,然后去获取他的thumb_media_id后再 提交新的图文素材,这里使用用户发送消息来触发测试

 最终代码:

WxManagerController.java

package com.controller;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.eclipse.jdt.internal.compiler.batch.Main;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;

import com.alibaba.fastjson.JSONObject;
import com.thoughtworks.xstream.XStream;
import com.wx.model.AccessToken;
import com.wx.model.WxArticles;
import com.wx.util.ImageMessage;
import com.wx.util.InputMessage;
import com.wx.util.OutputMessage;
import com.wx.util.SHA1;
import com.wx.util.SerializeXmlUtil;
import com.wx.util.WxManagerUtil;
import com.wx.util.WxMessageUtil;


@Controller 
@RequestMapping( "/wx" )
public class WxManagerController {
	
	private String Token = "CL0WQY79GJ12XV643BEZKMF5PHTAN";
	
	@RequestMapping(value = "", method = { RequestMethod.GET, RequestMethod.POST })
	@ResponseBody
	public void load(Model model, HttpServletRequest request, HttpServletResponse response) {
		//判断访问方式
		boolean isGet = request.getMethod().toLowerCase().equals("get");
		if (isGet) {
			//进行认证
			access(request, response);
		} else {
			try {
				//转码UTF-8,防止乱码
				request.setCharacterEncoding("utf-8");
				response.setCharacterEncoding( "utf-8" );
				//处理微信post请求
				acceptMessage(request,response);
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
	}
	
	/**
	 * 验证URL真实性
	 * 
	 * @param request
	 * @param response
	 * @return String
	 */
	private String access(HttpServletRequest request, HttpServletResponse response) {
		// 验证URL真实性
		String signature = request.getParameter("signature");// 微信加密签名
		String timestamp = request.getParameter("timestamp");// 时间戳
		String nonce = request.getParameter("nonce");// 随机数
		String echostr = request.getParameter("echostr");// 随机字符串
		List params = new ArrayList();
		params.add(Token);
		params.add(timestamp);
		params.add(nonce);
		// 1. 将token、timestamp、nonce三个参数进行字典序排序
		Collections.sort(params, new Comparator() {
			@Override
			public int compare(String o1, String o2) {
				return o1.compareTo(o2);
			}
		});
		// 2. 将三个参数字符串拼接成一个字符串进行sha1加密
		String temp = SHA1.encode(params.get(0) + params.get(1) + params.get(2));
		if (temp.equals(signature)) {
			try {
				response.getWriter().write(echostr);
				return echostr;
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
		return null;
	}
	
	private void acceptMessage(HttpServletRequest request, HttpServletResponse response) throws IOException {
		// 处理接收消息
		ServletInputStream in = request.getInputStream();
		// 将POST流转换为XStream对象
		XStream xs = SerializeXmlUtil.createXstream();
		xs.processAnnotations(InputMessage.class);
		xs.processAnnotations(OutputMessage.class);
		// 将指定节点下的xml节点数据映射为对象
		xs.alias("xml", InputMessage.class);
		// 将流转换为字符串
		StringBuilder xmlMsg = new StringBuilder();
		byte[] b = new byte[4096];
		for (int n; (n = in.read(b)) != -1;) {
			xmlMsg.append(new String(b, 0, n, "UTF-8"));
		}
		// 将xml内容转换为InputMessage对象
		InputMessage inputMsg = (InputMessage) xs.fromXML(xmlMsg.toString());

		String servername = inputMsg.getToUserName();// 服务端
		String custermname = inputMsg.getFromUserName();// 客户端
		long createTime = inputMsg.getCreateTime();// 接收时间
		Long returnTime = Calendar.getInstance().getTimeInMillis() / 1000;// 返回时间

		// 取得消息类型
		String msgType = inputMsg.getMsgType();
		// 根据消息类型获取对应的消息内容
		if (msgType.equals(WxMessageUtil.REQ_MESSAGE_TYPE_TEXT)) {
			// 文本消息
//			StringBuffer str = new StringBuffer();
//			str.append("");
//			str.append(" list = new ArrayList();
		WxArticles wxArticles = new WxArticles();
		wxArticles.setTitle("a title");
		wxArticles.setAuthor("a author");
		wxArticles.setContent("a content");
		wxArticles.setContent_source_url("a content_source_url");
		wxArticles.setDigest("a digest");
		wxArticles.setShow_cover_pic("a show_cover_pic");
		wxArticles.setThumb_media_id(mediaId);
		
		list.add(wxArticles);
		Map> maplist = new HashMap>();
		maplist.put("articles", list);
		
		String urls= "https://api.weixin.qq.com/cgi-bin/material/add_news?access_token="+token;
		String jsons = JSONObject.toJSONString(maplist);
		
		JSONObject jsonObject = WxManagerUtil.httpRequest(urls, "POST", jsons);
		String result=null;
		if (null != jsonObject) {
			result = jsonObject.getString("media_id");
			System.out.println("返回("+result+")");
		}
		
	}
}

 

\

上传永久素材成功!

别忘了 填写自己的 appID 和appSecret;

AccessToken at = WxManagerUtil.getAccessToken("你的AppID", "你的AppSecret");

补充: 自定义菜单 功能;

WxManagerController.java 下添加此方法

private void accessMenu(String token){
		
		String menu = "{\"button\":[{\"type\":\"click\",\"name\":\"项目管理\",\"key\":\"20_PROMANAGE\"},{\"type\":\"click\",\"name\":\"机构运作\",\"key\":\"30_ORGANIZATION\"},{\"name\":\"日常工作\",\"sub_button\":[{\"type\":\"click\",\"name\":\"待办工单\",\"key\":\"01_WAITING\"},{\"type\":\"click\",\"name\":\"已办工单\",\"key\":\"02_FINISH\"},{\"type\":\"click\",\"name\":\"我的工单\",\"key\":\"03_MYJOB\"},{\"type\":\"click\",\"name\":\"公告消息箱\",\"key\":\"04_MESSAGEBOX\"},{\"type\":\"click\",\"name\":\"签到\",\"key\":\"05_SIGN\"}]}]}";
		String requestUrl = "https://api.weixin.qq.com/cgi-bin/menu/create?access_token="+token;
		int result = 0 ;
		JSONObject jsonObject = WxManagerUtil.httpRequest(requestUrl, "POST", menu);

		if (null != jsonObject) {
			if (0 != jsonObject.getIntValue("errcode")) {
				result = jsonObject.getIntValue("errcode");
				System.out.println("创建菜单失败("+result+")");
			}
			System.out.println("创建成功("+result+")");
		}
	}

  

完成!

 

抱歉!评论已关闭.

×