Java微信公众平台开发(6) 微信开发中的token获取
(一)token的介绍
引用:access_token是公众号的全局唯一票据,公众号调用各接口时都需使用access_token。开发者需要进行妥善保存。access_token的存储至少要保留512个字符空间。access_token的有效期目前为2个小时,需定时刷新,重复获取将导致上次获取的access_token失效!
(二)token的获取参考文档
获取的流程我们完全可以参考微信官方文档: 如图:
(三)token获取流程分析
从公众平台获取账号的appid和appsecret;
token获取并解析存储执行体;
采用任务调度每隔两小时执行一次token获取执行体;
(四)token的获取流程的具体实现
①获取appid和appsecret
在微信公众平台【开发】——>【基本配置】中可以查看到我们需要的两个参数:
这里我们将他们定义到我们的配置文件【wechat.properties】中,大致代码为:
#获取到的appid appid=wx7e32765bc24xxxx #获取到的appsecret appsecret=d58051564fe9d86093f9xxxxx
②token获取并解析存储执行体的代码编写
由于在这里我们需要通过http的get请求向微信服务器获取时效性为7200秒的token,所以我在这里写了一个http请求的工具类,以方便我们的使用,如下:
package com.cuiyongzhi.wechat.util; import java.io.bufferedinputstream; import java.io.bufferedreader; import java.io.ioexception; import java.io.inputstream; import java.io.inputstreamreader; import java.io.outputstreamwriter; import java.net.malformedurlexception; import java.net.uri; import java.net.url; import java.net.urlconnection; import java.util.arraylist; import java.util.list; import java.util.map; import java.util.set; import java.util.zip.gzipinputstream; import org.apache.http.httpresponse; import org.apache.http.namevaluepair; import org.apache.http.client.clientprotocolexception; import org.apache.http.client.httpclient; import org.apache.http.client.entity.urlencodedformentity; import org.apache.http.client.methods.httpget; import org.apache.http.client.methods.httppost; import org.apache.http.entity.stringentity; import org.apache.http.impl.client.defaulthttpclient; import org.apache.http.message.basicnamevaluepair; import org.apache.http.protocol.http; import org.apache.http.util.entityutils; /** * classname: httputils * * @description: http请求工具类 * @author dapengniao * @date 2016年3月10日 下午3:57:14 */ @suppresswarnings("deprecation") public class httputils { /** * @description: http get请求共用方法 * @param @param requrl * @param @param params * @param @return * @param @throws exception * @author dapengniao * @date 2016年3月10日 下午3:57:39 */ @suppresswarnings("resource") public static string sendget(string requrl, map<string, string> params) throws exception { inputstream inputstream = null; httpget request = new httpget(); try { string url = buildurl(requrl, params); httpclient client = new defaulthttpclient(); request.setheader("accept-encoding", "gzip"); request.seturi(new uri(url)); httpresponse response = client.execute(request); inputstream = response.getentity().getcontent(); string result = getjsonstringfromgzip(inputstream); return result; } finally { if (inputstream != null) { inputstream.close(); } request.releaseconnection(); } } /** * @description: http post请求共用方法 * @param @param requrl * @param @param params * @param @return * @param @throws exception * @author dapengniao * @date 2016年3月10日 下午3:57:53 */ @suppresswarnings("resource") public static string sendpost(string requrl, map<string, string> params) throws exception { try { set<string> set = params.keyset(); list<namevaluepair> list = new arraylist<namevaluepair>(); for (string key : set) { list.add(new basicnamevaluepair(key, params.get(key))); } if (list.size() > 0) { try { httpclient client = new defaulthttpclient(); httppost request = new httppost(requrl); request.setheader("accept-encoding", "gzip"); request.setentity(new urlencodedformentity(list, http.utf_8)); httpresponse response = client.execute(request); inputstream inputstream = response.getentity().getcontent(); try { string result = getjsonstringfromgzip(inputstream); return result; } finally { inputstream.close(); } } catch (exception ex) { ex.printstacktrace(); throw new exception("网络连接失败,请连接网络后再试"); } } else { throw new exception("参数不全,请稍后重试"); } } catch (exception ex) { ex.printstacktrace(); throw new exception("发送未知异常"); } } /** * @description: http post请求json数据 * @param @param urls * @param @param params * @param @return * @param @throws clientprotocolexception * @param @throws ioexception * @author dapengniao * @date 2016年3月10日 下午3:58:15 */ public static string sendpostbuffer(string urls, string params) throws clientprotocolexception, ioexception { httppost request = new httppost(urls); stringentity se = new stringentity(params, http.utf_8); request.setentity(se); // 发送请求 @suppresswarnings("resource") httpresponse httpresponse = new defaulthttpclient().execute(request); // 得到应答的字符串,这也是一个 json 格式保存的数据 string retsrc = entityutils.tostring(httpresponse.getentity()); request.releaseconnection(); return retsrc; } /** * @description: http请求发送xml内容 * @param @param urlstr * @param @param xmlinfo * @param @return * @author dapengniao * @date 2016年3月10日 下午3:58:32 */ public static string sendxmlpost(string urlstr, string xmlinfo) { // xmlinfo xml具体字符串 try { url url = new url(urlstr); urlconnection con = url.openconnection(); con.setdooutput(true); con.setrequestproperty("pragma:", "no-cache"); con.setrequestproperty("cache-control", "no-cache"); con.setrequestproperty("content-type", "text/xml"); outputstreamwriter out = new outputstreamwriter( con.getoutputstream()); out.write(new string(xmlinfo.getbytes("utf-8"))); out.flush(); out.close(); bufferedreader br = new bufferedreader(new inputstreamreader( con.getinputstream())); string lines = ""; for (string line = br.readline(); line != null; line = br .readline()) { lines = lines + line; } return lines; // 返回请求结果 } catch (malformedurlexception e) { e.printstacktrace(); } catch (ioexception e) { e.printstacktrace(); } return "fail"; } private static string getjsonstringfromgzip(inputstream is) { string jsonstring = null; try { bufferedinputstream bis = new bufferedinputstream(is); bis.mark(2); // 取前两个字节 byte[] header = new byte[2]; int result = bis.read(header); // reset输入流到开始位置 bis.reset(); // 判断是否是gzip格式 int headerdata = getshort(header); // gzip 流 的前两个字节是 0x1f8b if (result != -1 && headerdata == 0x1f8b) { // logutil.i("httptask", " use gzipinputstream "); is = new gzipinputstream(bis); } else { // logutil.d("httptask", " not use gzipinputstream"); is = bis; } inputstreamreader reader = new inputstreamreader(is, "utf-8"); char[] data = new char[100]; int readsize; stringbuffer sb = new stringbuffer(); while ((readsize = reader.read(data)) > 0) { sb.append(data, 0, readsize); } jsonstring = sb.tostring(); bis.close(); reader.close(); } catch (exception e) { e.printstacktrace(); } return jsonstring; } private static int getshort(byte[] data) { return (data[0] << 8) | data[1] & 0xff; } /** * 构建get方式的url * * @param requrl * 基础的url地址 * @param params * 查询参数 * @return 构建好的url */ public static string buildurl(string requrl, map<string, string> params) { stringbuilder query = new stringbuilder(); set<string> set = params.keyset(); for (string key : set) { query.append(string.format("%s=%s&", key, params.get(key))); } return requrl + "?" + query.tostring(); } }
我们在做http请求的时候需要目标服务器的url,这里在项目中为了方便对url的管理我们在资源目录下建立了interface_url.properties用于存放目标url,这里我们将请求token的url存入:
#获取token的url tokenurl=https://api.weixin.qq.com/cgi-bin/token
我们需要将我们配置的配置文件在项目初始化后能得到启动,所以我在这里加入一个项目初始化的代码实现,用于项目启动初始化interface_url.properties和wechat.properties中的配置:
package com.cuiyongzhi.web.start; import javax.servlet.servletconfig; import javax.servlet.servletexception; import javax.servlet.http.httpservlet; /** * classname: interfaceurlintiservlet * @description: 项目启动初始化servlet * @author dapengniao * @date 2016年3月10日 下午4:08:43 */ public class interfaceurlintiservlet extends httpservlet { private static final long serialversionuid = 1l; @override public void init(servletconfig config) throws servletexception { interfaceurlinti.init(); } }
初始化的具体实现,将初始化过后的方法都存入到globalconstants中方便项目中随意调用,如下:
package com.cuiyongzhi.web.start; import java.io.ioexception; import java.io.inputstream; import java.util.properties; import com.cuiyongzhi.web.util.globalconstants; /** * classname: interfaceurlinti * @description: 项目启动初始化方法 * @author dapengniao * @date 2016年3月10日 下午4:08:21 */ public class interfaceurlinti { public synchronized static void init(){ classloader cl = thread.currentthread().getcontextclassloader(); properties props = new properties(); if(globalconstants.interfaceurlproperties==null){ globalconstants.interfaceurlproperties = new properties(); } inputstream in = null; try { in = cl.getresourceasstream("interface_url.properties"); props.load(in); for(object key : props.keyset()){ globalconstants.interfaceurlproperties.put(key, props.get(key)); } props = new properties(); in = cl.getresourceasstream("wechat.properties"); props.load(in); for(object key : props.keyset()){ globalconstants.interfaceurlproperties.put(key, props.get(key)); } } catch (ioexception e) { e.printstacktrace(); }finally{ if(in!=null){ try { in.close(); } catch (ioexception e) { e.printstacktrace(); } } } return; } }
当我们把所有的准备工作都做好了之后我们可以开始真正的去获取token了,这里我们将获取到的token解析之后依然存储到globalconstants中方便使用,简单代码如下:
package com.cuiyongzhi.wechat.common; import java.util.hashmap; import java.util.map; import net.sf.json.jsonobject; import com.cuiyongzhi.web.util.globalconstants; import com.cuiyongzhi.wechat.util.httputils; /** * classname: wechattask * @description: 微信两小时定时任务体 * @author dapengniao * @date 2016年3月10日 下午1:42:29 */ public class wechattask { /** * @description: 任务执行体 * @param @throws exception * @author dapengniao * @date 2016年3月10日 下午2:04:37 */ public void gettoken_getticket() throws exception { map<string, string> params = new hashmap<string, string>(); params.put("grant_type", "client_credential"); params.put("appid", globalconstants.getinterfaceurl("appid")); params.put("secret", globalconstants.getinterfaceurl("appsecret")); string jstoken = httputils.sendget( globalconstants.getinterfaceurl("tokenurl"), params); string access_token = jsonobject.fromobject(jstoken).getstring( "access_token"); // 获取到token并赋值保存 globalconstants.interfaceurlproperties.put("access_token", access_token); system.out.println(new simpledateformat("yyyy-mm-dd hh:mm:ss").format(new date())+"token为=============================="+access_token); } }
(三)采用任务调度每隔两小时执行一次token获取执行体
我们阅读过微信的文档会发现我们的token获取的接口每天是有调用次数限制的,为了防止我们业务量比较大的情况下token的直接调用的接口次数不够用,所以我们需要根据token的时效性(7200s)在自己的业务服务器上做到token的缓存并定时获取,我这里用到的任务调度的方式是采用quartz,有关quartz的使用可以参考文章 http://cuiyongzhi.com/?tags=%e5%ae%9a%e6%97%b6%e4%bb%bb%e5%8a%a1 ,下面具体代码的实现:
package com.cuiyongzhi.wechat.quartz; import org.apache.log4j.logger; import com.cuiyongzhi.wechat.common.wechattask; public class quartzjob{ private static logger logger = logger.getlogger(quartzjob.class); /** * @description: 任务执行获取token * @param * @author dapengniao * @date 2016年3月10日 下午4:34:26 */ public void workfortoken() { try { wechattask timer = new wechattask(); timer.gettoken_getticket(); } catch (exception e) { logger.error(e, e); } } }
这里新建配置文件spring-quartz.xml以方便quartz任务的管理和启用,这里将我们需要用到的workfortoken()加入到执行任务中:
<?xml version="1.0" encoding="utf-8"?> <!doctype beans public "-//spring//dtd bean//en" "http://www.springframework.org/dtd/spring-beans.dtd"> <beans> <!-- 要调用的工作类 --> <bean id="quartzjob" class="com.cuiyongzhi.wechat.quartz.quartzjob"></bean> <!-- 定义调用对象和调用对象的方法 --> <bean id="jobtaskfortoken" class="org.springframework.scheduling.quartz.methodinvokingjobdetailfactorybean"> <!-- 调用的类 --> <property name="targetobject"> <ref bean="quartzjob" /> </property> <!-- 调用类中的方法 --> <property name="targetmethod"> <value>workfortoken</value> </property> </bean> <!-- 定义触发时间 --> <bean id="dotimefortoken" class="org.springframework.scheduling.quartz.crontriggerbean"> <property name="jobdetail"> <ref bean="jobtaskfortoken" /> </property> <!-- cron表达式 --> <property name="cronexpression"> <value>0 0/1 * * * ?</value> </property> </bean> <!-- 总管理类 如果将lazy-init='false'那么容器启动就会执行调度程序 --> <bean id="startquertz" lazy-init="false" autowire="no" class="org.springframework.scheduling.quartz.schedulerfactorybean"> <property name="triggers"> <list> <ref bean="dotimefortoken" /> </list> </property> </bean> </beans>
这里我为了测试将执行间隔时间设置成了1分钟一次,根据需要可以自行修改执行时间;最后我们需要在我们的web.xml启动项中开启quartz的使用:
<context-param> <param-name>contextconfiglocation</param-name> <param-value>classpath:spring.xml,classpath:spring-mybatis.xml,classpath:spring-quartz.xml</param-value> <!-- ,classpath:spring-quartz.xml 用于做任务调度 任务定时都可以 --> </context-param>
当这一切都准备完毕之后我们启动项目,会发现每间隔一分钟就会有token获取到,这里我是将其存储在项目变量中,但是如果需要考虑到项目横向扩展这里建议将token存储到缓存中;运行结果如下:
那么到这里token的获取和保存就基本讲完了,下一篇将讲述【多媒体消息的回复】,感谢你的翻阅,如果有需要源码或有疑问可以留言!
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。
推荐阅读
-
Java微信公众平台开发(6) 微信开发中的token获取
-
Java微信公众平台开发(6) 微信开发中的token获取
-
ASP.NET MVC5+EF6+EasyUI后台管理系统 微信公众平台开发之资源环境准备
-
在SAE开发微信公众平台(java) 一 博客分类: 微信公众平台 java微信公众平台SAE
-
Jeewx捷微 , 免费微信公众账号管家系统发布,采用JAVA语言 博客分类: jeecg开源社区,jeewx微信java JEEWX微信开发平台开源java
-
用Java设计模式中的观察者模式开发微信公众号的例子
-
java微信公众号开发第一步 公众号接入和access_token管理
-
用Java设计模式中的观察者模式开发微信公众号的例子
-
微信公众平台开发实战Java版之微信获取用户基本信息
-
java微信公众号开发第一步 公众号接入和access_token管理