微信公众号之智能绑定实现初始版本

微信公众号智能绑定实现步骤:

微信公众号智能绑定功能所有的步骤都是以模拟实现的。
1.模拟打开微信公众帐号登录页面;
    URL地址:https://mp.weixin.qq.com/
2.读取自己微信公众帐号的用户名和密码;
    此处用户名和密码可以持久化到内存,文件,数据库,缓存中都可以,因为在此操作过程中需要多次验证。
3.配置微信公众号接入的URL和Token;
    此处可以和步骤2一样选择持久化。(持久化可以应对多个微信公众帐号操作)
4.模拟微信公众号成功登录;
    此处有时候因网络原因需要重试几次。
5.设置开发模式,服务器回调;
    成功建立连接,启用开发模式此处模拟提交URL和Token。
6.完成步骤5后,在开发者页面读取AppId和AppSecret让其持久化;
    此处根据请求到的页面代码,可以使用正则表达式或者使用:Document、Elements 对象定位AppId和AppSecret的位置,便于获取值。
7.在微信公众号设置页面,获取微信公众号信息持久化到数据库中;
    如:公众号名称,原始帐号ID,公众号头像及二维码(此处头像可以下载保存到自己的服务器中),公众号类型等。
8.成功完成以上步骤后,可以根据自己的需求调用微信相关API获取数据。
    a.获取粉丝信息持久化到数据库中。
    b.创建自定义菜单。
说明:因为此处模拟人工操作,后期微信页面布局及页面相关属性变化,则智能绑定功能会失效,此时需要修改步骤5、7的内容。
    如果后期微信公众号登录只能通过扫描二维码登录则智能绑定功能完全失效,目前企业号使用的就是扫描二维码+密码登录方式。

具体实现过程如下:

1.智能绑定核心类:

import java.io.IOException;

import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.httpclient.Cookie;
import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.HttpException;
import org.apache.commons.httpclient.HttpStatus;
import org.apache.commons.httpclient.NameValuePair;
import org.apache.commons.httpclient.methods.GetMethod;
import org.apache.commons.httpclient.methods.PostMethod;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.select.Elements;
import org.oms.wechat.pojo.WechatAccountInfo;
import org.oms.wechat.pojo.WechatDevInfo;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;

/**
 * 智能绑定核心类
 * 
 * @author sunlight
 *
 */
public class SmartBindUtil {
	public final static Log log = LogFactory.getLog(SmartBindUtil.class);
	// 微信URL
	public final static String HOST_URL = "https://mp.weixin.qq.com";
	// 微信登陆地址
	public final static String LOGIN_URL = "https://mp.weixin.qq.com/cgi-bin/login?lang=zh_CN";
	// 微信登出地址
	public final static String LOGOUT_URL = "http://mp.weixin.qq.com/cgi-bin/logout?t=wxm-logout&lang=zh_CN&token=";
	// 主页地址
	public final static String INDEX_URL = "http://mp.weixin.qq.com/cgi-bin/home?t=home/index&lang=zh_CN&token=";
	// 微信账号信息
	public final static String ACCOUNT_INFO_URL = "http://mp.weixin.qq.com/cgi-bin/settingpage?t=setting/index&action=index&lang=zh_CN&token=";
	// 开发者页面地址
	public final static String DEV_URL = "https://mp.weixin.qq.com/advanced/advanced?action=dev&t=advanced/dev&lang=zh_CN&token=";
	// 切换开发模式
	public final static String DEV_UPDATE_RUL = "https://mp.weixin.qq.com/misc/skeyform?form=advancedswitchform&lang=zh_CN&flag=1&type=2";
	// 服务器配置提交地址
	public final static String DEV_SERVICE_URL = "https://mp.weixin.qq.com/advanced/callbackprofile?t=ajax-response&lang=zh_CN&token=";
	// 获取ACCESS_TOKEN接口
	public final static String ACCESS_TOKEN_URL = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=%s&secret=%s";
	// 获取粉丝列表
	public final static String FANS_URL = "https://api.weixin.qq.com/cgi-bin/user/get?access_token=%s&next_openid=%s";
	// 获取粉丝信息
	public final static String FANS_INFO_URL = "https://api.weixin.qq.com/cgi-bin/user/info?access_token=%s&openid=%s&lang=zh_CN";
	// 删除自定义菜单
	public final static String MENU_DELETE_URL = "https://api.weixin.qq.com/cgi-bin/menu/delete?access_token=%s";
	// 创建自定义菜单
	public final static String MENU_CREATE_URL = "https://api.weixin.qq.com/cgi-bin/menu/create?access_token=%s";

