微信公眾號開發(一)

開發前首先我們要知道一些概念

各公眾號區別:
1、訂閱號:為媒體和個人提供一種信息傳播方式,主要偏于為用戶傳達資訊(類似報紙雜志),主要的定位是閱讀,每天可以群發1條消息;
2、服務號:為企業,政府或組織提供對用戶進行服務,主要偏于服務交互(類似銀行提供服務查詢),每個月只可群發4條消息;
3、企業號:為企業,政府,事業單位,實現生產管理和協作運營的移動化,主要用于公司內部通訊使用,旨在為用戶提供移動辦公,需要先有成員的通訊信息驗證才可以關注成功企業號;

溫馨提示:
1、如果想簡單的發送消息,達到宣傳效果,建議選擇訂閱號;
2、如果想進行商品銷售,進行商品售賣,為用戶提供服務,建議申請服務號;
3、如果想用來管理內部企業員工、團隊,對內使用,可選擇申請企業號。

編輯模式:主要針對非編程人員及信息發布類公眾帳號使用。
開啟該模式后,可以方便地通過界面配置“自定義菜單”(認證的訂閱號、服務號)和“自動回復的消息”。
好處是可視化界面配置,操作簡單,快捷,但是功能有限。
由于編輯模式功能有限我們就不做多講我們把重點方法開發模式上
首先我們自己要先申請一個公眾號對立面的各個功能要熟悉然后再看開發模式

開發模式:主要針對具備開發能力的人使用。開啟該模式后,能夠使用微信公眾平臺開放的接口,
通過編程方式實現自定義菜單的創建、刪除、用戶消息的交互,
這種模式更加靈活,能實現更多復雜的功能,提供個性化服務。

微信公眾平臺是運營者通過公眾號為微信用戶提供資訊和服務的平臺,而公眾平臺開發接口則是提供服務的基礎,
開發者在公眾平臺網站中創建公眾號、獲取接口權限后,可以通過閱讀本接口文檔來幫助開發。
文檔地址:http://mp.weixin.qq.com/wiki/home/
要站在自己網站的角度看文檔。

注冊公眾號

我們這里只對訂閱號做探討其實和服務號開發是一樣的

開發模式

微信公眾平臺開發模式交互原理

這里我們需要服務器,服務器有阿里云,bae,花生殼,nat123,natapp由于價格原因阿里云,bae價格都比較比較昂貴我們現在做測試就選擇natapp內網穿透https://natapp.cn/
先下載客戶端


在官網上購買隧道5塊錢的要以.top為后綴的


再購買一個二級域名5塊錢


安裝natapp


根據教程安裝好natapp

安裝成功的標識 把地址拷貝下來http://zhangshuai.nat100.top


這個時候就可以通過域名在任何地方訪問我們的應用了

微信公眾號URL接入

進入公眾平臺測試帳號


可以查看文檔接入指南(https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421135319)也可以跟我一起來(我們這里Tomcat服務器的端口是8080有些人的端口是80根據自己的情況)

首先我們先創建一個Maven項目


先配置好項目
web.xml

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee"
     xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
     id="WebApp_ID" version="3.0">
  <display-name>Archetype Created Web Application</display-name>
<servlet>
<servlet-name>springMVC</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
  <param-name>contextConfigLocation</param-name>
  <param-value>classpath:application-mvc.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>springMVC</servlet-name>
<url-pattern>*.do</url-pattern>
</servlet-mapping>
<!--配置字符編碼過濾器-->
 <filter>
<filter-name>encodingFilter</filter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
<init-param>
  <param-name>encoding</param-name>
  <param-value>UTF-8</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>encodingFilter</filter-name>
<url-pattern>*.do</url-pattern>
</filter-mapping>
</web-app>

application-mvc.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop" xmlns:jee="http://www.springframework.org/schema/jee"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="
        http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
        http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee.xsd
        http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd
        http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd
        http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd">
<!-- 0.開啟注解掃描  -->
<context:component-scan base-package="com.jd.wx"/>
<!-- 1.注解驅動支持 -->
<mvc:annotation-driven/>
<!-- 2.配置靜態資源處理 -->
<mvc:default-servlet-handler/>
<!-- 3.視圖解析器 -->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
    <property name="prefix" value="/WEB-INF/views/"/>
    <property name="suffix" value=".jsp"/>
