从零接入微信公众号(附源码+详细解说)

提前准备:

1.一台云服务器(配置好web项目环境)

2.一个域名(https)

关于服务器的,如果你是小白可以看我的其他博客:

CentOS 服务器配置 jdk +Tomcat + mysql

基于腾讯云的域名绑定和 tomcat 部署

腾讯云申请免费 ssl 证书+tomcat配置https

本文是直接上代码,有些需要修改和解释都会说明。

一、实现接口连接的类( SignUtil 和 WechatController )

我的 web 开发环境是用 SSM 和 maven 搭建的,不懂可以看我的另外一篇博客:

eclipse+maven 配置SSM项目环境

SignUtil.java 的实现代码

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

/**
 * 微信请求校验工具类
 * 
 * @author yy
 *
 */
public class SignUtil {
	// 与接口配置信息中的 Token 要一致
	private static String token = "o2o";

	/**
	 * 验证签名
	 * 
	 * @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 digest
	 * @return
	 */
	private static String byteToStr(byte[] byteArray) {
		String strDigest = "";
		for (int i = 0; i < byteArray.length; i++) {
			strDigest += byteToHexStr(byteArray[i]);
		}
		return strDigest;
	}

	/**
	 * 将字符转换为十六进制字符串
	 * 
	 * @param b
	 * @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;
	}

}

WechatController.java 的实现代码

import java.io.IOException;
import java.io.PrintWriter;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

import com.util.wechat.SignUtil;

@Controller
@RequestMapping("wechat")
public class WechatController {
	
	private static Logger log = LoggerFactory.getLogger(WechatController.class);

	@RequestMapping(method = { RequestMethod.GET })
	public void doGet(HttpServletRequest request, HttpServletResponse response) {
		log.debug("wechat get...");
		// 微信加密签名
		String signature = request.getParameter("signature");
		// 时间戳
		String timestamp = request.getParameter("timestamp");
		// 随机数
		String nonce = request.getParameter("nonce");
		// 随机字符串
		String echostr = request.getParameter("echostr");

		// 通过检验signature对请求进行校验,若校验成功则原样返回echostr,表示接入成功,否则接入失败
		PrintWriter out = null;
		try {
			out = response.getWriter();
			if (SignUtil.checkSignature(signature, timestamp, nonce)) {
				log.debug("wechat get success....");
				out.print(echostr);
			}
		} catch (IOException e) {
			e.printStackTrace();
		} finally {
			if (out != null)
				out.close();
		}
	}
}

到这里并不需要去修改什么代码,只需要将工程导出为 jar 包扔到自己的云服务器上就行方便后面微信测试号接口的链接测试。记得要放到服务器上,不然访问不到!!!

二、微信测试号的申请与接口连接

刚刚开始做微信公众号的时候,我们建议选择微信测试号就好,等熟悉了之后,微信公众号的配置什么的,跟测试号是一样的。

接下来就交大家怎么来申请测试号。

首先进入微信公众号的开发文档:https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1445241432

点击“接口配置信息”旁边的“修改”按钮,然后按要求填写内容,然后点击提交,会提示你是否能连接上的。

三、微信测试号连接代码的实现

上面测试了微信测试号的接口的可以连接上的,现在来实现手机上的微信测试号是如果连接上的。需要通过五个类来实现,分别是:

WechatLoginController.java

UserAccessToken.java 

WechatUser.java

WechatUtil.java

MyX509TrustManager.java

下面会一一讲解这个5个类的实现,以及一些要修改的地方。

提示:

我的代码里面使用 debug 输出信息,如果不需要可以把如下面的代码给删除掉:

private static Logger log = LoggerFactory.getLogger(WechatLoginController.class);
log.debug("weixin login get...");

WechatLoginController.java 的实现代码:

import java.io.IOException;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

import com.dto.UserAccessToken;
import com.dto.WechatUser;
import com.util.wechat.WechatUtil;

/**
 * 获取关注公众号之后的微信用户信息的接口,如果在微信浏览器里访问
 * https://open.weixin.qq.com/connect/oauth2/authorize?appid=wxaxxxxxxxxxxxxx&redirect_uri=https://www.yyzheng.cn/o2o/wechatlogin/logincheck&role_type=1&response_type=1&response_type=code&scope=snsapi_userinfo&state=1#wechat_redirect 
 * 则在这里将会获取到code,之后再通过code获取到access_token进而获取到用户信息
 * 
 * @author yy
 *
 */
@Controller
@RequestMapping("wechatlogin")
public class WechatLoginController {
	private static Logger log = LoggerFactory.getLogger(WechatLoginController.class);

