微信公众号开发

本文详细介绍如何配置微信公众号的后台服务器,包括参数配置、接口验证、消息处理等关键步骤,并提供了完整的Java示例代码。

微信公众号开发

(一)微信后台服务器接入(以测试号为例)

参数配置

点击左上角的“修改”进行接口配置信息的修改

后台代码

@Controller
public class WxController {
private static Logger logger =  LoggerFactory.getLogger(WxController.class);
//通过这个方法执行验签,验证自己的服务器和微信服务器之间是否可以正常进行信息传输
@RequestMapping(value="/wx",method=RequestMethod.GET)
public void doGet(HttpServletRequest request,HttpServletResponse response,Map<String,Object> map) throws IOException {
	logger.info("开始接入,进入doGet方法");
	request.setCharacterEncoding("UTF-8");
    response.setCharacterEncoding("UTF-8");	
	PrintWriter out = response.getWriter();

	/**
	 * signature	微信加密签名,signature结合了开发者填写的token参数和请求中的timestamp参数、nonce参数。
		timestamp	时间戳
		nonce	随机数
		echostr	随机字符串
	 */
	
	String signature = request.getParameter("signature");
	String timestamp = request.getParameter("timestamp");
	String nonce = request.getParameter("nonce");
	String echostr = request.getParameter("echostr");
	//验证签名
	if(BasicUtil.check(nonce,timestamp,signature)) {
		logger.info("接入成功");
		out.print(echostr);
		out.flush();
		out.close();
	}else {
		logger.info("接入失败,验签错误");
	}
}
/**
 * 处理消息和事件
 * @param request
 * @param response
 * @param map
 * @throws IOException
 */
@RequestMapping(value="/wx",method=RequestMethod.POST)
public void doPost(HttpServletRequest request,HttpServletResponse response) throws IOException {
	logger.info("post方法接入");
	request.setCharacterEncoding("utf-8");
	response.setCharacterEncoding("utf-8");
	BasicUtil bu = new BasicUtil();
	//解析xml数据包,将xml解析为map
	Map<String,String> requestMap = bu.parseRequset(request.getInputStream());
	logger.info("将xml解析为map:"+requestMap);
	MessageEvent me = new MessageEvent();
	//调用消息处理方法
	String resp = me.getRespose(requestMap);
	logger.info("resp:"+resp);
	PrintWriter out = response.getWriter();
	out.print(resp);
	out.flush();
	out.close();
}

}

工具类

/**
 * 	基础工具类
 * @author LILUO
 *
 */

public class BasicUtil {
/**
 * 验证签名
 * 
 * @param nonce
 * @param timestamp
 * @param signature
 * @return
 */
public static  boolean check(String nonce, String timestamp, String signature) {
	// 1)将token、timestamp、nonce三个参数进行字典序排序
	String[] strs = new String[] {WxConfig.TOKEN, timestamp, nonce };
	Arrays.sort(strs);
	// 2)将三个参数字符串拼接成一个字符串进行sha1加密
	String str = strs[0] + strs[1] + strs[2];
	String encryption = sha1(str);
	// 3)开发者获得加密后的字符串可与signature对比,标识该请求来源于微信
	return encryption.equalsIgnoreCase(signature);
}
/**
 * sha1() 加密
 * 
 * @param str
 * @return
 */
public static  String sha1(String str) {
	try {
		// new对象,传入加密方式,即可直接按照封装好的方法进行加密
		MessageDigest md = MessageDigest.getInstance("sha1");
		// 将传入的字符串转为byte类型,存储如byte数组中
		byte[] digest = md.digest(str.getBytes());
		char[] chars = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' };
		StringBuilder sb = new StringBuilder();
		// 加密结果处理
		for (byte b : digest) {
			// 处理高四位
			sb.append(chars[(b >> 4) & 15]);
			// 处理第四位
			sb.append(chars[b & 15]);
		}
		return sb.toString();
	} catch (NoSuchAlgorithmException e) {
		e.printStackTrace();
		return null;
	}
}

/**
 * 	解析xml数据包,直接从httpserlet流中读取
 * 
 * @param inputStream
 * @return
 */
public Map<String, String> parseRequset(ServletInputStream inputStream) {
	SAXReader reader = new SAXReader();
	Map<String, String> map = new HashMap<>();
	try {
		// 读取输入流,获取文档对象
		Document doucment = reader.read(inputStream);
		// 根据文档读取根节点
		Element root = doucment.getRootElement();
		// 获取子节点
		@SuppressWarnings("unchecked")
		List<Element> element = root.elements();
		for (Element e : element) {
			map.put(e.getName(), e.getStringValue());
		}
		return map;
	} catch (DocumentException e) {
		e.printStackTrace();
		return null;
	}

	
}

/**
 *	 将消息对象转为xml数据包,微信消息事件处理时使用
 * 
 * @param msg
 * @return
 */
public static  String beanToXml(BaseMessage msg) {
	// 借助xstream包可以快速将一个对象转为xml
	XStream stream = new XStream();
	// 让对应的类执行相应懂得注解,起别名为xml
	stream.processAnnotations(TextMessage.class);
	stream.processAnnotations(ImageMessage.class);
	stream.processAnnotations(MusicMessage.class);
	stream.processAnnotations(NewsMessage.class);
	stream.processAnnotations(VideoMessage.class);
	stream.processAnnotations(VoiceMessage.class);
	String xml = stream.toXML(msg);
	return xml;
}


}

