一、公众号配置
配置公众号回调地址
URL配置微信会验证此地址,直接返回微信的echostr参数即可。
这里是用的测试环境,正式环境的话会提示:
是否确定开启服务器配置?
请注意:开启后,用户发送的消息将自动转发到该配置地址,并且在网站中设置的自动回复和自定义菜单将失效。也可使用微信云托管免服务器免鉴权接收用户消息及开发者事件推送。
接收事件推送
1 关注/取消关注事件
2 扫描带参数二维码事件
3 上报地理位置事件
4 自定义菜单事件
5 点击菜单拉取消息时的事件推送
6 点击菜单跳转链接时的事件推送
关注/取消关注事件
用户在关注与取消关注公众号时,微信会把这个事件推送到开发者填写的URL。方便开发者给用户下发欢迎消息或者做账号的解绑。为保护用户数据隐私,开发者收到用户取消关注事件时需要删除该用户的所有信息。
微信服务器在五秒内收不到响应会断掉连接,并且重新发起请求,总共重试三次。
关于重试的消息排重,推荐使用FromUserName + CreateTime 排重。
假如服务器无法保证在五秒内处理并回复,可以直接回复空串,微信服务器不会对此作任何处理,并且不会发起重试。
二、后端接口开发
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintWriter;
import java.util.*;
/**
* 接受微信事件的url
*
* @author wanlilang
*/
@RestController
@RequestMapping(value = "/ReceiveWechatMessageAction")
@Api(value = "ReceiveWechatMessageAction.*")
@Scope("prototype")//多例模式
public class ReceiveWechatMessageAction {
private Logger logger = Logger.getLogger(ReceiveWechatMessageAction.class);
private static final String appid = "255644";
/*第三方用户唯一凭证密钥,即appsecret*/
private static final String secret = "55225555";
/*微信提供获取access_token接口地址*/
private static final String access_token_url = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential";
/**
* 接受微信发送过来的关注事件
*
* @return
*/
@RequestMapping(value = "/receiveWechat.html", method = RequestMethod.GET)
public void receiveMessageGet(PrintWriter out, HttpServletResponse response,
@RequestParam(value = "signature", required = false) String signature, @RequestParam String timestamp,
@RequestParam String nonce, @RequestParam String echostr) {
if (CheckUtil.checkSignature(signature, timestamp, nonce)) {
out.print(echostr);
logger.info("echostr===" + echostr);
}
}
/**
* 接受微信发送过来的关注事件
*
* @return
*/
@RequestMapping(value = "/receiveWechat.html", method = RequestMethod.POST)
public void receiveMessagePost(HttpServletRequest request, HttpServletResponse response) {
Map<String, String> toPostMessage = this.getXMLFromRequset(request);
logger.info("接受微信发送过来的关注事件数据信息===" + JSONObject.fromObject(toPostMessage));
JSONObject user = JSONObject.fromObject(toPostMessage);
// 获取关注用户的openid
if (user.size() > 0) {
String openid = user.getString("FromUserName");
// 通过openid获取用户微信
if (openid != null && !"".equals(openid)) {
String access_token = getToken();
// 获得用户信息
String threadURL = getWechatMessageThread(access_token, openid);
JSONObject returnmessage2 = JSONObject.fromObject(HttpTools.HttpGet(threadURL).toString());
JSONObject object2 = JSONObject.fromObject(returnmessage2);
logger.info("接收消息:"+object2);
}
}
try {
response.getWriter().write("success");
} catch (IOException e) {
e.printStackTrace();
}
}
public String getToken() {
String tokenGetUrl = access_token_url;//微信提供获取access_token接口地址
System.out.println("~~~~~appid:" + appid);
System.out.println("~~~~~secret:" + secret);
org.json.JSONObject tokenJson = new org.json.JSONObject();
if (StringUtils.isNotBlank(appid) && StringUtils.isNotBlank(secret)) {
tokenGetUrl += "&appid=" + appid + "&secret=" + secret;
tokenJson = new WeixinUtil().getUrlResponse(tokenGetUrl);
System.out.println("~~~~~tokenJson:" + tokenJson.toString());
try {
return (String) tokenJson.get("access_token");
} catch (JSONException e) {
System.out.println("报错了");
return null;
}
} else {
System.out.println("appid和secret为空");
return null;
}
}
/**
* 第三步获得用户信息。
*
* @param access_token
* @param openid
* @return
*/
private String getWechatMessageThread(String access_token, String openid) {
StringBuffer sub = new StringBuffer();
sub.append("https://api.weixin.qq.com/cgi-bin/user/info?");
sub.append("access_token=" + access_token);
sub.append("&openid=" + openid);
sub.append("&lang=zh_CN");
return sub.toString();
}
/**
* 从请求中获得xml信息。
*
* @param request
* @return
*/
private Map<String, String> getXMLFromRequset(HttpServletRequest request) {
try {
// 将解析结果存储在HashMap中
Map<String, String> map = new HashMap<String, String>();
// 从request中取得输入流
InputStream inputStream = request.getInputStream();
// 读取输入流
SAXReader reader = new SAXReader();
Document document = reader.read(inputStream);
// 得到xml根元素
Element root = document.getRootElement();
// 得到根元素的所有子节点
List<Element> list = root.elements();
// 遍历所有子节点
for (Element e : list) {
map.put(e.getName(), e.getText());
}
inputStream.close();
inputStream = null;
logger.info("二维码识别发送上来的参数为:" + map);
return map;
} catch (Exception e) {
logger.error(e);
}
return null;
}
}
验证消息的合法性
package com.ddm.baseService.tools;
import java.util.Arrays;
public class CheckUtil {
//配置微信公众号时填写的Token
private static final String token = "lian@123456";
public static boolean checkSignature(String signature, String timestamp, String nonce) {
// 拼接字符串
String[] arr = new String[] { token, timestamp, nonce };
// 排序
Arrays.sort(arr);
// 生成字符串
StringBuffer content = new StringBuffer();
for (int i = 0; i < arr.length; i++) {
content.append(arr[i]);
}
// SHA1加密
String tmp = DecriptUtil.SHA1(content.toString());
return tmp.equals(signature);
}
}
加密解密
package com.ddm.baseService.tools;
import javax.crypto.*;
import javax.crypto.spec.SecretKeySpec;
import java.io.UnsupportedEncodingException;
import java.security.InvalidKeyException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
public class DecriptUtil {
public static String SHA1(String decript) {
try {
MessageDigest digest = MessageDigest
.getInstance("SHA-1");
digest.update(decript.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 "";
}
public static String SHA(String decript) {
try {
MessageDigest digest = MessageDigest
.getInstance("SHA");
digest.update(decript.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 "";
}
public static String MD5(String input) {
try {
// 获得MD5摘要算法的 MessageDigest 对象
MessageDigest mdInst = MessageDigest.getInstance("MD5");
// 使用指定的字节更新摘要
mdInst.update(input.getBytes());
// 获得密文
byte[] md = mdInst.digest();
// 把密文转换成十六进制的字符串形式
StringBuffer hexString = new StringBuffer();
// 字节数组转换为 十六进制 数
for (int i = 0; i < md.length; i++) {
String shaHex = Integer.toHexString(md[i] & 0xFF);
if (shaHex.length() < 2) {
hexString.append(0);
}
hexString.append(shaHex);
}
return hexString.toString();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
return "";
}
/**
* 加密
*
* @param content
* 需要加密的内容
* @param password
* 加密密码
* @return
*/
public static byte[] encryptAES(String content, String password) {
try {
KeyGenerator kgen = KeyGenerator.getInstance("AES");
kgen.init(128, new SecureRandom(password.getBytes()));
SecretKey secretKey = kgen.generateKey();
byte[] enCodeFormat = secretKey.getEncoded();
SecretKeySpec key = new SecretKeySpec(enCodeFormat, "AES");
Cipher cipher = Cipher.getInstance("AES");// 创建密码器
byte[] byteContent = content.getBytes("utf-8");
cipher.init(Cipher.ENCRYPT_MODE, key);// 初始化
byte[] result = cipher.doFinal(byteContent);
return result; // 加密
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (NoSuchPaddingException e) {
e.printStackTrace();
} catch (InvalidKeyException e) {
e.printStackTrace();
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
} catch (IllegalBlockSizeException e) {
e.printStackTrace();
} catch (BadPaddingException e) {
e.printStackTrace();
}
return null;
}
/**
* 解密
*
* @param content
* 待解密内容
* @param password
* 解密密钥
* @return
*/
public static byte[] decryptAES(byte[] content, String password) {
try {
KeyGenerator kgen = KeyGenerator.getInstance("AES");
kgen.init(128, new SecureRandom(password.getBytes()));
SecretKey secretKey = kgen.generateKey();
byte[] enCodeFormat = secretKey.getEncoded();
SecretKeySpec key = new SecretKeySpec(enCodeFormat, "AES");
Cipher cipher = Cipher.getInstance("AES");// 创建密码器
cipher.init(Cipher.DECRYPT_MODE, key);// 初始化
byte[] result = cipher.doFinal(content);
return result; // 加密
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (NoSuchPaddingException e) {
e.printStackTrace();
} catch (InvalidKeyException e) {
e.printStackTrace();
} catch (IllegalBlockSizeException e) {
e.printStackTrace();
} catch (BadPaddingException e) {
e.printStackTrace();
}
return null;
}
/**
* BASE64解密
*
* @param key
* @return
* @throws Exception
*/
public static String decryptBASE64(String key) {
return "";
}
/**
* BASE64加密
*
* @param key
* @return
* @throws Exception
*/
public static String encryptBASE64(String key) {
return "";
}
}