weinx公众号--AccessToken和JsapiTicket的获取方法及长期保存 (整合)

  

创建 AccessToken实体类 和 JsapiTicket 实体类 ,用来长久保存 token 和 ticket 值 

package com.play.bean; /** 
 * 微信通用接口凭证 
 */  
public class AccessToken {  
    // 获取到的凭证  
    private String token;  
    // 凭证有效时间,单位:秒  
    private int expiresIn;  
  
    public String getToken() {  
        return token;  
    }  
  
    public void setToken(String token) {  
        this.token = token;  
    }  
  
    public int getExpiresIn() {  
        return expiresIn;  
    }  
  
    public void setExpiresIn(int expiresIn) {  
        this.expiresIn = expiresIn;  
    }  
 
package com.play.bean; /** 
 * 微信通用接口凭证 
 */ 
public class JsapiTicket { 
    // 获取到的凭证 
    private String ticket; 
    // 凭证有效时间,单位:秒 
    private int expiresIn; 
    
    public String getTicket() {
return ticket;
}

public void setTicket(String ticket) {
this.ticket = ticket;
}
 
    public int getExpiresIn() { 
        return expiresIn; 

 
public void setExpiresIn(int expiresIn) { 
this.expiresIn = expiresIn; 
    } 
定期获取并存储 access_token 的流程为:

Web服务器启动时就加载一个Servlet,在Servlet的init()方法中启动一个线程,在线程的run()方法中通过死循环+Thread.sleep()的方式定期获取 access_token,然后将获取到的 access_token 保存在public static修饰的变量中。
在工程中创建一个InitServlet类,该类的代码如下:

package com.play.servlet;  
  
import javax.servlet.ServletException;  
import javax.servlet.http.HttpServlet;  
import com.play.thread.TokenThread;  
import com.play.util.WeixinUtil;  
  
/** 
 * 初始化servlet 
 */  
public class InitServlet extends HttpServlet {  
    private static final long serialVersionUID = 1L;   
  
    public void init() throws ServletException {  
        // 获取web.xml中配置的参数  
        TokenThread.appid = getInitParameter("appid");  
        TokenThread.appsecret = getInitParameter("appsecret");  
  
        System.out.println("weixin api appid:{}", TokenThread.appid);  
        System.out.println("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();  
        }  
    }  
}
 
从上面的代码可以看出,InitServlet类只重写了init()方法,并没有重写 doGet() 和 doPost() 两个方法,因为我们并不打算让 InitServlet 来处理访问请求。

init()方法的实现也比较简单,先获取在 web.xml 中配置的参数 appid 和 appsecret,再启动线程 TokenThread 定时获取 access_token。
InitServlet在web.xml中的配置如下:

<?xml version="1.0" encoding="UTF-8"?>   <web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee"       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"       xsi:schemaLocation="http://java.sun.com/xml/ns/javaee        http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">          <servlet>           <servlet-name>initServlet</servlet-name>           <servlet-class>               com.play.servlet.InitServlet           </servlet-class>           <!-- 配置获取access_token所需参数appid和appsecret -->           <init-param>               <param-name>appid</param-name>               <param-value>wxwxwxxwxx54wx54wx54</param-value>           </init-param>           <init-param>               <param-name>appsecret</param-name>               <param-value>4xw544xw44x4wwx4xw4wx4wx</param-value>           </init-param>           <load-on-startup>0</load-on-startup>       </servlet>          <welcome-file-list>           <welcome-file>index.jsp</welcome-file>       </welcome-file-list>   </web-app>  
 InitServlet在 web.xml 中的配置与普通 Servlet 的配置有几点区别:

1)通过配置 <init-param> 向 Servlet 中传入参数;

2)通过配置 <load-on-startup> 使得Web服务器启动时就加载该 Servlet;

3)没有配置 <servlet-mapping>,因为 InitServlet 并不对外提供访问。

TokenThread的源代码如下:

package com.play.util;

import java.util.UUID;
import javax.servlet.http.HttpServletRequest;
import com.play.util.StringUtil;
import com.play.bean.AccessToken;
import com.play.bean.JsapiTicket;

/** 
 * 定时获取微信 access_token 和 jsapiTicket 的线程 
 */  
public class TokenThread implements Runnable {  
    // 第三方用户唯一凭证  
    public static String appid = "";  
    // 第三方用户唯一凭证密钥  
    public static String appsecret = "";  
    public static AccessToken accessToken = null;  
    public static JsapiTicket jsapiTicket = null;  
  