发送请求

/**
*	发送请求
* @author ThinkPad
*
 */
public class GetAndPost {
	//{"access_token":"ACCESS_TOKEN","expires_in":7200}
	/*
	 * 错误时微信会返回错误码等信息,JSON数据包示例如下(该示例为AppID	无效错误):
	 * {"errcode":40013,"errmsg":"invalid appid"}
	 */


/**
 * 向指定的url发送get请求,获取token
 * @param url
 * @return
 * @throws MalformedURLException
 */
public static String get(String url) {
	try {
		URL urlObject = new URL(url);
		//开始连接
		URLConnection connection = urlObject.openConnection();
		InputStream is = connection.getInputStream();
		byte [] b = new byte[1024];
		int len;
		StringBuilder sb = new StringBuilder();
		while((len =is.read(b))!=-1) {
			sb.append(new String(b,0,len));
		}
		return sb.toString();
	} catch (Exception e) {
		e.printStackTrace();
	}
			
	return null;
}
/**
 * 	向指定url发送一个post请求,带着data数据
 * @param url
 * @param data
 * @return
 */
public static String post(String url,String data) {
	try {
		//获取url
		URL urlObj = new URL(url);
		//开始连接
		URLConnection connection = urlObj.openConnection();
		//要发送数据必须设置数据发送状态;
		connection.setDoOutput(true);
		//获取输出流
		OutputStream os = connection.getOutputStream();
		//写出数据
		os.write(data.getBytes());
		//刷新,关流
		os.flush();
		os.close();
		InputStream is = connection.getInputStream();
		byte [] b = new byte[1024];
		int len;
		StringBuilder sb = new StringBuilder();
		while((len =is.read(b))!=-1) {
			sb.append(new String(b,0,len));
		}
		return sb.toString();
	} catch (Exception e) {
		e.printStackTrace();
	}
	
	return null;
}
}

获取tocken

public class GetTocken {
	private static Logger logger = Logger.getLogger(GetTocken.class);
	private static AccessToken at;// 用于存储token

private static void getToken() {
	String url = WxConfig.URL.replace("APPID", WxConfig.APPID).replace("APPSECRET", WxConfig.APPSECRET);
	String TokenStr = GetAndPost.get(url);
	// System.out.println(TokenStr);//打印access_token和expires_in
	// 将字符串封装成一个对象
	JSONObject jsonObject = JSONObject.fromObject(TokenStr);
	String token = jsonObject.getString("access_token");
	//System.out.println("access_token" + token);
	String expireIn = jsonObject.getString("expires_in");
	//System.out.println("expires_in" + expireIn);
	// 创建token对象,并存储token详情
	at = new AccessToken(token, expireIn);
	logger.info("第一次获取access_token:"+token);

}

/**
 * 向外暴露获取token的方法
 * 
 * @return
 */
public static String getAccessToken() {
	//后期tocken可以存到redis中无需每次都单独获取
	if (at == null || at.isExpire()) {
		getToken();
	}
	return at.getAccessToken();
}

}

MD5加密方式,有多种,选择需要的

package com.liluo.restful.util;

import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Random;


	import org.apache.commons.codec.binary.Hex;