</bean>
</beans>

現在我們先創建一個Controller測試一下

@Controller
public class WeixinController {
//驗證簽名
/**
 * @param signature 微信加密簽名,signature結合了開發者填寫的token參數和請求中的timestamp參數、nonce參數。
 * @param timestamp 時間戳
 * @param nonce 隨機數
 * @param echostr 隨機字符串
 * @return
 */
@RequestMapping("/weixin")
public String checkSignature(String signature,String timestamp,String nonce,String echostr){
    System.out.println(signature);
    System.out.println(timestamp);
    System.out.println(nonce);
    System.out.println(echostr);
    return null;
}
}

打印結果

開發者通過檢驗signature對請求進行校驗(下面有校驗方式)。若確認此次GET請求來自微信服務器,請原樣返回echostr參數內容,則接入生效,成為開發者成功,否則接入失敗。加密/校驗流程如下:
1)將token、timestamp、nonce三個參數進行字典序排序
2)將三個參數字符串拼接成一個字符串進行sha1加密
3)開發者獲得加密后的字符串可與signature對比,標識該請求來源于微信

  //加密/校驗流程如下:
    String[] arr = {WeixinUtil.TOKEN,timestamp,nonce};
    //1.將token、timestamp、nonce三個參數進行字典序排序
    Arrays.sort(arr);
    String str = "";
    //2.將三個參數字符串拼接成一個字符串進行sha1加密
    for (String temp : arr) {
        str += temp;
    }
    //3.開發者獲得加密后的字符串可與signature對比,標識該請求來源于微信
    if (signature.equals(SecurityUtil.SHA1(str))) {
        System.out.println("接入成功!");
        return echostr;
    }
    System.out.println("接入失敗!");
    return null;

此時我們需要工具類

和SecurityUtil

package com.jd.wx.util;
import java.security.MessageDigest;
public class SecurityUtil {
private static final char[] HEX_DIGITS = { '0', '1', '2', '3', '4', '5',
        '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' };
/**
 * encode string
 * @param algorithm
 * @param str
 * @return String
 */
public static String encode(String algorithm, String str) {
    if (str == null) {
        return null;
    }
    try {
        MessageDigest messageDigest = MessageDigest.getInstance(algorithm);
        messageDigest.update(str.getBytes());
        return getFormattedText(messageDigest.digest());
    } catch (Exception e) {
        throw new RuntimeException(e);
    }
}
/**
 * Takes the raw bytes from the digest and formats them correct.
 * @param bytes
 *  the raw bytes from the digest.
 * @return the formatted bytes.
 */
private static String getFormattedText(byte[] bytes) {
    int len = bytes.length;
    StringBuilder buf = new StringBuilder(len * 2);
    // 把密文轉換成十六進制的字符串形式
    for (int j = 0; j < len; j++) {
        buf.append(HEX_DIGITS[(bytes[j] >>> 4) & 0x0f]);
        buf.append(HEX_DIGITS[bytes[j] & 0x0f]);
    }
    return buf.toString();
}

public static String MD5(String content){
    return SecurityUtil.encode("MD5", content);
}

public static String SHA1(String content){
    return SecurityUtil.encode("SHA1", content);
}

public static void main(String[] args) {
    System.out.println("111111 MD5  :"
            + SecurityUtil.encode("MD5", "111111"));
    System.out.println("111111 SHA1 :"
            + SecurityUtil.encode("SHA1", "111111"));
}
}

配置成功

現在有一個問題是當用戶給公眾號發送一個消息,消息會推送到我們給的地址上面 我們怎么知道什么時候是驗證什么時候是消息推送怎么區分呢?
其實很簡單 開發者通過檢驗signature對請求進行校驗若確認此次GET請求來自微信服務器,如果是消息接收他會發送一個POST請求
當普通微信用戶向公眾賬號發消息時,微信服務器將POST消息的XML數據包到開發者填寫的URL上。

 @RequestMapping(value="/weixin",method= RequestMethod.GET)
 @RequestMapping(value="/weixin",method= RequestMethod.POST)

此時他會發送一個xml數據過來我們需要jaxb(自己百度一下)工具來解析xml