	@RequestMapping(value = "/logincheck", method = { RequestMethod.GET })
	public String doGet(HttpServletRequest request, HttpServletResponse response) {
		log.debug("weixin login get...");
		// 获取微信公众号传输过来的code,通过code可获取access_token,进而获取用户信息
		String code = request.getParameter("code");
		// 这个State可以用来传我们自定义的信息,方便程序调用,这里也可以不用
		// String roleType = request.getParameter("state");
		log.debug("weixin login code:" + code);
		WechatUser user = null;
		String openId = null;
		if (null != code) {
			UserAccessToken token;
			try {
				// 通过code获取access_token
				token = WechatUtil.getUserAccessToken(code);
				log.debug("weixin login token:" + token.toString());
				// 通过token获取accessToken
				String accessToken = token.getAccessToken();
				// 通过token获取openId
				openId = token.getOpenId();
				// 通过access_token和openId获取用户昵称等信息
				user = WechatUtil.getUserInfo(accessToken, openId);
				log.debug("weixin login user:" + user.toString());
				request.getSession().setAttribute("openId", openId);
			} catch (Exception e) {
				log.error("error in getUserAccessToken or getUserInfo or findByOpenId: " + e.toString());
				e.printStackTrace();
			}
		}

		// ========todo begin ===========
		// 前面咱门获取到openid后,可以通过它去数据库判断该微信账号是否在我们网站里有对应的 账号了
		// 没有的话这里可以自动创建上,直接实现微信与咱门网站的无缝对接
		// ========todo end =============

		if (user != null) {
			// 获取到微信验证的信息后返回到指定的路由(需要自己设定)
			return "frontend/index";                                  /*******修改******/
		} else {
			return null;
		}
	}
}

解析:

这段代码需要注意的是最后这一小段代码:

这里的意思是,用户进入微信公众号后,验证通过,并判断用户不为空,就跳转到自己想要跳转的页面。所有需要修改掉 return 的值,代码的值是返回到我项目里的其他页面。只要返回到一个简单的页面就行,这里只是做个测试,看微信测试号是否真的能够接入成功,成功就跳转。不成功就不跳转。

UserAccessToken.java  的实现代码

import com.fasterxml.jackson.annotation.JsonProperty;

/**
 * 用户授权token
 *
 */
public class UserAccessToken {
	
	//获取到的凭证
	@JsonProperty("access_token")
	private String accessToken;
	//凭证有效时间,单位:秒
	@JsonProperty("expires_in")
	private String expiresIn;
	//表示更新令牌,用来获取下一次的访问令牌,这里没太大用处
	@JsonProperty("refresh_token")
	private String refreshToken;
	//该用户在此公众号下的身份表示,对于此微信号具有唯一性
	@JsonProperty("openid")
	private String openId;
	//表示权限范围,这里可省略
	@JsonProperty("scope")
	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 "accessToken:" + this.getAccessToken() + ",openId:" + this.getOpenId();
	}

}

只是一个实体类,用来封装信息的。

WechatUser.java 的实现代码

import java.io.Serializable;

import com.fasterxml.jackson.annotation.JsonProperty;
/**
 * 微信用户实体类

 *
 */
public class WechatUser implements Serializable {

	private static final long serialVersionUID = -4681067645282292327L;

