微信接入指南
https://mp.weixin.qq.com/wiki/8/f9a0b8382e0b77d87b3bcc1ce6fbc104.html
目录
1 概述
2 第一步:填写服务器配置
3 第二步:验证服务器地址的有效性
package com.web.wexin;
import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import com.web.wexin.util.SignUtil;
/**
* 核心请求处理类
*/
public class WeiXinServlet extends HttpServlet{
private static final long serialVersionUID = 1L;
/**
* 确认请求来自微信服务器
*/
public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// 微信加密签名
String signature = request.getParameter("signature");
// 时间戳
String timestamp = request.getParameter("timestamp");
// 随机数
String nonce = request.getParameter("nonce");
// 随机字符串
String echostr = request.getParameter("echostr");
PrintWriter out = response.getWriter();
// 通过检验signature对请求进行校验,若校验成功则原样返回echostr,表示接入成功,否则接入失败
if (SignUtil.checkSignature(signature, timestamp, nonce)) {
out.print(echostr);
}
out.close();
out = null;
}
/**
* 处理微信服务器发来的消息
*/
public void doPost(HttpServletRequest request, HttpServletResponse response)throws ServletException, IOException {
// 将请求、响应的编码均设置为UTF-8(防止中文乱码)
request.setCharacterEncoding("UTF-8");
response.setCharacterEncoding("UTF-8");
// 调用核心业务类接收消息、处理消息
String respMessage = CoreService.processRequest(request);
// 响应消息
PrintWriter out = response.getWriter();
out.print(respMessage);
out.close();
}
}
4 第三步:依据接口文档实现业务逻辑
先提供一个工具类
package com.web.wexin.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 java.util.HashMap;
import java.util.Map;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManager;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONException;
import com.alibaba.fastjson.JSONObject;
import com.bean.AccessToken;
import com.web.wexin.vo.Menu;
import com.web.wexin.vo.OAuthInfo;
import com.web.wexin.vo.WxUserinfo;
/**
* 公众平台通用接口工具类
*
*/
public class WeixinUtil {
private static Logger log = LoggerFactory.getLogger(WeixinUtil.class);
// 第三方用户唯一凭证
public final static String appId = "wx71335f894797efd0";
// 第三方用户唯一凭证密钥
public final static String appSecret = "d524fcf5147c6c732a2270a64f5869a5";
// 获取access_token的接口地址(GET)
public final static String access_token_url = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=APPID&secret=APPSECRET";
// 菜单创建
public final static String menu_create_url = "https://api.weixin.qq.com/cgi-bin/menu/create?access_token=ACCESS_TOKEN";
// 发送模板消息
public final static String sendtemplat_url = "https://api.weixin.qq.com/cgi-bin/message/template/send?access_token=ACCESS_TOKEN";
// 获取code后,请求以下链接获取用户access_token:
public final static String userinfotoken_url = "https://api.weixin.qq.com/sns/oauth2/access_token?appid=APPID&secret=APPSECRET&code=CODE&grant_type=authorization_code";
// 粉丝列表
public final static String fansi_url = "https://api.weixin.qq.com/cgi-bin/user/get?access_token=ACCESS_TOKEN";
// 获取用户信息
public final static String user_url = "https://api.weixin.qq.com/sns/userinfo?access_token=ACCESS_TOKEN&openid=OPENID&lang=zh_CN";
// 获取用户基本信息(包括UnionID机制) 粉丝列表时使用
public final static String fansi_userinfo_url = "https://api.weixin.qq.com/cgi-bin/user/info?access_token=ACCESS_TOKEN&openid=OPENID&lang=zh_CN";
// 创建临时二维码ticket
public final static String temporary_url = "https://api.weixin.qq.com/cgi-bin/qrcode/create?access_token=ACCESS_TOKEN";
// 创建永久二维码ticket
public final static String permanent_url = "https://api.weixin.qq.com/cgi-bin/qrcode/create?access_token=ACCESS_TOKEN";
// 通过ticket换取二维码 提醒:TICKET记得进行UrlEncode
public final static String qrcode_url = "https://mp.weixin.qq.com/cgi-bin/showqrcode?ticket=TICKET";
// 获取用户增减数据 最大时间跨度7天
public final static String getusersummary_url = "https://api.weixin.qq.com/datacube/getusersummary?access_token=ACCESS_TOKEN";
// 获取累计用户数据 最大时间跨度7天
public final static String getusercumulate_url = "https://api.weixin.qq.com/datacube/getusercumulate?access_token=ACCESS_TOKEN";
// 获取jsapi_ticket
public final static String jsapi_ticket_url = "https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token=ACCESS_TOKEN&type=jsapi";
// 创建分组 POST数据例子:{"group":{"name":"test"}}
public final static String create_group_url = "https://api.weixin.qq.com/cgi-bin/groups/create?access_token=ACCESS_TOKEN";
// 查询所有分组
public final static String selete_group_url = "https://api.weixin.qq.com/cgi-bin/groups/get?access_token=ACCESS_TOKEN";
// 查询用户所在分组 POST数据例子:{"openid":"od8XIjsmk6QdVTETa9jLtGWA6KBc"}
public final static String selectUsers_group_url = "https://api.weixin.qq.com/cgi-bin/groups/getid?access_token=ACCESS_TOKEN";
// 修改分组名 POST数据例子:{"group":{"id":108,"name":"test2_modify2"}}
public final static String update_group_url = "https://api.weixin.qq.com/cgi-bin/groups/update?access_token=ACCESS_TOKEN";
// 批量移动用户分组
public final static String batch_group_url = "https://api.weixin.qq.com/cgi-bin/groups/members/batchupdate?access_token=ACCESS_TOKEN";
// 获取微信卡券 api_ticket
public final static String api_ticket_url = "https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token=ACCESS_TOKEN&type=wx_card";
// 批量查询卡券列表
public final static String batchget_url = "https://api.weixin.qq.com/card/batchget?access_token=ACCESS_TOKEN";
// 查询卡券详情 http请求方式: POST POST数据 {"card_id":"pFS7Fjg8kV1IdDz01r4SQwMkuCKc"}
public final static String get_card_url = "https://api.weixin.qq.com/card/get?access_token=ACCESS_TOKEN";
// 创建卡券二维码 ticket post请求
public final static String create_cardTicket_url = "https://api.weixin.qq.com/card/qrcode/create?access_token=ACCESS_TOKEN";
// 核销卡券Code接口
public final static String delete_code_url = "https://api.weixin.qq.com/card/code/consume?access_token=ACCESS_TOKEN";
// Code解码接口
public final static String code_decrypt_url = "https://api.weixin.qq.com/card/code/decrypt?access_token=ACCESS_TOKEN";
// 检测Code是否正常状态
public final static String check_code_url = "https://api.weixin.qq.com/card/code/get?access_token=ACCESS_TOKEN";
// 创建卡券货架接口
public final static String create_cardhj_url = "https://api.weixin.qq.com/card/landingpage/create?access_token=ACCESS_TOKEN";
// 获取用户已领取卡券接口
public final static String getcardlist_url = "https://api.weixin.qq.com/card/user/getcardlist?access_token=ACCESS_TOKEN";
/**
* 获取access_token
*
* @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.getInteger("expires_in"));
} catch (JSONException e) {
accessToken = null;
// 获取token失败
log.error("获取token失败 errcode:{} errmsg:{}",
jsonObject.getInteger("errcode"),
jsonObject.getString("errmsg"));
}
}
return accessToken;
}
/**
* 获取网页授权用户的token
*
* @param code
* @return 2016年8月17日
*/
public static OAuthInfo getUserToken(String code) {
String requestUrl = userinfotoken_url.replace("APPID", appId)
.replace("APPSECRET", appSecret).replace("CODE", code);
JSONObject jsonObject = httpRequest(requestUrl, "GET", null);
OAuthInfo oa = new OAuthInfo();
if (null != jsonObject) {
try {
oa.setAccessToken(jsonObject.getString("access_token"));
oa.setExpiresin(jsonObject.getInteger("expires_in").toString());
oa.setRefreshToken(jsonObject.getString("refresh_token"));
oa.setOpenid(jsonObject.getString("openid"));
oa.setScope(jsonObject.getString("scope"));
} catch (JSONException e) {
// 获取token失败
log.error("网页授权获取openId失败 errcode:{} errmsg:{}",
jsonObject.getInteger("errcode"),
jsonObject.getString("errmsg"));
}
}
return oa;
}
/**
* 获取网页授权用户的详细信息
*
* @param oa
* @return 2016年8月17日
*/
public static WxUserinfo getWxUserInfo(OAuthInfo oa) {
String requestUrl = user_url.replace("ACCESS_TOKEN",
oa.getAccessToken()).replace("OPENID", oa.getOpenid());
JSONObject jsonObject = httpRequest(requestUrl, "GET", null);
WxUserinfo user = new WxUserinfo();
if (jsonObject != null) {
user.setOpenid(jsonObject.getString("openid"));
user.setNickname(jsonObject.getString("nickname"));
user.setSex(jsonObject.getString("sex"));
user.setProvince(jsonObject.getString("province"));
user.setCity(jsonObject.getString("city"));
user.setCountry(jsonObject.getString("country"));
user.setHeadimgurl(jsonObject.getString("headimgurl"));
user.setPrivilege(jsonObject.getString("privilege"));
}
return user;
}
/**
* 创建菜单
*
* @param menu
* 菜单实例
* @param accessToken
* 有效的access_token
* @return 0表示成功,其他值表示失败
*/
public static int createMenu(Menu menu, String accessToken) {
int result = 0;
// 拼装创建菜单的url
String url = menu_create_url.replace("ACCESS_TOKEN", accessToken);
// 将菜单对象转换成json字符串
String jsonMenu = JSONObject.toJSONString(menu);
// 调用接口创建菜单
JSONObject jsonObject = httpRequest(url, "POST", jsonMenu);
if (null != jsonObject) {
if (0 != jsonObject.getInteger("errcode")) {
result = jsonObject.getInteger("errcode");
log.error("创建菜单失败 errcode:{} errmsg:{}",
jsonObject.getInteger("errcode"),
jsonObject.getString("errmsg"));
}
}
return result;
}
/**
* 获取粉丝列表信息
*
* @param accessToken
* @param openid
* @return 2016年8月18日
*/
public static JSONArray getFansList(String accessToken) {
JSONArray arrs = new JSONArray();
String url = fansi_url.replace("ACCESS_TOKEN", accessToken);
JSONObject jsonObject = httpRequest(url, "GET", null);
if (null != jsonObject) {
JSONObject data = (JSONObject) jsonObject.get("data");
JSONArray openidList = data.getJSONArray("openid");
for (int i = 0; i < openidList.size(); i++) {
System.out.println(openidList.getString(i));
JSONObject obj = httpRequest(
fansi_userinfo_url.replace("ACCESS_TOKEN", accessToken)
.replace("OPENID", openidList.getString(i)),
"GET", null);
if (obj != null) {
arrs.add(obj);
}
}
}
return arrs;
}
/**
* 创建二维码ticket
*
* @param type
* 1=临时 ,2=永久
* @param accessToken
* @param expire_seconds
* 该二维码有效时间,以秒为单位。 最大不超过2592000
* @param scene_id
* 场景值ID,临时二维码时为32位非0整型,永久二维码时最大值为100000
* @return 2016年8月18日
*/
public static JSONObject createTicket(String type, String accessToken,
String expire_seconds, String scene_id) {
String requestUrl = "";
String jsonStr = "";
if (type.equals("1")) {// 临时
requestUrl = temporary_url.replace("ACCESS_TOKEN", accessToken);
jsonStr = "{\"expire_seconds\": 604800, \"action_name\": \"QR_SCENE\", \"action_info\": {\"scene\": {\"scene_id\": 123}}}";
} else {// 永久
requestUrl = permanent_url.replace("ACCESS_TOKEN", accessToken);
jsonStr = "{\"action_name\": \"QR_LIMIT_SCENE\", \"action_info\": {\"scene\": {\"scene_id\": 123}}}";
}
JSONObject jsonObject = httpRequest(requestUrl, "POST", jsonStr);
return jsonObject;
}
/**
* 发起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) {
log.error("Weixin server connection timed out.");
} catch (Exception e) {
log.error("https request error:{}", e);
}
return jsonObject;
}
public static Map wxMsgMap(Map allMap,
String key, String value, String color) {
Map map = new HashMap();
map.put("value", value);
map.put("color", color);
allMap.put(key, map);
return allMap;
}
/**
* 发送模板消息
*
* @param accessToken
* @param OpenId
* @param template_id
* @param detailUrl
* @param map
* @return 2016年9月19日
*/
public static JSONObject postTemplateMsg(String accessToken, String OpenId,
String template_id, String detailUrl, Map map) {
String url = sendtemplat_url.replace("ACCESS_TOKEN", accessToken);
JSONObject jso = new JSONObject();
jso.put("touser", OpenId);
jso.put("template_id", template_id);
if (detailUrl != null)
jso.put("url", detailUrl);
jso.put("data", map);
return httpRequest(url, "POST", jso.toString());
}
}
springMVC控制器
package com.web;
import java.util.HashMap;
import java.util.Map;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.lang.StringUtils;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.bean.CardReceive;
import com.mapper.SqlcardReceive;
import com.util.AjaxUtil;
import com.util.DateUtil;
import com.web.wexin.TokenThread;
import com.web.wexin.util.SignUtil;
import com.web.wexin.util.WeixinUtil;
import com.web.wexin.vo.OAuthInfo;
import com.web.wexin.vo.WxUserinfo;
@Controller
@RequestMapping("/wx")
public class WeiXinController {
@Resource
public SqlcardReceive sqlcardReceive;
/**
* 网页授权获取用户信息
* @param view
* @param req
* @return
* 2016年8月16日
*/
@RequestMapping("/oauth")
public ModelAndView oauth(ModelAndView view,HttpServletRequest req){
String code=req.getParameter("code");
System.out.println(code);
OAuthInfo oauth=WeixinUtil.getUserToken(code);
if(oauth!=null){
WxUserinfo userinfo=WeixinUtil.getWxUserInfo(oauth);
System.out.println("oauth===="+userinfo.toString());
}
return view;
}
/**
* 微信jssdk
* @param request
* 2016年8月25日
*/
@RequestMapping("/wxJs")
public void wxJs(HttpServletRequest request,HttpServletResponse response){
String url=request.getParameter("url");
String ticket=(String) request.getSession().getAttribute("ticket");
if(StringUtils.isEmpty(ticket)){
JSONObject json=WeixinUtil.httpRequest(WeixinUtil.jsapi_ticket_url.replaceAll("ACCESS_TOKEN", TokenThread.accessToken.getToken()), "GET", null);
ticket=json.getString("ticket");
request.getSession().setAttribute("ticket", json.getString("ticket"));
request.getSession().setMaxInactiveInterval(7200);
}
Map
微信验证工具 微信js-sdk验证
package com.web.wexin.util;
import java.io.UnsupportedEncodingException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Arrays;
import java.util.Formatter;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
public class SignUtil {
// 与接口配置信息中的Token要一致
private static String token = "weixinqxw";
/**
* 验证签名
*
* @param signature
* @param timestamp
* @param nonce
* @return
*/
public static boolean checkSignature(String signature, String timestamp, String nonce) {
String[] arr = new String[] { token, timestamp, nonce };
// 将token、timestamp、nonce三个参数进行字典序排序
Arrays.sort(arr);
StringBuilder content = new StringBuilder();
for (int i = 0; i < arr.length; i++) {
content.append(arr[i]);
}
MessageDigest md = null;
String tmpStr = null;
try {
md = MessageDigest.getInstance("SHA-1");
// 将三个参数字符串拼接成一个字符串进行sha1加密
byte[] digest = md.digest(content.toString().getBytes());
tmpStr = byteToStr(digest);
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
content = null;
// 将sha1加密后的字符串可与signature对比,标识该请求来源于微信
return tmpStr != null ? tmpStr.equals(signature.toUpperCase()) : false;
}
/**
* 将字节数组转换为十六进制字符串
*
* @param byteArray
* @return
*/
private static String byteToStr(byte[] byteArray) {
String strDigest = "";
for (int i = 0; i < byteArray.length; i++) {
strDigest += byteToHexStr(byteArray[i]);
}
return strDigest;
}
/**
* 将字节转换为十六进制字符串
*
* @param mByte
* @return
*/
private static String byteToHexStr(byte mByte) {
char[] Digit = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };
char[] tempArr = new char[2];
tempArr[0] = Digit[(mByte >>> 4) & 0X0F];
tempArr[1] = Digit[mByte & 0X0F];
String s = new String(tempArr);
return s;
}
/**
* JS-SDK使用权限签名算法
* @param jsapi_ticket
* @param url
* @return
* 2016年8月25日
*/
public static Map sign(String jsapi_ticket, String url) {
Map ret = new HashMap();
String nonce_str = create_nonce_str();
String timestamp = create_timestamp();
String string1;
String signature = "";
//注意这里参数名必须全部小写,且必须有序
string1 = "jsapi_ticket=" + jsapi_ticket +
"&noncestr=" + nonce_str +
"×tamp=" + timestamp +
"&url=" + url;
System.out.println(string1);
try
{
MessageDigest crypt = MessageDigest.getInstance("SHA-1");
crypt.reset();
crypt.update(string1.getBytes("UTF-8"));
signature = byteToHex(crypt.digest());
}
catch (NoSuchAlgorithmException e)
{
e.printStackTrace();
}
catch (UnsupportedEncodingException e)
{
e.printStackTrace();
}
ret.put("url", url);
ret.put("jsapi_ticket", jsapi_ticket);
ret.put("nonceStr", nonce_str);
ret.put("timestamp", timestamp);
ret.put("signature", signature);
return ret;
}
private static String byteToHex(final byte[] hash) {
Formatter formatter = new Formatter();
for (byte b : hash)
{
formatter.format("%02x", b);
}
String result = formatter.toString();
formatter.close();
return result;
}
private static String create_nonce_str() {
return UUID.randomUUID().toString();
}
private static String create_timestamp() {
return Long.toString(System.currentTimeMillis() / 1000);
}
}
package com.web.wexin.vo;
/**
* 网页授权用
* @author admin
* 2016年8月16日
*/
public class OAuthInfo {
private String accessToken;
private String expiresin;
private String refreshToken;
private String openid;
private String scope;
public String getAccessToken() {
return accessToken;
}
public void setAccessToken(String accessToken) {
this.accessToken = accessToken;
}
public String getExpiresin() {
return expiresin;
}
public void setExpiresin(String expiresin) {
this.expiresin = expiresin;
}
public String getRefreshToken() {
return refreshToken;
}
public void setRefreshToken(String refreshToken) {
this.refreshToken = refreshToken;
}
public String getOpenid() {
return openid;
}
public void setOpenid(String openid) {
this.openid = openid;
}
public String getScope() {
return scope;
}
public void setScope(String scope) {
this.scope = scope;
}
@Override
public String toString() {
return "OAuthInfo [accessToken=" + accessToken + ", expiresin="
+ expiresin + ", refreshToken=" + refreshToken + ", openid="
+ openid + ", scope=" + scope + "]";
}
}