微信公众号开发
(一)微信后台服务器接入(以测试号为例)
参数配置

点击左上角的“修改”进行接口配置信息的修改
后台代码
@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可以先注释掉,后边使用

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