1. 程式人生 > >微信公眾號開發(一)

微信公眾號開發(一)

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

各公眾號區別:
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

@[email protected]@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.html 、 http://www.qq.com/login.html 都可以進行OAuth2.0鑑權。但http://pay.qq.com 、 http://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.href = "http://www.baidu.com";
                // 使用者確認分享後執行的回撥函式\
            },
            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簽名拷貝到