	// 模拟请求所用的参数设置
	public final static String COOKIE_H = "Cookie";
	public final static String CONNECTION_H = "Connection";
	public final static String CONNECTION = "keep-alive";
	public final static String HOST_H = "Host";
	public final static String HOST = "mp.weixin.qq.com";
	public final static String REFERER_H = "Referer";
	public final static String REFERER = "https://mp.weixin.qq.com/";
	public final static String USER_AGENT_H = "User-Agent";
	public final static String USER_AGENT = "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/33.0.1750.154 Safari/537.36";
	public final static String ACCEPT_H = "Accept";
	public final static String ACCEPT = "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8";
	public final static String ACCEPT_ENCODEING_H = "Accept-Encoding";
	public final static String ACCEPT_ENCODEING = "gzip,deflate,sdch";
	public final static String ACCEPT_LANGUAGE_H = "Accept-Language";
	public final static String ACCEPT_LANGUAGE = "zh-CN,zh;q=0.8";
	public final static String CONTENT_TYPE_H = "Content-Type";
	public final static String CONTENT_TYPE = "application/x-www-form-urlencoded; charset=UTF-8";
	public final static String XMLHTTP_REQUEST_H = "X-Requested-With";
	public final static String XMLHTTP_REQUEST = "XMLHttpRequest";
	public final static String UTF_8 = "UTF-8";

	private HttpClient client = new HttpClient();
	private boolean isLogin = false;
	private String cookiestr;
	private String loginUser;
	private String loginPwd;
	private String token;
	
	public SmartBindUtil(String loginUser, String loginPwd) {
		super();
		this.loginUser = loginUser;
		this.loginPwd = loginPwd;
	}
	
	/**
	 * 得到AppId,AppSecret
	 * 
	 * @return
	 */
	public WechatDevInfo getWechatDev() {
		int staus = login();
		if (staus == HttpStatus.SC_OK) {
			isLogin = true;
			return getWechatDevInfo();
		} else {
			return null;
		}
	}

	public int login() {
		if (isLogin) {
			return HttpStatus.SC_OK;
		}
		PostMethod post = new PostMethod(LOGIN_URL);
		setHeader(post);
		setLoginParams(post);
		log.info("正在登陆微信公众平台...");
		try {
			System.setProperty("jsse.enableSNIExtension", "false");
			int status = client.executeMethod(post);
			if (status == HttpStatus.SC_OK) {
				log.info("登陆成功");
				String res = post.getResponseBodyAsString();
				JSONObject retcode = JSON.parseObject(res);
				int ret = Integer.parseInt(retcode.getJSONObject("base_resp")
						.getString("ret"));
				if (ret == 0) {
					log.info("微信端验证成功");
					Cookie[] cookies = client.getState().getCookies();
					StringBuffer cookie = new StringBuffer();
					for (Cookie c : cookies) {
						cookie.append(c.getName()).append("=")
								.append(c.getValue()).append(";");
					}
					this.cookiestr = cookie.toString();
					log.info("正在获取token...");
					String redirect_url = retcode.getString("redirect_url");
					if (redirect_url != null
							&& redirect_url.trim().length() > 0) {
						String[] ss = StringUtils.split(redirect_url, "?");
						String[] ps = null;
						if (ss.length == 2) {
							if (!StringUtils.isBlank(ss[1])
									&& ss[1].indexOf("&") != -1)
								ps = StringUtils.split(ss[1], "&");
						} else if (ss.length == 1) {
							if (!StringUtils.isBlank(ss[0])
									&& ss[0].indexOf("&") != -1)
								ps = StringUtils.split(ss[0], "&");
						}
						if (ps != null) {
							for (String p : ps) {
								if (StringUtils.isBlank(p)) {
									continue;
								}
								String[] tk = StringUtils.split(p, "=");
								if (tk[0] != null
										&& "token".equals(tk[0].trim()
												.toLowerCase())) {
									token = tk[1];
									break;
								}
							}
						}
						log.info("获取token成功...");
					}
					log.info("正在进入微信首页...");
					return index();
				} else {
					log.info("微信端验证失败code: " + ret);
					return ret;
				}
			} else {
				System.err.println("Method failed: " + post.getStatusLine());
				log.info("网络连接错误");
				return -1;
			}
		} catch (Exception e) {
			log.error(e.getMessage(), e);
			e.printStackTrace();
			return -1;
		} finally {
			post.releaseConnection();
		}
	}

