微信公众号开发-接入
一 首先实现内网穿透,公众号需要连接我们的服务器,内外无法访问,所以先实现自己的内网可以测试时连接外网,下载natapp,选择windows,顺便下载config,ini 配置文件。注册好购买免费的隧道 然后将token写入配置文件中,操作很简单,windows中需要加入环境变量,因为我直接放到D盘的natapp文件夹中,所以路劲为D:\natapp即可,双击.exe 就能启动 http://phxgaj.natappfree.cc -> 127.0.0.1:90 通过前面的网址phxgaj.natappfree.cc 访问就能连接我们自己的内网环境了。
二 自然是注册微信公众号了,注册的时候要瞪大眼睛了,微信公众平台 微信开发平台 服务号订阅号小程序一定要分清楚,一个邮箱只能注册一个功能,所以要慎重选,我注册的是 微信公众平台的 服务号,适合公司使用
三 公众号接入,在开发者配置中,写进这个方法,我用的是springmvc,所以要写到这个指定的路径
import java.io.IOException;
5 import java.io.UnsupportedEncodingException;
6 import java.security.MessageDigest;
7 import java.security.NoSuchAlgorithmException;
8 import java.util.Arrays;
9 import java.util.Map;
10
11 import javax.servlet.http.HttpServletRequest;
12 import javax.servlet.http.HttpServletResponse;
13
14 import org.slf4j.Logger;
15 import org.slf4j.LoggerFactory;
16 import org.springframework.beans.BeansException;
17 import org.springframework.context.ApplicationContext;
18 import org.springframework.context.ApplicationContextAware;
19 import org.springframework.stereotype.Component;
20 import org.springframework.stereotype.Controller;
21 import org.springframework.web.bind.annotation.RequestMapping;
22 import org.springframework.web.bind.annotation.RequestMethod;
23
24 import com.alibaba.fastjson.JSON;
25 import com.alibaba.fastjson.JSONObject;
26 import com.fuyin.mp.entity.AccessToken;
27 import com.fuyin.mp.utils.GetMenuJson;
28 import com.fuyin.mp.utils.MessageHandlerUtil;
29 import com.fuyin.mp.utils.NetWorkHelper;
30 import com.fuyin.mp.utils.WxaApi;
31 @Controller
32 @RequestMapping("wxconnect.action")
33 public class WxBase {
34 private static final Logger logger = LoggerFactory.getLogger(WxBase.class);
35 /*
36 * 自定义token, 用作生成签名,从而验证安全性
37 * */
38 private final String TOKEN = WxaApi.TOKEN;
39 /**
40 * 检验签名
41 * @param req
42 * @param res
43 */
44 @RequestMapping(method = RequestMethod.GET)
45 public void wxconnect(HttpServletRequest req,HttpServletResponse res){
46 logger.debug("-----开始校验签名-----");
47 /**
48 * 接收微信服务器发送请求时传递过来的参数
49 */
50 String signature = req.getParameter("signature");
51 String timestamp = req.getParameter("timestamp");
52 String nonce = req.getParameter("nonce"); //随机数
53 String echostr = req.getParameter("echostr");//随机字符串
54
55 /**
56 * 将token、timestamp、nonce三个参数进行字典序排序
57 * 并拼接为一个字符串
58 */
59 String sortStr = sort(TOKEN,timestamp,nonce);
60 /**
61 * 字符串进行shal加密
62 */
63 String mySignature = shal(sortStr);
64 /**
65 * 校验微信服务器传递过来的签名 和 加密后的字符串是否一致, 若一致则签名通过
66 */
67 if(!"".equals(signature) && !"".equals(mySignature) && signature.equals(mySignature)){
68 try {
69 logger.debug("-----签名校验通过-----");
70 res.getWriter().write(echostr);
71 } catch (IOException e) {
72 // TODO Auto-generated catch block
73 e.printStackTrace();
74 }
75 }else {
76 logger.debug("-----校验签名失败-----");
77 }
78 }
79 @RequestMapping(method = RequestMethod.POST)
80 protected void doPost(HttpServletRequest request, HttpServletResponse response) {
81 // TODO 接收、处理、响应由微信服务器转发的用户发送给公众帐号的消息
82 // 将请求、响应的编码均设置为UTF-8(防止中文乱码)
83 try {
84 request.setCharacterEncoding("UTF-8");
85 response.setCharacterEncoding("UTF-8");
86 System.out.println("请求进入");
87 String result = "";
88 try {
89 Map<String,String> map = MessageHandlerUtil.parseXml(request);
90 System.out.println("开始构造消息");
91 result = MessageHandlerUtil.buildResponseMessage(map, "你好");
92 System.out.println(result);
93 if(result.equals("")){
94 result = "未正确响应";
95 }
96 } catch (Exception e) {
97 e.printStackTrace();
98 System.out.println("发生异常:"+ e.getMessage());
99 }
100 response.getWriter().write(result);
101 } catch (UnsupportedEncodingException e) {
102 // TODO Auto-generated catch block
103 e.printStackTrace();
104 } catch (IOException e) {
105 // TODO Auto-generated catch block
106 e.printStackTrace();
107 }
108 }
109
110 /**
111 * 参数排序
112 * @param token
113 * @param timestamp
114 * @param nonce
115 * @return
116 */
117 public String sort(String token, String timestamp, String nonce) {
118 String[] strArray = {token, timestamp, nonce};
119 Arrays.sort(strArray);
120 StringBuilder sb = new StringBuilder();
121 for (String str : strArray) {
122 sb.append(str);
123 }
124 return sb.toString();
125 }
126
127 /**
128 * 字符串进行shal加密
129 * @param str
130 * @return
131 */
132 public String shal(String str){
133 try {
134 MessageDigest digest = MessageDigest.getInstance("SHA-1");
135 digest.update(str.getBytes());
136 byte messageDigest[] = digest.digest();
137
138 StringBuffer hexString = new StringBuffer();
139 // 字节数组转换为 十六进制 数
140 for (int i = 0; i < messageDigest.length; i++) {
141 String shaHex = Integer.toHexString(messageDigest[i] & 0xFF);
142 if (shaHex.length() < 2) {
143 hexString.append(0);
144 }
145 hexString.append(shaHex);
146 }
147 return hexString.toString();
148
149 } catch (NoSuchAlgorithmException e) {
150 e.printStackTrace();
151 }
152 return "";
153 }
154 }
WxApi 这个是工具类,因为测试用的穿透所以一直要改Ip,干脆提取出来修改方便,上面代码是接入微信服务。
import javax.annotation.PostConstruct;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.ApplicationEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.context.support.AbstractApplicationContext;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.fuyin.mp.entity.AccessToken;
import com.fuyin.mp.utils.GetMenuJson;
import com.fuyin.mp.utils.NetWorkHelper;
import com.fuyin.mp.utils.WxaApi;
/**
* 线程 自动获得token
* @author Administrator
*
*/
@Controller
public class AccessTokenInit implements ApplicationContextAware{
private static final Logger logger = LoggerFactory.getLogger(AccessTokenInit.class);
@Override
public void setApplicationContext(ApplicationContext arg0) throws BeansException {
new Thread(new Runnable() {
@Override
public void run() {
while (true) {
try {
//获取accessToken
System.out.println("开始获取TOKEN");
WxaApi.accessToken = getAccessToken(WxaApi.appID, WxaApi.appsecret);
//获取成功
if ( WxaApi.accessToken== null) {
//获取失败
Thread.sleep(1000 * 3); //获取的access_token为空 休眠3秒
} else {
//获取到access_token 休眠7000秒,大约2个小时左右
Thread.sleep(7000 * 1000);
//Thread.sleep(10 * 1000);//10秒钟获取一次
}
} catch (Exception e) {
System.out.println("发生异常:" + e.getMessage());
e.printStackTrace();
try {
Thread.sleep(1000 * 10); //发生异常休眠1秒
} catch (Exception e1) {
}
}
}
}
}).start();
}
/**
* 获取access_token
*
* @return AccessToken
*/
private AccessToken getAccessToken(String appId, String appSecret) {
NetWorkHelper netHelper = new NetWorkHelper();
String Url=WxaApi.GetTokenApi.replace("APPID", appId).replaceAll("APPSECRET", appSecret);;
//此请求为https的get请求,返回的数据格式为{"access_token":"ACCESS_TOKEN","expires_in":7200}
String result = netHelper.getHttpsResponse(Url, "",null);
System.out.println("获取到的access_token="+result);
//使用FastJson将Json字符串解析成Json对象
JSONObject json = JSON.parseObject(result);
AccessToken token = new AccessToken();
token.setAccessToken(json.getString("access_token"));
token.setExpiresin(json.getInteger("expires_in"));
return token;
}
}
这是一个springmvc启动以后就会启动的线程,用来自动获取token
工具类 用来发送https请求
1 import javax.net.ssl.*;
2 import java.io.BufferedReader;
3 import java.io.InputStream;
4 import java.io.InputStreamReader;
5 import java.io.OutputStream;
6 import java.io.UnsupportedEncodingException;
7 import java.net.URL;
8 import java.net.URLEncoder;
9 import java.security.cert.CertificateException;
10 import java.security.cert.X509Certificate;
11
12 /**
13 * 访问网络用到的工具类
14 */
15 public class NetWorkHelper {
16
17 /**
18 * 发起Https请求
19 * @param reqUrl 请求的URL地址
20 * @param requestMethod
21 * @return 响应后的字符串
22 */
23 public String getHttpsResponse(String reqUrl, String requestMethod,String outputStr) {
24 URL url;
25 InputStream is;
26 String resultData = "";
27 try {
28 url = new URL(reqUrl);
29 HttpsURLConnection con = (HttpsURLConnection) url.openConnection();
30 TrustManager[] tm = {xtm};
31
32 SSLContext ctx = SSLContext.getInstance("TLS");
33 ctx.init(null, tm, null);
34 con.setSSLSocketFactory(ctx.getSocketFactory());
35 con.setHostnameVerifier(new HostnameVerifier() {
36 @Override
37 public boolean verify(String arg0, SSLSession arg1) {
38 return true;
39 }
40 });
41
42
43 con.setDoInput(true); //允许输入流,即允许下载
44
45 //在android中必须将此项设置为false
46 con.setDoOutput(true); //允许输出流,即允许上传
47 con.setUseCaches(false); //不使用缓冲
48 if (null != requestMethod && !requestMethod.equals("")) {
49 con.setRequestMethod(requestMethod); //使用指定的方式
50 } else {
51 con.setRequestMethod("GET"); //使用get请求
52 }
53 con.setRequestProperty("content-type",
54 "application/x-www-form-urlencoded");
55 // 当outputStr不为null时向输出流写数据
56 if (null != outputStr) {
57 OutputStream outputStream = con.getOutputStream();
58 // 注意编码格式
59 outputStream.write(outputStr.getBytes("UTF-8"));
60 outputStream.close();
61 }
62 is = con.getInputStream(); //获取输入流,此时才真正建立链接
63 InputStreamReader isr = new InputStreamReader(is);
64 BufferedReader bufferReader = new BufferedReader(isr);
65 String inputLine;
66 while ((inputLine = bufferReader.readLine()) != null) {
67 resultData += inputLine + "\n";
68 }
69 // System.out.println(resultData);
70
71 } catch (Exception e) {
72 e.printStackTrace();
73 }
74 return resultData;
75 }
76
77 X509TrustManager xtm = new X509TrustManager() {
78 @Override
79 public X509Certificate[] getAcceptedIssuers() {
80 return null;
81 }
82
83 @Override
84 public void checkServerTrusted(X509Certificate[] arg0, String arg1)
85 throws CertificateException {
86
87 }
88
89 @Override
90 public void checkClientTrusted(X509Certificate[] arg0, String arg1)
91 throws CertificateException {
92
93 }
94 };
95 /**
96 * 对URL地址进行EnCode处理
97 * @param url
98 * @return
99 */
100 public static String urlEnCode(String url)
101 {
102 String enCodedUrl = "";
103
104 try
105 {
106 enCodedUrl = URLEncoder.encode(url, "utf-8");
107 }
108 catch (UnsupportedEncodingException e)
109 {
110 // TODO Auto-generated catch block
111 e.printStackTrace();
112 System.out.println("转码失败!");
113 }
114
115 return enCodedUrl;
116 }
117 }
线程启动可能会获取两次token是因为springmvc把项目的Bean扫描了两次,可以把applicationContext.xml配置成只扫描mapper,其他不扫,由springmvc.xml去扫面serveice 和controller,因为我这样还是失败了,所以把线程的类也定义成controller,这样用前面的方法只会扫描一次