@Getter@Setter@ToString
@XmlRootElement(name = "xml")
@XmlAccessorType(XmlAccessType.FIELD)
public class XmlMessageEntity {
private String ToUserName;
private String FromUserName;
private Long CreateTime;
private String MsgType;
private String Content;
private String Event;
private Long MsgId;
}

這個時候我們就可以在Controller里處理用戶發送到公眾號上的消息進行回復了

@RequestMapping(value="/weixin",method=RequestMethod.POST)
@ResponseBody
public XmlMessageEntity handlerMessage(@RequestBody XmlMessageEntity entity){
    System.out.println(entity);
    XmlMessageEntity newEntity = new XmlMessageEntity();
    newEntity.setFromUserName(entity.getToUserName()); //設置發送方
    newEntity.setToUserName(entity.getFromUserName());  //設置接收方
    newEntity.setCreateTime(new Date().getTime()); //設置發送時間
    //如果是第一次關注,回復“歡迎關注!”
    if("event".equals(entity.getMsgType())){
        //如果是關注事件
        if("subscribe".equals(entity.getEvent())){
            //調用接口獲取用戶詳細信息
            String reuslt = HttpUtil.get(WeixinUtil.GET_USERINFO_URL.replace("ACCESS_TOKEN", WeixinUtil.getAccessToken())
                    .replace("OPENID", entity.getFromUserName()));
            System.out.println(WeixinUtil.GET_USERINFO_URL.replace("ACCESS_TOKEN", WeixinUtil.getAccessToken())
                    .replace("OPENID", entity.getFromUserName()));
            System.out.println(reuslt);
            //創建客戶信息,保存到數據庫
            //回復內容
            newEntity.setContent("歡迎關注!");
        }else if ("unsubscribe".equals(entity.getEvent())) {
            //更新客戶狀態,設置為取消關注
        }
    }
    //如果發送的是你好,回復“很高興認識你”
    if(entity.getContent().contains("你好")){
        newEntity.setContent("很高興認識你");
    }else{
        //否則就統一回復“帥哥好”
        newEntity.setContent("帥哥好!");
    }
    //發送類型
    newEntity.setMsgType("text");
    return newEntity;

}