	/**
	 * 切换开发模式/编辑模式
	 * 
	 * @param flag
	 *            开启1关闭0
	 * @param type
	 *            开发模式2编辑模式1
	 * @return
	 */
	public Result<Integer> enabledDev(int flag, int type) {
		Result<Integer> result = new Result<Integer>();
		int staus = login();
		if (staus == HttpStatus.SC_OK) {
			isLogin = true;
			PostMethod post = new PostMethod(DEV_UPDATE_RUL);
			setHeader(post);
			NameValuePair[] params = new NameValuePair[] {
					new NameValuePair("flag", "" + flag),
					new NameValuePair("type", "" + type),
					new NameValuePair("token", token) };
			post.setRequestBody(params);
			try {
				log.info("正在切换开发模式/编辑模式...");
				staus = client.executeMethod(post);
				if (staus == HttpStatus.SC_OK) {
					log.info("服务器连接成功");
					String res = post.getResponseBodyAsString();
					JSONObject retcode = JSON.parseObject(res);
					JSONObject base = retcode.getJSONObject("base_resp");
					result.setObject(Integer.parseInt(base.getString("ret")));
					result.setMsg(base.getString("err_msg"));
				} else {
					result.setObject(staus);
					result.setMsg("网络连接错误,请稍后再试");
				}
			} catch (Exception e) {
				log.error(e.getMessage(), e);
				result.setObject(staus);
				result.setMsg("网络连接错误,请稍后再试");
			} finally {
				post.releaseConnection();
			}
		} else {
			result.setObject(staus);
			result.setMsg("服务器登陆失败,请稍后再试");
		}
		return result;
	}

	/**
	 * 设置开发模式,服务器回调
	 * 
	 * @param url
	 * @param callback_token
	 * @return
	 */
	public Result<Integer> setDevServiceUrl(String url, String callback_token) {
		Result<Integer> result = new Result<Integer>();
		int staus = login();
		if (staus == HttpStatus.SC_OK) {
			isLogin = true;
			PostMethod post = new PostMethod(DEV_SERVICE_URL + token);
			setHeader(post);
			NameValuePair[] params = new NameValuePair[] {
					new NameValuePair("url", "" + url),
					new NameValuePair("callback_token", callback_token) };
			post.setRequestBody(params);
			try {
				log.info("正在设置公众平台回调...");
				staus = client.executeMethod(post);
				if (staus == HttpStatus.SC_OK) {
					log.info("服务器连接成功");
					String res = post.getResponseBodyAsString();
					// TODO............
					System.out.println("res========" + res);
					JSONObject retcode = JSON.parseObject(res);
					staus = Integer.parseInt(retcode.getString("ret"));
					result.setObject(Integer.parseInt(retcode.getString("ret")));
					result.setMsg(retcode.getString("msg"));
				} else {
					result.setObject(staus);
					result.setMsg("网络连接错误,请稍后再试");
				}
			} catch (Exception e) {
				log.info(e.getMessage(), e);
				result.setObject(staus);
				result.setMsg("网络连接错误,请稍后再试");
			} finally {
				post.releaseConnection();
			}
		} else {
			result.setObject(staus);
			result.setMsg("服务器登陆失败,请稍后再试");
		}
		return result;
	}