/**
 * MD5加密解密工具类
*/
public class MD5Util {

/**
 * 普通MD5加密 01
 */
public static String getStrMD5(String inStr) {
	// 获取MD5实例
	MessageDigest md5 = null;
	try {
		md5 = MessageDigest.getInstance("MD5");
	} catch (NoSuchAlgorithmException e) {
		e.printStackTrace();
		System.out.println(e.toString());
		return "";
	}

	// 将加密字符串转换为字符数组
	char[] charArray = inStr.toCharArray();
	byte[] byteArray = new byte[charArray.length];

	// 开始加密
	for (int i = 0; i < charArray.length; i++)
		byteArray[i] = (byte) charArray[i];
	byte[] digest = md5.digest(byteArray);
	StringBuilder sb = new StringBuilder();
	for (int i = 0; i < digest.length; i++) {
		int var = digest[i] & 0xff;
		if (var < 16)
			sb.append("0");
		sb.append(Integer.toHexString(var));
	}
	return sb.toString();
}

/**
 * 普通MD5加密 02
 */
public static String getStrrMD5(String password) {

	char hexDigits[] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' };
	try {
		byte strTemp[] = password.getBytes("UTF-8");
		MessageDigest mdTemp = MessageDigest.getInstance("MD5");
		mdTemp.update(strTemp);
		byte md[] = mdTemp.digest();
		int j = md.length;
		char str[] = new char[j * 2];
		int k = 0;
		for (int i = 0; i < j; i++) {
			byte byte0 = md[i];
			str[k++] = hexDigits[byte0 >>> 4 & 0xf];
			str[k++] = hexDigits[byte0 & 0xf];
		}

		return new String(str);
	} catch (Exception e) {
		return null;
	}
}

/**
 * MD5双重解密
 */
public static String getconvertMD5(String inStr) {
	char[] charArray = inStr.toCharArray();
	for (int i = 0; i < charArray.length; i++) {
		charArray[i] = (char) (charArray[i] ^ 0xff);
	}
	String str = String.valueOf(charArray);
	return str;
}

/**
 * 使用Apache的Hex类实现Hex(16进制字符串和)和字节数组的互转
 */
@SuppressWarnings("unused")
private static String md5Hex(String str) {
	try {
		MessageDigest md = MessageDigest.getInstance("MD5");
		byte[] digest = md.digest(str.getBytes());
		return new String(new Hex().encode(digest));
	} catch (Exception e) {
		e.printStackTrace();
		System.out.println(e.toString());
		return "";
	}
}

/**
 * 加盐MD5加密
 */
public static String getSaltMD5(String password) {
	// 生成一个16位的随机数
	Random random = new Random();
	StringBuilder sBuilder = new StringBuilder(16);
	//生成一个伪随机数,每次不重复
	sBuilder.append(random.nextInt(99999999)).append(random.nextInt(99999999));
	int len = sBuilder.length();
	if (len < 16) {
		for (int i = 0; i < 16 - len; i++) {
			sBuilder.append("0");
		}
	}
	// 生成最终的加密盐
	String Salt = sBuilder.toString();
	password = md5Hex(password + Salt);
	char[] cs = new char[48];
	for (int i = 0; i < 48; i += 3) {//一共十六位
		cs[i] = password.charAt(i / 3 * 2);
		char c = Salt.charAt(i / 3);
		cs[i + 1] = c;
		cs[i + 2] = password.charAt(i / 3 * 2 + 1);
	}
	
	return String.valueOf(cs);
}

/**
 * 验证加盐后是否和原文一致
 */
public static boolean getSaltverifyMD5(String password, String md5str) {
	char[] cs1 = new char[32];
	char[] cs2 = new char[16];
	for (int i = 0; i < 48; i += 3) {
		//原始密码
		cs1[i / 3 * 2] = md5str.charAt(i);
		cs1[i / 3 * 2 + 1] = md5str.charAt(i + 2);
		//盐值
		cs2[i / 3] = md5str.charAt(i + 1);
	}
	String Salt = new String(cs2);
	return md5Hex(password + Salt).equals(String.valueOf(cs1));
}


//MD5带key
public static String MD5Encode(String origin, String charsetname) {	
	String resultString = null;
	try {
		resultString = new String(origin);
		MessageDigest md = MessageDigest.getInstance("MD5");
		if(charsetname == null || charsetname.equals("")) {
			resultString = byteArreyToHexString(md.digest(resultString.getBytes()));
		}else {
			resultString = byteArreyToHexString(md.digest(resultString.getBytes(charsetname)));
		}
	} catch (Exception e) {
		// TODO: handle exception
		e.printStackTrace();
	}
	return resultString;
}

private static String byteArreyToHexString(byte[] digest) {
	StringBuffer stringBuffer = new StringBuffer();
	
	for(int i = 0; i < digest.length; i++) {
		stringBuffer.append(byteToHexString(digest[i]));
	}
	
	return stringBuffer.toString();
}

private static Object byteToHexString(byte b) {

	int n = b;
	if(n < 0) {
		n+=256;
	}
	int b1 = n / 16;
	int b2 = n % 16;
	return hexDigite[b1]+hexDigite[b2];
}
private static final String hexDigite [] = {
	"0","1","2","3","4","5","6","7","8","9","a","b","c","d","e","f"
};

}

仅仅对于接口配置,只需要get方法就好,post可以先注释掉,后边使用

在这里插入图片描述

配置好以后就可以运行项目测试连接了,如果提示“配置成功”则项目配置成功,测试号由于不稳定,可能需要多次尝试才可以成功,请耐心。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值