現在我通過手機掃二維碼進行測試(也可以通過接口調試工具https://mp.weixin.qq.com/debug/cgi-bin/apiinfo?t=index)


測試成功


獲取用戶基本信息(包括UnionID機制)

開發者可通過OpenID來獲取用戶基本信息。請使用https協議。
接口調用請求說明
http請求方式: GET https://api.weixin.qq.com/cgi-bin/user/info?access_token=ACCESS_TOKEN&openid=OPENID&lang=zh_CN

獲取access_token
access_token是公眾號的全局唯一接口調用憑據,公眾號調用各接口時都需使用access_token。開發者需要進行妥善保存。access_token的存儲至少要保留512個字符空間。access_token的有效期目前為2個小時,需定時刷新,重復獲取將導致上次獲取的access_token失效。

首先我們先創建一個網絡請求的工具類HttpUtil

/**
* Http工具類
*/
public class HttpUtil {

/**
 * 發送get請求
 * @throws Exception
 */
public static String get(String url) {

    String result = "";
    InputStream in = null;
    try {
        // 打開和URL之間的連接
        HttpURLConnection conn = (HttpURLConnection) new URL(url)
                .openConnection();
        // 設置通用的請求屬性
        conn.setRequestProperty("accept", "*/*");
        conn.setRequestProperty("connection", "Keep-Alive");
        conn.setRequestProperty("Content-Type", "application/json");
        conn.setRequestProperty("Accept", "application/json");
        conn.setRequestMethod("GET");
        // 建立實際的連接
        conn.connect();
        // 定義輸入流來讀取URL的響應
        in = conn.getInputStream();
        result = StreamUtils.copyToString(in, Charset.forName("utf-8"));
    } catch (Exception e) {
        e.printStackTrace();
    } finally {
        if (in != null) {
            try {
                in.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
    return result;
}

/**
 * 發送post請求
 *
 * @throws Exception
 */
public static String post(String url, String paramStr) {
    InputStream in = null;
    OutputStream os = null;
    String result = "";
    try {
        // 打開和URL之間的連接
        HttpURLConnection conn = (HttpURLConnection) new URL(url)
                .openConnection();
        // 設置通用的請求屬性
        conn.setRequestProperty("accept", "*/*");
        conn.setRequestProperty("connection", "Keep-Alive");
        // 發送POST請求須設置
        conn.setRequestMethod("POST");
        conn.setDoOutput(true);
        conn.setDoInput(true);
        os = conn.getOutputStream();
        // 注意編碼格式,防止中文亂碼
        if (StringUtils.hasText(paramStr)) {
            os.write(paramStr.getBytes("utf-8"));
            os.close();
        }

        in = conn.getInputStream();
        result = StreamUtils.copyToString(in, Charset.forName("utf-8"));
    } catch (Exception e) {
        e.printStackTrace();
    } finally {
        try {
            if (os != null) {
                os.close();
            }
            if (in != null) {
                in.close();
            }
        } catch (IOException ex) {
            ex.printStackTrace();
        }
    }
    return result;
}
}

接著我們想獲取AccessToken 通過HttpUtil發送GET請求 "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=APPID&secret=APPSECRET"我們把響應回來的數據轉成json對象然后回去對象的access_token

public static String accessToken;
//accessToken的失效時間
public static Long expiresTime = 0L;

/**
 * 獲取AccessToken
 * @return
 */
public static String getAccessToken() {
//如果accessToken為null或者accessToken已經失效就去重新獲取(提前10秒)
if(new Date().getTime()>= expiresTime){
    //發送http請求
    String result = HttpUtil.get(GET_ACCESSTOKEN_URL.replace("APPID", APPID).replace("APPSECRET", SECRET));
    //轉成json對象
    JSONObject json = JSON.parseObject(result);
    accessToken = (String) json.get("access_token");
    Integer expires_in = (Integer) json.get("expires_in");
    //失效時間=當前時間+7200
    expiresTime = new Date().getTime()+((expires_in-10)*1000);
}
return accessToken;
}
public class WeixinUtil {
public static final String TOKEN = "zhangshuai";
public static final String APPID = "wxa379f9e383fb5ad7";
public static final String SECRET = "ce2d71a98230f1d5d6cb35c7e5e90573";
//獲取基礎ACCESSTOKEN的URL
public static final String GET_ACCESSTOKEN_URL = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=APPID&secret=APPSECRET";

public static String accessToken;
//accessToken的失效時間
public static Long expiresTime = 0L;

/**
 * 獲取AccessToken
 * @return
 */
public static String getAccessToken() {
    //如果accessToken為null或者accessToken已經失效就去重新獲取(提前10秒)
    if(new Date().getTime()>= expiresTime){
        //發送http請求
        String result = HttpUtil.get(GET_ACCESSTOKEN_URL.replace("APPID", APPID).replace("APPSECRET", SECRET));
        //轉成json對象
        JSONObject json = JSON.parseObject(result);
        accessToken = (String) json.get("access_token");
        Integer expires_in = (Integer) json.get("expires_in");
        //失效時間=當前時間+7200
        expiresTime = new Date().getTime()+((expires_in-10)*1000);
    }
    return accessToken;
}

接著我們來獲取用戶信息

//獲取用戶信息的URL(需要關注公眾號)
public static final String GET_USERINFO_URL = "https://api.weixin.qq.com/cgi-bin/user/info?access_token=ACCESS_TOKEN&openid=OPENID&lang=zh_CN ";
//調用接口獲取用戶詳細信息
String reuslt = HttpUtil.get(WeixinUtil.GET_USERINFO_URL.replace("ACCESS_TOKEN", WeixinUtil.getAccessToken())
 .replace("OPENID", entity.getFromUserName()));
System.out.println(WeixinUtil.GET_USERINFO_URL.replace("ACCESS_TOKEN", WeixinUtil.getAccessToken())
 .replace("OPENID", entity.getFromUserName()));
System.out.println(reuslt);

這時我們重新關注查看日志是否打印用戶信息

自定義菜單創建接口

1、自定義菜單最多包括3個一級菜單,每個一級菜單最多包含5個二級菜單。
2、一級菜單最多4個漢字,二級菜單最多7個漢字,多出來的部分將會以“...”代替。
3、創建自定義菜單后,菜單的刷新策略是,在用戶進入公眾號會話頁或公眾號profile頁時,如果發現上一次拉取菜單的請求在5分鐘以前,就會拉取一下菜單,如果菜單有更新,就會刷新客戶端的菜單。測試時可以嘗試取消關注公眾賬號后再次關注,則可以看到創建后的效果。

http請求方式:POST(請使用https協議) https://api.weixin.qq.com/cgi-bin/menu/create?access_token=ACCESS_TOKEN

先根據文檔發送GET請求

//創建自定義菜單的URL
public static final String CREATEMENU_URL = "https://api.weixin.qq.com/cgi-bin/menu/create?access_token=ACCESS_TOKEN";

/**
 *創建菜單
 * @return
 */
public static void createMenu() {
    String result = HttpUtil.post(CREATEMENU_URL.replace("ACCESS_TOKEN", getAccessToken()),
            "{\"button\":[{ \"type\":\"click\",\"name\":\"今日歌曲\",\"key\":\"V1001_TODAY_MUSIC\"},{\"name\":\"菜單\",\"sub_button\":[{  \"type\":\"view\",\"name\":\"百度官網\",\"url\":\"https://open.weixin.qq.com/connect/oauth2/authorize?appid=wx2cd3085e1b3695d9&redirect_uri=http://chen.nat300.top/index.do&response_type=code&scope=snsapi_userinfo#wechat_redirect\"},{\"type\":\"click\",\"name\":\"贊一下我們\",\"key\":\"V1001_GOOD\"}]}]}");
    System.out.println(result);
}

模板消息

發送模板消息調用接口
http請求方式: POST
https://api.weixin.qq.com/cgi-bin/message/template/send?access_token=ACCESS_TOKEN

//發送模板消息的URL
public static final String SEND_TEMPLATE_URL = "https://api.weixin.qq.com/cgi-bin/message/template/send?access_token=ACCESS_TOKEN";

/**
 * 發送模板消息
 */
public static void sendTemplate() {
    String result = HttpUtil.post(SEND_TEMPLATE_URL.replace("ACCESS_TOKEN",getAccessToken()),"{\"touser\":\"ogGJv0zE12eFlOGQuCALxtD3vT7E\",\"template_id\":\"flx-OcF_I8Uj6lj3kRLA4_hN79xVCY4OZ006JQLreVU\",\"url\":\"http://www.baidu.com\",\"data\":{\"first\":{\"value\":\"恭喜你購買成功!\",\"color\":\"#173177\"},\"keyword1\":{\"value\":\"巧克力\",\"color\":\"#173177\"},\"keyword3\":{\"value\":\"39.8元\",\"color\":\"#173177\"},\"keyword2\":{\"value\":\"2017年9月04日\",\"color\":\"#173177\"},\"remark\":{\"value\":\"歡迎再次購買!\",\"color\":\"#173177\"}}}");
    System.out.println(result);
}

每個模板的id不固定

微信網頁授權

關于網頁授權回調域名的說明

1、在微信公眾號請求用戶網頁授權之前,開發者需要先到公眾平臺官網中的“開發 - 接口權限 - 網頁服務 - 網頁帳號 - 網頁授權獲取用戶基本信息”的配置選項中,修改授權回調域名。請注意,這里填寫的是域名(是一個字符串),而不是URL,因此請勿加 http:// 等協議頭;
2、授權回調域名配置規范為全域名,比如需要網頁授權的域名為:www.qq.com,配置以后此域名下面的頁面http://www.qq.com/music.htmlhttp://www.qq.com/login.html 都可以進行OAuth2.0鑒權。但http://pay.qq.comhttp://music.qq.comhttp://qq.com無法進行OAuth2.0鑒權
3、如果公眾號登錄授權給了第三方開發者來進行管理,則不必做任何設置,由第三方代替公眾號實現網頁授權即可

關于網頁授權的兩種scope的區別說明

1、以snsapi_base為scope發起的網頁授權,是用來獲取進入頁面的用戶的openid的,并且是靜默授權并自動跳轉到回調頁的。用戶感知的就是直接進入了回調頁(往往是業務頁面)
2、以snsapi_userinfo為scope發起的網頁授權,是用來獲取用戶的基本信息的。但這種授權需要用戶手動同意,并且由于用戶同意過,所以無須關注,就可在授權后獲取該用戶的基本信息。
3、用戶管理類接口中的“獲取用戶基本信息接口”,是在用戶和公眾號產生消息交互或關注后事件推送后,才能根據用戶OpenID來獲取用戶基本信息。這個接口,包括其他微信接口,都是需要該用戶(即openid)關注了公眾號后,才能調用成功的。

關于網頁授權access_token和普通access_token的區別

1、微信網頁授權是通過OAuth2.0機制實現的,在用戶授權給公眾號后,公眾號可以獲取到一個網頁授權特有的接口調用憑證(網頁授權access_token),通過網頁授權access_token可以進行授權后接口調用,如獲取用戶基本信息;
2、其他微信接口,需要通過基礎支持中的“獲取access_token”接口來獲取到的普通access_token調用。

關于UnionID機制

1、請注意,網頁授權獲取用戶基本信息也遵循UnionID機制。即如果開發者有在多個公眾號,或在公眾號、移動應用之間統一用戶帳號的需求,需要前往微信開放平臺(open.weixin.qq.com)綁定公眾號后,才可利用UnionID機制來滿足上述需求。
2、UnionID機制的作用說明:如果開發者擁有多個移動應用、網站應用和公眾帳號,可通過獲取用戶基本信息中的unionid來區分用戶的唯一性,因為同一用戶,對同一個微信開放平臺下的不同應用(移動應用、網站應用和公眾帳號),unionid是相同的。

下面我們來進行網絡授權

1 第一步:用戶同意授權,獲取code
2 第二步:通過code換取網頁授權access_token
3 第三步:刷新access_token(如果需要)
4 第四步:拉取用戶信息(需scope為 snsapi_userinfo)

修改網頁賬號

如果用戶在微信客戶端中訪問第三方網頁,公眾號可以通過微信網頁授權機制,來獲取用戶基本信息,進而實現業務邏輯。

https://open.weixin.qq.com/connect/oauth2/authorize?appid=APPID&redirect_uri=REDIRECT_URI&response_type=code&scope=SCOPE&state=STATE#wechat_redirect

請求方法
獲取code后,請求以下鏈接獲取access_token: https://api.weixin.qq.com/sns/oauth2/access_token?appid=APPID&secret=SECRET&code=CODE&grant_type=authorization_code

//獲取網頁版的ACCESSTOKEN的URL
public static final String GET_WEB_ACCESSTOKEN_URL="https://api.weixin.qq.com/sns/oauth2/access_token?appid=APPID&secret=SECRET&code=CODE&grant_type=authorization_code";
//通過網頁獲取用戶信息的URL(不需要關注公眾號)
public static final String GET_WEB_USERINFO_URL = "https://api.weixin.qq.com/sns/userinfo?access_token=ACCESS_TOKEN&openid=OPENID&lang=zh_CN";

  @Controller
public class IndexController {
@RequestMapping("/index")
public String index(String code){
    System.out.println(code);
    //獲取ACCESSTOKEN
    String result = HttpUtil.get(WeixinUtil.GET_WEB_ACCESSTOKEN_URL.replace("APPID", WeixinUtil.APPID)
            .replace("SECRET", WeixinUtil.SECRET).replace("CODE", code));
    System.out.println(result);
    JSONObject json = JSON.parseObject(result);
    String access_token = json.getString("access_token");
    String openid = json.getString("openid");
    //獲取用戶信息
    String userinfo = HttpUtil.get(WeixinUtil.GET_WEB_USERINFO_URL.replace("ACCESS_TOKEN", access_token).replace("OPENID", openid));
    System.out.println(userinfo);
    //把openid放到session
    //.....
    return "index";
}
}

微信JS-SDK


第一步:綁定域名

第二步引入JSP文件

index.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<script type="text/javascript" src="http://res.wx.qq.com/open/js/jweixin-1.2.0.js"></script>
<script type="text/javascript">
    //通過config接口注入權限驗證配置
    wx.config({
        debug: true, // 開啟調試模式,調用的所有api的返回值會在客戶端alert出來,若要查看傳入的參數,可以在pc端打開,參數信息會通過log打出,僅在pc端時才會打印。
        appId: 'wxa379f9e383fb5ad7', // 必填,公眾號的唯一標識
        timestamp: '666666', // 必填,生成簽名的時間戳
        nonceStr: 'zhangshuai', // 必填,生成簽名的隨機串
        signature: 'c7f8ec79e45118a512820345cba07abac0caa0ca',// 必填,簽名,見附錄1
        jsApiList: ['onMenuShareTimeline', 'hideAllNonBaseMenuItem'] // 必填,需要使用的JS接口列表,所有JS接口列表見附錄2
    });
    wx.ready(function () {
        // config信息驗證后會執行ready方法,所有接口調用都必須在config接口獲得結果之后,config是一個客戶端的異步操作,所以如果需要在頁面加載時就調用相關接口,則須把相關接口放在ready函數中調用來確保正確執行。對于用戶觸發時才調用的接口,則可以直接調用,不需要放在ready函數中。
        wx.onMenuShareTimeline({
            title: '新的標題', // 分享標題
            desc: '新的描述', // 分享描述
            link: 'http://zhangshuai.nat100.top/index.jsp', // 分享鏈接,該鏈接域名或路徑必須與當前頁面對應的公眾號JS安全域名一致
            imgUrl: 'http://zhangshuai.nat100.top/1.jpg', // 分享圖標
            success: function () {
                window.location.;
                // 用戶確認分享后執行的回調函數\
            },
            cancel: function () {
                // 用戶取消分享后執行的回調函數
                alert("取消分享!")
            }
        });
        //隱藏非基礎按鈕
        wx.hideAllNonBaseMenuItem();
    });
</script>
<title>免費燒餅</title>
</head>
<body>
燒餅店
</body>
</html>

第三步:通過config接口注入權限驗證配置

  //通過config接口注入權限驗證配置
    wx.config({
        debug: true, // 開啟調試模式,調用的所有api的返回值會在客戶端alert出來,若要查看傳入的參數,可以在pc端打開,參數信息會通過log打出,僅在pc端時才會打印。
        appId: 'wxa379f9e383fb5ad7', // 必填,公眾號的唯一標識
        timestamp: '666666', // 必填,生成簽名的時間戳
        nonceStr: 'zhangshuai', // 必填,生成簽名的隨機串
        signature: 'c7f8ec79e45118a512820345cba07abac0caa0ca',// 必填,簽名,見附錄1
        jsApiList: ['onMenuShareTimeline', 'hideAllNonBaseMenuItem'] // 必填,需要使用的JS接口列表,所有JS接口列表見附錄2
    });

在這里值得注意的是signature生成簽名要按步驟

簽名說明
1.將 api_ticket、timestamp、card_id、code、openid、nonce_str的value值進行字符串的字典序排序。
2.將所有參數字符串拼接成一個字符串進行sha1加密,得到signature。
3.signature中的timestamp,nonce字段和card_ext中的timestamp,nonce_str字段必須保持一致。
4.code=1434008071,timestamp=1404896688,card_id=pjZ8Yt1XGILfi-FUsewpnnolGgZk, api_ticket=ojZ8YtyVyr30HheH3CM73y7h4jJE ,nonce_str=123 則signature=sha1(12314048966881434008071ojZ8YtyVyr30HheH3CM73y7h4jJEpjZ8Yt1XGILfi-FUsewpnnolGgZk)=f137ab68b7f8112d20ee528ab6074564e2796250。
強烈建議開發者使用卡券資料包中的簽名工具SDK進行簽名或使用debug工具進行校驗:http://mp.weixin.qq.com/debug/cgi-bin/sandbox?t=cardsign

WeixinUtil
Dubug模式獲取ticket票據

//獲取jssdk使用的ticket
public static final String GET_TICKET_URL = "https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token=ACCESS_TOKEN&type=jsapi";

public static void main(String[] args) {
    //通過access_token獲取ticket
    String temp = HttpUtil.get(WeixinUtil.GET_TICKET_URL.replace("ACCESS_TOKEN", getAccessToken()));
    System.out.println(temp);
}

將控制臺輸出的票據拷貝


確認簽名算法正確,可用 http://mp.weixin.qq.com/debug/cgi-bin/sandbox?t=jsapisign 頁面工具進行校驗。

這時將生成的signature簽名拷貝到

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 227,837評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,196評論 3 414
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 175,688評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,654評論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,456評論 6 406
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 54,955評論 1 321
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,044評論 3 440
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,195評論 0 287
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,725評論 1 333
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,608評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,802評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,318評論 5 358
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,048評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,422評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,673評論 1 281
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,424評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,762評論 2 372

推薦閱讀更多精彩內容