	/**
	 * 得到微信公众平台个人信息
	 * 
	 * @return
	 */
	public WechatAccountInfo getAccount() {
		// TODO获取微信账号基本信息
		log.info("获取账户信息...");
		WechatAccountInfo account = null;
		GetMethod get = new GetMethod(ACCOUNT_INFO_URL + token);
		setHeader(get);
		try {
			int status = client.executeMethod(get);
			if (status == HttpStatus.SC_OK) {
				log.info("已经连接,正在接收数据");
				String res = get.getResponseBodyAsString();
				Document doc = Jsoup.parseBodyFragment(res);
				Elements eles = doc.select("#settingArea .meta_content");
				if (eles != null && eles.size() > 0) {
					account = new WechatAccountInfo();
					account.setAccountName(eles.get(0).text());
					account.setHeadImage(HOST_URL
							+ eles.get(1).select("img").attr("src"));
					account.setAccountId(eles.get(3).text());
					account.setWechatNumber(eles.get(4).text());
					account.setType(eles.get(5).text());
					account.setAuthenticate(eles.get(6).text());
				}
			}
		} catch (Exception e) {
			log.error("网络连接发生错误", e);
		} finally {
			get.releaseConnection();
		}
		return account;
	}

	/**
	 * 得到AppId,AppSecret
	 * 
	 * @return
	 */
	public WechatDevInfo getWechatDevInfo() {
		// TODO 得到AppId,AppSecret
		log.info("获取appid,appsecret信息...");
		WechatDevInfo devInfo = null;
		GetMethod get = new GetMethod(DEV_URL + token);
		setHeader(get);
		try {
			int status = client.executeMethod(get);
			if (status == HttpStatus.SC_OK) {
				log.info("已经连接,正在接收数据");
				String res = get.getResponseBodyAsString();
				Document doc = Jsoup.parseBodyFragment(res);
				Elements eles = doc.select(".developer_info_wrp");
				if (eles != null && eles.size() > 0) {
					Elements datas = eles.select(".frm_vertical_pt");
					devInfo = new WechatDevInfo();
					devInfo.setAppId(datas.get(0).text());
					devInfo.setAppSecret(datas.get(1).text()
							.replaceAll("重置", "").trim());
				}
			}
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			get.releaseConnection();
		}
		return devInfo;
	}

	/**
	 * 登出操作
	 * 
	 * @return 异常CODE
	 * @throws HttpException
	 * @throws IOException
	 */
	public int logout() throws HttpException, IOException {
		log.info("退出服务...");
		GetMethod get = new GetMethod(LOGOUT_URL + token);
		setHeader(get);
		int status = client.executeMethod(get);
		get.releaseConnection();
		return status;
	}

	/**
	 * 进入微信首页
	 * 
	 * @return 异常CODE
	 * @throws HttpException
	 * @throws IOException
	 */
	private int index() throws HttpException, IOException {
		GetMethod get = new GetMethod(INDEX_URL + token);
		setHeader(get);
		int status = client.executeMethod(get);
		get.releaseConnection();
		return status;
	}

	private void setHeader(PostMethod post) {
		post.setRequestHeader(CONNECTION_H, CONNECTION);
		post.setRequestHeader(HOST_H, HOST);
		post.setRequestHeader(REFERER_H, REFERER);
		post.setRequestHeader(USER_AGENT_H, USER_AGENT);
		if (this.cookiestr != null && this.cookiestr.length() > 0) {
			post.setRequestHeader(COOKIE_H, this.cookiestr);
		}
	}

	private void setHeader(GetMethod get) {
		get.setRequestHeader(CONNECTION_H, CONNECTION);
		get.setRequestHeader(HOST_H, HOST);
		get.setRequestHeader(REFERER_H, REFERER);
		get.setRequestHeader(USER_AGENT_H, USER_AGENT);
		if (this.cookiestr != null && this.cookiestr.length() > 0) {
			get.setRequestHeader(COOKIE_H, this.cookiestr);
		}
	}

	private void setLoginParams(PostMethod post) {
		post.setRequestHeader(CONTENT_TYPE_H, CONTENT_TYPE);
		post.setRequestHeader(XMLHTTP_REQUEST_H, XMLHTTP_REQUEST);
		NameValuePair[] params = new NameValuePair[] {
				new NameValuePair("username", this.loginUser),
				new NameValuePair("pwd", DigestUtils.md5Hex(this.loginPwd
						.getBytes())), new NameValuePair("f", "json"),
				new NameValuePair("imgcode", "") };
		post.setQueryString(params);
	}
}

智能绑定执行类:
import org.oms.wechat.pojo.WechatAccountInfo;
import org.oms.wechat.pojo.WechatDevInfo;
/**
 * 智能绑定执行类
 * @author Sunlight
 *
 */
public class WechatBind {
	private static String msg;
	private static String url="http://omsvip.sinaapp.com/coreServlet";
	private static String token="weixinCourse";

