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

来源:互联网 发布:兰州网络机柜 编辑:程序博客网 时间:2024/06/13 13:22

  

创建 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


0 0
原创粉丝点击