	// openId,标识该公众号下面的该用户的唯一Id
	@JsonProperty("openid")
	private String openId;
	// 用户昵称
	@JsonProperty("nick_name")
	private String nickName;
	// 性别
	@JsonProperty("sex")
	private int sex;
	// 省份
	@JsonProperty("province")
	private String province;
	// 诚实
	@JsonProperty("city")
	private String city;
	// 区
	@JsonProperty("country")
	private String country;
	// 头像图片地址
	@JsonProperty("headimgurl")
	private String headimgurl;
	// 语言
	@JsonProperty("language")
	private String language;
	// 用户权限,这里没什么作用
	@JsonProperty("privilege")
	private String[] privilege;

	public String getOpenId() {
		return openId;
	}

	public void setOpenId(String openId) {
		this.openId = openId;
	}

	public String getNickName() {
		return nickName;
	}

	public void setNickName(String nickName) {
		this.nickName = nickName;
	}

	public int getSex() {
		return sex;
	}

	public void setSex(int sex) {
		this.sex = sex;
	}

	public String getProvince() {
		return province;
	}

	public void setProvince(String province) {
		this.province = province;
	}

	public String getCity() {
		return city;
	}

	public void setCity(String city) {
		this.city = city;
	}

	public String getCountry() {
		return country;
	}

	public void setCountry(String country) {
		this.country = country;
	}

	public String getHeadimgurl() {
		return headimgurl;
	}

	public void setHeadimgurl(String headimgurl) {
		this.headimgurl = headimgurl;
	}

	public String getLanguage() {
		return language;
	}

	public void setLanguage(String language) {
		this.language = language;
	}

	public String[] getPrivilege() {
		return privilege;
	}

	public void setPrivilege(String[] privilege) {
		this.privilege = privilege;
	}

	public static long getSerialversionuid() {
		return serialVersionUID;
	}

}

也是一个实体类,用来封装信息的。

WechatUtil.java 的实现代码

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.ConnectException;
import java.net.URL;

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.controller.wechat.WechatLoginController;
import com.dto.UserAccessToken;
import com.dto.WechatUser;
import com.fasterxml.jackson.core.JsonParseException;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.ObjectMapper;

import net.sf.json.JSONObject;

public class WechatUtil {
	private static Logger log = LoggerFactory.getLogger(WechatLoginController.class);

	/**
	 * 获取UserAccessToken实体类
	 * 
	 * @param code
	 * @return
	 */
	public static UserAccessToken getUserAccessToken(String code) {
		// 测试号信息里的appId
		String appId = "wxaxxxxxxxxxxxxxxxxx";
		log.debug("appId:" + appId);
		// 测试号信息里的appsecret
		String appsecret = "6dxxxxxxxxxxxxxxxxxxxxxxxxxx";
		log.debug("appsecret:" + appsecret);
		// 根据传入的code,拼接出访问微信定义好的接口的URL
		String url = "https://api.weixin.qq.com/sns/oauth2/access_token?appid=" + appId + "&secret=" + appsecret
				+ "&code=" + code + "&grant_type=authorization_code";
		// 向相应URL发送请求获取token json字符串
		String tokenStr = httpsRequest(url, "GET", null).toString();
		log.debug("userAccessToken:" + tokenStr);
		UserAccessToken token = new UserAccessToken();
		ObjectMapper objectMapper = new ObjectMapper();
		try {
			// 将json字符串转换成相应对象
			token = objectMapper.readValue(tokenStr, UserAccessToken.class);
		} catch (JsonParseException e) {
			log.error("获取用户accessToken失败:" + e.getMessage());
			e.printStackTrace();
		} catch (JsonMappingException e) {
			log.error("获取用户accessToken失败:" + e.getMessage());
			e.printStackTrace();
		} catch (IOException e) {
			log.error("获取用户accessToken失败:" + e.getMessage());
			e.printStackTrace();
		}
		if (token == null) {
			log.error("获取用户accessToken失败.");
			return null;
		}
		return token;
	}