	public static String SmartBind(String username, String password) {
		// 开始绑定帐号
		// 初始化登录信息
		SmartBindUtil smartBind = new SmartBindUtil(username, password);
		//登录成功后,在开发者中心获取开发者ID(AppId、AppSecret)
		WechatDevInfo devInfo=smartBind.getWechatDev();
		System.out.println("devInfo="+devInfo);
		//获取微信公众号设置基本信息(名称、头像、类型、认证情况等)
		WechatAccountInfo account = smartBind.getAccount();
		if (account == null) {
			msg = "{success:false,msg:\"登陆微信服务器失败,请检查用户名/密码是否正确\"}";
		}
		System.out.println("account="+account);

		// 启动开发模式
		if (msg == null) {
			Result<Integer> result = smartBind.enabledDev(1, 2);
			if (result.getObject() != 0) {
				msg = "{success:false,msg:\"" + result.getMsg() + "\"}";
			}
		}
		// 验证服务器接口回调,此处修改服务器配置中的URL和Token
		if (msg == null) {
			Result<Integer> result = smartBind.setDevServiceUrl(url, token);
			if (result.getObject() != 0) {
				msg = "{success:false,msg:\"" + result.getMsg() + "\"}";
			}
		}
		if(msg==null){
			msg="智能绑定成功!";
		}
		return msg;
	}
}

Result<T>.class 这个可以不要,也可以在前面文章中拷贝!

需要用到的2个Pojo 公众号信息类,代码如下:

/**
 * 微信账号基本信息
 * 
 * @author sunlight
 *
 */
public class WechatAccountInfo {
	/**
	 * 原始ID
	 */
	private String accountId;
	/**
	 * 名称
	 */
	private String accountName;
	/**
	 * 微信头像
	 */
	private String headImage;
	/**
	 * 微信号
	 */
	private String wechatNumber;
	/**
	 * 账号类型
	 */
	private String type;
	/**
	 * 认证情况
	 */
	private String authenticate;

	public String getAccountId() {
		return accountId;
	}

	public void setAccountId(String accountId) {
		this.accountId = accountId;
	}

	public String getAccountName() {
		return accountName;
	}

	public void setAccountName(String accountName) {
		this.accountName = accountName;
	}

	public String getHeadImage() {
		return headImage;
	}

	public void setHeadImage(String headImage) {
		this.headImage = headImage;
	}

	public String getWechatNumber() {
		return wechatNumber;
	}

	public void setWechatNumber(String wechatNumber) {
		this.wechatNumber = wechatNumber;
	}

	public String getType() {
		return type;
	}

	public void setType(String type) {
		this.type = type;
	}

	public String getAuthenticate() {
		return authenticate;
	}

	public void setAuthenticate(String authenticate) {
		this.authenticate = authenticate;
	}

	@Override
	public String toString() {
		return "WechatAccountInfo [accountId=" + accountId + ", accountName="
				+ accountName + ", headImage=" + headImage + ", wechatNumber="
				+ wechatNumber + ", type=" + type + ", authenticate="
				+ authenticate + "]";
	}

}

开发者信息类:
/**
 * 微信开发者信息
 * 
 * @author sunlight
 *
 */
public class WechatDevInfo {
	private String appId;
	private String appSecret;

	public String getAppId() {
		return appId;
	}

	public void setAppId(String appId) {
		this.appId = appId;
	}

	public String getAppSecret() {
		return appSecret;
	}

	public void setAppSecret(String appSecret) {
		this.appSecret = appSecret;
	}

	@Override
	public String toString() {
		return "WechatDevInfo [appId=" + appId + ", appSecret=" + appSecret
				+ "]";
	}

}

最后附上执行结果:

微信公众号之智能绑定实现初始版本_第1张图片

注意:因为微信公众号页面等相关调整频繁,因此智能绑定微信公众号只是模拟用户登录,所以微信公众号官网页面等相关信息发生变化会导致智能绑定功能失效!

因此,微信修改一次我们的代码也会变化一次。

本文章因时间问题,编写的的不是很完美,等有时间重新整理一份!

本人微信公众号:PainPoint

扫描二维码关注我.

微信公众号之智能绑定实现初始版本_第2张图片




你可能感兴趣的:(微信公众号之智能绑定实现初始版本)