参考:https://www.cnblogs.com/liuhongfeng/p/4848851.html
一:介绍。
接口调用请求说明
http请求方式: GET https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=APPID&secret=APPSECRET
参数说明
参数 | 是否必须 | 说明 |
---|---|---|
grant_type | 是 | 获取access_token填写client_credential |
appid | 是 | 第三方用户唯一凭证 |
secret | 是 | 第三方用户唯一凭证密钥,即appsecret |
返回说明
正常情况下,微信会返回下述JSON数据包给公众号:
{"access_token":"ACCESS_TOKEN","expires_in":7200}
参数 | 说明 |
---|---|
access_token | 获取到的凭证 |
expires_in | 凭证有效时间,单位:秒 |
错误时微信会返回错误码等信息,JSON数据包示例如下(该示例为AppID无效错误):
{"errcode":40013,"errmsg":"invalid appid"}
我们可以直接
二:封装基本类。
package com.wx.bean; /** * 封装token类。 */ public class AccessToken { //获取到的凭证 private String accessToken; //凭证有效时间,单位:秒 private int expiresin; public String getAccessToken() { return accessToken; } public void setAccessToken(String accessToken) { this.accessToken = accessToken; } public int getExpiresin() { return expiresin; } public void setExpiresin(int expiresin) { this.expiresin = expiresin; } }
三:如何获取token。
一:直接访问浏览器。替换自己的appid和secret。
https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=wxb05f6ccd16cf38fb&secret=b50d2e0911e255f4723f93d73233b04f
二:编写程序,模拟https连接,获得token。
对于https请求,我们需要一个证书信任管理器,这个管理器类需要自己定义,但需要实现X509TrustManager接口,
package com.wx.util; import java.security.cert.CertificateException; import java.security.cert.X509Certificate; import javax.net.ssl.X509TrustManager; /** * 类名: MyX509TrustManager </br> * 描述: 信任管理器 </br> * 开发人员: 六 </br> * 发布版本:V1.0 </br> */ public class MyX509TrustManager implements X509TrustManager { // 检查客户端证书 public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException { } // 检查服务器端证书 public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException { } // 返回受信任的X509证书数组 public X509Certificate[] getAcceptedIssuers() { return null; } }
微信服务器返回的是json数据,如何从json里面解析出来的值?所以要转换成Java对象。
封装一个通用的Util。
package com.wx.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 javax.net.ssl.HttpsURLConnection; import javax.net.ssl.SSLContext; import javax.net.ssl.SSLSocketFactory; import javax.net.ssl.TrustManager; import com.alibaba.fastjson.JSONException; import com.alibaba.fastjson.JSONObject; import com.wx.bean.AccessToken; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * 类名: CommonUtil </br> * 描述: 通用工具类 </br> * 开发人员: 六 </br> * 创建时间: 2018-8-9 </br> * 发布版本:V1.0 </br> */ public class CommonUtil { private static Logger log = LoggerFactory.getLogger(CommonUtil.class); // 凭证获取(GET) public final static String token_url = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=APPID&secret=APPSECRET"; /** * 发送https请求 * * @param requestUrl 请求地址 * @param requestMethod 请求方式(GET、POST) * @param outputStr 提交的数据 * @return JSONObject(通过JSONObject.get(key)的方式获取json对象的属性值) */ public static JSONObject httpsRequest(String requestUrl, String requestMethod, String outputStr) { 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 conn = (HttpsURLConnection) url.openConnection(); conn.setSSLSocketFactory(ssf); conn.setDoOutput(true); conn.setDoInput(true); conn.setUseCaches(false); // 设置请求方式(GET/POST) conn.setRequestMethod(requestMethod); // 当outputStr不为null时向输出流写数据 if (null != outputStr) { OutputStream outputStream = conn.getOutputStream(); // 注意编码格式 outputStream.write(outputStr.getBytes("UTF-8")); outputStream.close(); } // 从输入流读取返回内容 InputStream inputStream = conn.getInputStream(); InputStreamReader inputStreamReader = new InputStreamReader(inputStream, "utf-8"); BufferedReader bufferedReader = new BufferedReader(inputStreamReader); String str = null; StringBuffer buffer = new StringBuffer(); while ((str = bufferedReader.readLine()) != null) { buffer.append(str); } // 释放资源 bufferedReader.close(); inputStreamReader.close(); inputStream.close(); inputStream = null; conn.disconnect(); jsonObject = JSONObject.parseObject(buffer.toString()); } catch (ConnectException ce) { log.error("连接超时:{}", ce); } catch (Exception e) { log.error("https请求异常:{}", e); } return jsonObject; } /** * 获取接口访问凭证 * * @param appid 凭证 * @param appsecret 密钥 * @return */ public static AccessToken getToken(String appid, String appsecret) { AccessToken token = null; String requestUrl = token_url.replace("APPID", appid).replace("APPSECRET", appsecret); // 发起GET请求获取凭证 JSONObject jsonObject = httpsRequest(requestUrl, "GET", null); if (null != jsonObject) { try { token = new AccessToken(); token.setAccessToken(jsonObject.getString("access_token")); token.setExpiresin(jsonObject.getInteger("expires_in")); } catch (JSONException e) { token = null; // 获取token失败 log.error("获取token失败 errcode:{} errmsg:{}", jsonObject.getInteger("errcode"), jsonObject.getString("errmsg")); } } return token; } }
然后写个token的测试类就可以获取到token
import java.io.BufferedReader; import java.io.InputStream; import java.io.InputStreamReader; 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 com.wx.bean.AccessToken; import com.wx.util.CommonUtil; import com.wx.util.MyX509TrustManager; import org.junit.Test; public class TokenTest { @Test public void testGetToken1() throws Exception { String tokenUrl = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=wxb05f6ccd16cf38fb&secret=b50d2e0911e255f4723f93d73233b04f"; // 建立连接 URL url = new URL(tokenUrl); HttpsURLConnection httpUrlConn = (HttpsURLConnection) url.openConnection(); // 创建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(); httpUrlConn.setSSLSocketFactory(ssf); httpUrlConn.setDoOutput(true); httpUrlConn.setDoInput(true); // 设置请求方式(GET/POST) httpUrlConn.setRequestMethod("GET"); // 取得输入流 InputStream inputStream = httpUrlConn.getInputStream(); InputStreamReader inputStreamReader = new InputStreamReader( inputStream, "utf-8"); BufferedReader bufferedReader = new BufferedReader(inputStreamReader); // 读取响应内容 StringBuffer buffer = new StringBuffer(); String str = null; while ((str = bufferedReader.readLine()) != null) { buffer.append(str); } bufferedReader.close(); inputStreamReader.close(); // 释放资源 inputStream.close(); httpUrlConn.disconnect(); // 输出返回结果 System.out.println(buffer); } @Test public void testGetToken2() { AccessToken token = CommonUtil.getToken("appID","appsecret"); System.out.println("access_token:"+token.getAccessToken()); System.out.println("expires_in:"+token.getExpiresin()); } }
在项目中的其他类,可以通过调用 TokenThread.accessToken.getToken() 来得到接口访问凭证access_token。
四:问题:但是如何让这个保存工作隔7200 秒进行保存呢?
方案:
做一个定时器。
做一个web实现监听器。
定期获取并存储access_token的流程为:
Web服务器启动时就加载一个Servlet,在Servlet的init()方法中启动一个线 程,在线程的run()方法中通过死循环+Thread.sleep()的方式定期获取access_token,然后将获取到的 access_token保存在public static修饰的变量中。
在工程中创建一个InitServlet类,该类的代码如下:
InitServlet类只重写了init()方法,并没有重写doGet()和doPost()两个方法,
因为我们并不打算让 InitServlet来处理访问请求。
package com.wx.servlet; import javax.servlet.ServletException; import javax.servlet.annotation.WebInitParam; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; import com.wx.util.CommonUtil; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * 类名: InitServlet </br> * 描述: 初始化servlet </br> * 包:com.wx.servlet</br> * 开发人员: 六 </br> * 创建时间: 2018-8-9 </br> * 发布版本:V1.0 </br> */ @WebServlet( name = "AccessTokenServlet", urlPatterns = {"/AccessTokenServlet"}, loadOnStartup = 1, initParams = { @WebInitParam(name = "appId", value = "wxb05f6ccd16cf38fb"), @WebInitParam(name = "appSecret", value = "b50d2e0911e255f4723f93d73233b04f") }) public class InitServlet extends HttpServlet { private static final long serialVersionUID = 1L; private static Logger log = LoggerFactory.getLogger(CommonUtil.class); public void init() throws ServletException { System.out.println("启动得到Token的Servlet"); // 获取web.xml中配置的参数 TokenThread.appid = getInitParameter("appId"); TokenThread.appsecret = getInitParameter("appSecret"); log.info("weixin api appid:{}", TokenThread.appid); log.info("weixin api appsecret:{}", TokenThread.appsecret); // 未配置appid、appsecret时给出提示 if ("".equals(TokenThread.appid) || "".equals(TokenThread.appsecret)) { log.error("appid and appsecret configuration error, please check carefully."); } else { // 启动定时获取access_token的线程 new Thread(new TokenThread()).start(); } } }
package com.wx.servlet; import com.wx.bean.AccessToken; import com.wx.bean.Ttoken; import com.wx.util.CommonUtil; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * 类名: TokenThread </br> * 描述: 定时获取微信access_token的线程 </br> * 开发人员: 六 </br> * 创建时间: 2018-8-9 </br> * 发布版本:V1.0 </br> */ public class TokenThread implements Runnable { private static Logger log = LoggerFactory.getLogger(TokenThread.class); /* @Autowired TtokenMapper ttokenMapper; */ // 第三方用户唯一凭证 public static String appid = ""; // 第三方用户唯一凭证密钥 public static String appsecret = ""; public static AccessToken accessToken = null; public void run() { while (true) { try { accessToken = CommonUtil.getToken(appid, appsecret); if (null != accessToken) { //调用存储到数据库 System.out.println("Token的值:"+accessToken); Ttoken ttoken = new Ttoken(3,accessToken.getAccessToken(),accessToken.getExpiresin(),null); System.out.println("Ttoken的值:"+ttoken); //ttokenMapper.select(); //ttokenMapper.save(ttoken); log.info("获取access_token成功,有效时长{}秒 token:{}", accessToken.getExpiresin(), accessToken.getAccessToken()); // 休眠7000秒 Thread.sleep((accessToken.getExpiresin() - 200)*1000); } else { // 如果access_token为null,60秒后再获取 Thread.sleep(60 * 1000); } } catch (InterruptedException e) { try { Thread.sleep(60 * 1000); } catch (InterruptedException e1) { log.error("{}", e1); } log.error("{}", e); } } } }
代码中通过while(true){}构造了一个死循环(永久执行);
调用公众平台接口获取access_token;
让线程休眠7000秒再运行,即每隔7000秒获取一次access_token,保证access_token永不失效。