	/**
	 * 发起https请求并获取结果
	 * 
	 * @param requestUrl
	 *            请求地址
	 * @param requestMethod
	 *            请求方式(GET、post)
	 * @param outputStr
	 *            提交的数据
	 * @return json字符串
	 */
	private static JSONObject httpsRequest(String requestUrl, String requestMethod, String outputStr) {
		StringBuffer buffer = new StringBuffer();
		JSONObject jsonObject = null;
		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();
			log.debug("https buffer:" + buffer.toString());
		} catch (ConnectException ce) {
			log.error("Wechat server connection timed out");
		} catch (Exception e) {
			log.error("https request error:{}", e);
		}
		jsonObject = JSONObject.fromObject(buffer.toString());
		return jsonObject;
	}

	public static WechatUser getUserInfo(String accessToken, String openId) {
		String url = "https://api.weixin.qq.com/sns/userinfo?access_token=" + accessToken + "&openid=" + openId
				+ "&lang=zh_CN";
		JSONObject jsonObject = WechatUtil.httpsRequest(url, "GET", null);
		WechatUser user = new WechatUser();
		String openid = jsonObject.getString("openid");
		if (openid == null) {
			log.debug("获取用户信息失败。");
			return null;
		}
		user.setOpenId(openid);
		user.setNickName(jsonObject.getString("nickname"));
		user.setSex(jsonObject.getInt("sex"));
		user.setProvince(jsonObject.getString("province"));
		user.setCity(jsonObject.getString("city"));
		user.setCountry(jsonObject.getString("country"));
		user.setHeadimgurl(jsonObject.getString("headimgurl"));
		user.setPrivilege(null);
		// user.setUnionid(jsonObject.getString("unionid"));
		return user;
	}
}

解析

1.获取UserAccessToken工具类,需要修改的是开头的两个地方:

我代码里提供的是乱码,所以这里需要把 appid 和 appsecret 修改为自己的,至于在哪里找???你进入自己申请好的微信测试号就行看到了。

2.代码里面还使用到 JSONObject 这个类,关于这个类的相关 jar 包的引入,之前另一篇博客写过,直接看下就行:

JSONObject 使用maven引入 net.sf.json 时报错解决方法

MyX509TrustManager.java 的实现代码

import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;

import javax.net.ssl.X509TrustManager;

/**
 * 证书信任管理器(用于https请求)
 *
 */
public class MyX509TrustManager implements X509TrustManager{

	@Override
	public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {
		// TODO Auto-generated method stub
		
	}

	@Override
	public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {
		// TODO Auto-generated method stub
		
	}

	@Override
	public X509Certificate[] getAcceptedIssuers() {
		// TODO Auto-generated method stub
		return null;
	}

}

只是直接继承了 X509TrustManager 这个接口,并实现接口里面的方法,并不需要自己写什么。

到这里,需要实现的 java 类都实现了,同样的道理,需要把整个项目导出 jar 包,然后扔到服务器上。不然访问不到!!!

四、尝试连接微信测试号

登录微信测试号页面,找到测试号二维码,然后用自己的微信号扫码,使自己的微信账号位于用户列表中。

然后,在微信上直接打开下面的链接:

https://open.weixin.qq.com/connect/oauth2/authorize?appid=xxxxxx&redirect_uri=https://www.xxxxxx.cn/o2o/wechatlogin/logincheck&role_type=1&response_type=1&response_type=code&scope=snsapi_userinfo&state=1#wechat_redirect 

这个链接,有个参数需要修改下,就是 appid 这个参数后面的值需要换上自己测试号的 appid 哦,还有 redirect_uri 的参数中的域名需要换成自己的域名,还有项目名字如果自己修改了,也要换掉哦。其它的参数不要动。然后把链接发给微信上,用手机微信打开,就能发现成功进入测试号后,会跳转到自己指定的页面。

猜你喜欢

转载自blog.csdn.net/weidong_y/article/details/81141868