    public void run() {   
        while (true) {  
            try {  
                accessToken = WeixinUtil.getAccessToken(appid, appsecret);  
                if (null != accessToken) {  
                    System.out.println("获取access_token成功,有效时长"+ accessToken.getExpiresIn()+"秒 token:"+ accessToken.getToken());  
                    try{
                    	jsapiTicket = WeixinUtil.getJsapiTicket(accessToken.getToken());
                        if(jsapiTicket!=null){
                        	System.out.println("获取jsapiTicket成功,有效时长"+ jsapiTicket.getExpiresIn()+"秒 ticket:"+ jsapiTicket.getTicket());  
                        }
                    }catch(Exception e){
                    	 // 如果jsapiTicket为null,60秒后再获取  
                        Thread.sleep(60 * 1000);  
                    }
                    // 休眠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) {  
                	  System.out.println(e1);  
                }  
                System.out.println( e);  
            }  
        }  
    }
}  
编写 WeixinUtil 工具类来实现发送的GET请求,并获取对应的 AccessToken 和 JsapiTicket 

package com.play.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 net.sf.json.JSONException; import net.sf.json.JSONObject; import com.play.bean.AccessToken; import com.play.bean.JsapiTicket;   /**   * 公众平台通用接口工具类   */  public class WeixinUtil {   // 获取access_token的接口地址(GET) 限200(次/天)   public final static String access_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 httpRequest(String requestUrl, String requestMethod, String outputStr) {          JSONObject jsonObject = null;          StringBuffer buffer = new StringBuffer();          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();              jsonObject = JSONObject.fromObject(buffer.toString());          } catch (ConnectException ce) {           ce.printStackTrace();         } catch (Exception e) {           e.printStackTrace();         }          return jsonObject;      }      /**       * 获取access_token       *        * @param appid 凭证       * @param appsecret 密钥       * @return       */      public static AccessToken getAccessToken(String appid, String appsecret) {       //获取公众号access_token的链接      String access_token = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=APPID&secret=APPSECRET";         AccessToken accessToken = null;                   String requestUrl = access_token.replace("APPID", appid).replace("APPSECRET", appsecret);          //String requestUrl = access_token_url.replace("APPID", appid).replace("APPSECRET", appsecret);          JSONObject jsonObject = httpRequest(requestUrl, "GET", null);          // 如果请求成功          if (null != jsonObject) {              try {                  accessToken = new AccessToken();                  accessToken.setToken(jsonObject.getString("access_token"));                  accessToken.setExpiresIn(jsonObject.getInt("expires_in"));              } catch (JSONException e) {                  accessToken = null;                  // 获取token失败   System.out.println("获取token失败 errcode:{} errmsg:{}", jsonObject.getInt("errcode"), jsonObject.getString("errmsg"));              }          }          return accessToken;      }        /**       * 获取jsapi_ticket      *        * @param appid 凭证       * @param appsecret 密钥       * @return       */      public static JsapiTicket getJsapiTicket(String accessToken) {       //获取公众号jsapi_ticket的链接      String jsapi_ticket_url = "https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token=ACCESS_TOKEN&type=jsapi";      JsapiTicket jsapiticket = null; //ticket分享值      if(accessToken != null){       String requestUrl = jsapi_ticket_url.replace("ACCESS_TOKEN", accessToken);        //String requestUrl = access_token_url.replace("APPID", appid).replace("APPSECRET", appsecret);        JSONObject jsonObject = httpRequest(requestUrl, "GET", null);        // 如果请求成功        if (null != jsonObject) {        try {        jsapiticket = new JsapiTicket();        jsapiticket.setTicket(jsonObject.getString("ticket"));        jsapiticket.setExpiresIn(jsonObject.getInt("expires_in"));        } catch (JSONException e) {        jsapiticket = null;        // 获取ticket失败       System.out.println("获取ticket失败 errcode:{} errmsg:{}", jsonObject.getInt("errcode"), jsonObject.getString("errmsg"));        }            }       }else{      System.out.println("*****token为空 获取ticket失败******");      }                 return jsapiticket;      }  }   


 

额外补充:获取 JsapiTicket 是用来验证签名,使用分享接口等操作所需要的。

我们可以在 TokenThread 类 中添加 share 方法来获取所需要的参数 (例如,分享功能需要:appId、timestampnonceStrsignature等参数

 
  
  
  
  
public  void share(HttpServletRequest request) throws Exception {
            
            StringBuffer homeUrl = request.getRequestURL();
            String queryString =request.getQueryString();
            if (StringUtils.isNotBlank(queryString)){
                homeUrl.append( "?" ).append(queryString);
            }
 
            long timestamp = System.currentTimeMillis() / 1000 ;
            
            String nonceStr = UUID.randomUUID().toString();
          
            String signature = SignUtil.getSignature(
                    weiXinBaseService.getJsTicket(), nonceStr, timestamp,
                    homeUrl.toString());
            logger.info( "url=" +homeUrl);
            logger.info( "nonceStr=" + nonceStr);
            logger.info( "timestamp=" + timestamp);
            logger.info( "signature=" + signature);
            logger.info( "appid=" + WebConfig.get( "weixin.appid" ));
          request.setAttribute( "appid" ,  WebConfig.get( "weixin.appid" ));
            request.setAttribute( "timestamp" , timestamp);
            request.setAttribute( "nonceStr" , nonceStr);
            request.setAttribute( "signature" , signature);
        }
 编写工具类 SignUtil 来实现校验操作


  
  
  
  
/**
      * 获得分享链接的签名。
      * @param ticket
      * @param nonceStr //随机字符串
      * @param timeStamp //时间戳
      * @param url //请求页面的链接
      * @return
      * @throws Exception
      */
   public static String getSignature(String ticket, String nonceStr, long timeStamp, String url) throws Exception {
         String sKey = "jsapi_ticket=" + ticket
                 + "&noncestr=" + nonceStr + " &timestamp =" + timeStamp
                 + "&url=" + url;
         return getSignature(sKey);
     }
 
  /**
      * 验证签名。
      *
      * @param signature
      * @param timestamp
      * @param nonce
      * @return
      */
     public static String getSignature(String sKey) throws Exception {
         String ciphertext = null ;
         MessageDigest md = MessageDigest.getInstance( "SHA-1" );
         byte [] digest = md.digest(sKey.toString().getBytes());
         ciphertext = byteToStr(digest);
         return ciphertext.toLowerCase();
     }
 
  /**
      * 将字节数组转换为十六进制字符串
     
      * @param byteArray
      * @return
      */ 
     private static String byteToStr( byte [] byteArray) { 
         String strDigest = ""
         for ( int i = 0 ; i < byteArray.length; i++) { 
             strDigest += byteToHexStr(byteArray[i]); 
        
         return strDigest; 
    
   /**
      * 将字节转换为十六进制字符串
     
      * @param mByte
      * @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; 
     }
 
 前端页面中引入微信js文件并配置config

<script src="http://res.wx.qq.com/open/js/jweixin-1.0.0.js"></script>
 
<script>
  var shareTitle = "分享链接的开头!";
    var shareImg = "分享链接的图片地址"
    wx.config({
        debug: false, //
        appId: '${appid}', // 必填,公众号的唯一标识
        timestamp: '${timestamp}', // 必填,生成签名的时间戳
        nonceStr: '${nonceStr}', // 必填,生成签名的随机串
        signature: '${signature}',// 必填,签名,见附录1
        jsApiList: [
            'onMenuShareTimeline',
            'onMenuShareAppMessage',
            'showOptionMenu'
        ] // 必填,需要使用的JS接口列表
    });
 wx.ready(function () {
       
wx.showOptionMenu();
wx.onMenuShareTimeline({
    title: '', // 分享标题
    link: '', // 分享链接
    imgUrl: '', // 分享图标
    success: function () { 
        // 用户确认分享后执行的回调函数
    },
    cancel: function () { 
        // 用户取消分享后执行的回调函数
    }
});
wx.onMenuShareAppMessage({
    title: '', // 分享标题
    desc: '', // 分享描述
    link: '', // 分享链接
    imgUrl: '', // 分享图标
    type: '', // 分享类型,music、video或link,不填默认为link
    dataUrl: '', // 如果type是music或video,则要提供数据链接,默认为空
    success: function () { 
        // 用户确认分享后执行的回调函数
    },
    cancel: function () { 
        // 用户取消分享后执行的回调函数
    }
});

  });


参考资料
PS:微信开发第14篇,如何获取 access_token  http://blog.csdn.net/lyq8479/article/details/9841371
微信开发第22篇,如何让 access_token 长期有效  http://blog.csdn.net/lyq8479/article/details/25076223
官网 http://mp.weixin.qq.com/wiki/4/9ac2e7b1f1d22e9e57260f6553822520.html
http://www.2cto.com/weixin/201506/406848.html


你可能感兴趣的:(微信)