首先,整理一下獲取授權的步驟。
一、配置微信服務器。
//省略import部分
@RequestMapping("/wechat")
@Controller
public class MobileWechatController {
private static String token = "wechat";
@RequestMapping(value = "/testWx")
public void get(HttpServletRequest request, HttpServletResponse response) throws Exception {
System.out.println("========WechatController========= ");
Enumeration pNames = request.getParameterNames();
while (pNames.hasMoreElements()) {
String name = (String) pNames.nextElement();
String value = request.getParameter(name);
// out.print(name + "=" + value);
String log = "name =" + name + " value =" + value;
System.out.println(log + "aaaa");
}
String signature = request.getParameter("signature");/// 微信加密簽名
String timestamp = request.getParameter("timestamp");/// 時間戳
String nonce = request.getParameter("nonce"); /// 隨機數
String echostr = request.getParameter("echostr"); // 隨機字符串
PrintWriter out = response.getWriter();
if (checkSignature(signature, timestamp, nonce)) {
System.out.println("--------接入成功--------");
out.print(echostr);
}
out.close();
out = null;
}
private static boolean checkSignature(String signature, String timestamp, String nonce) {
System.out.println("signature:" + signature + "timestamp:" + timestamp + "nonce:" + nonce);
String[] arr = new String[] { token, timestamp, nonce };
// 將token、timestamp、nonce三個參數進行字典序排序
Arrays.sort(arr);
StringBuilder content = new StringBuilder();
for (int i = 0; i < arr.length; i++) {
content.append(arr[i]);
}
MessageDigest md = null;
String tmpStr = null;
try {
md = MessageDigest.getInstance("SHA-1");
// 將三個參數字符串拼接成一個字符串進行sha1加密
byte[] digest = md.digest(content.toString().getBytes());
tmpStr = byteToStr(digest);
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
content = null;
// 將sha1加密後的字符串可與signature對比,標識該請求來源於微信
System.out.println(tmpStr.equals(signature.toUpperCase()));
return tmpStr != null ? tmpStr.equals(signature.toUpperCase()) : false;
}
/**
* 將字節數組轉換爲十六進制字符串
*
* @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;
}
}
二、封裝向微信服務器發送請求的工具類。
/**
* 調用對方接口方法
* @param path 對方或第三方提供的路徑
* @param data 向對方或第三方發送的數據,大多數情況下給對方發送JSON數據讓對方解析
*/
public static String interfaceUtil(String path, String data) {
try {
URL url = new URL(path);
//打開和url之間的連接
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
PrintWriter out = null;
/**設置URLConnection的參數和普通的請求屬性****start***/
conn.setRequestProperty("accept", "*/*");
conn.setRequestProperty("connection", "Keep-Alive");
conn.setRequestProperty("user-agent", "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1)");
/**設置URLConnection的參數和普通的請求屬性****end***/
//設置是否向httpUrlConnection輸出,設置是否從httpUrlConnection讀入,此外發送post請求必須設置這兩個
//最常用的Http請求無非是get和post,get請求可以獲取靜態頁面,也可以把參數放在URL字串後面,傳遞給servlet,
//post與get的 不同之處在於post的參數不是放在URL字串裏面,而是放在http請求的正文內。
conn.setDoOutput(true);
conn.setDoInput(true);
conn.setRequestMethod("GET");//GET和POST必須全大寫
/**GET方法請求*****start*/
/**
* 如果只是發送GET方式請求,使用connet方法建立和遠程資源之間的實際連接即可;
* 如果發送POST方式的請求,需要獲取URLConnection實例對應的輸出流來發送請求參數。
* */
conn.connect();
/**GET方法請求*****end*/
/***POST方法請求****start*/
/*out = new PrintWriter(conn.getOutputStream());//獲取URLConnection對象對應的輸出流
out.print(data);//發送請求參數即數據
out.flush();//緩衝數據
*/
/***POST方法請求****end*/
//獲取URLConnection對象對應的輸入流
InputStream is = conn.getInputStream();
//構造一個字符流緩存
BufferedReader br = new BufferedReader(new InputStreamReader(is));
String str = "";
String sss = "";
while ((str = br.readLine()) != null) {
str=new String(str.getBytes(),"UTF-8");//解決中文亂碼問題
System.out.println(str);
sss = str;
}
//關閉流
is.close();
//斷開連接,最好寫上,disconnect是在底層tcp socket鏈接空閒時才切斷。如果正在被其他線程使用就不切斷。
//固定多線程的話,如果不disconnect,鏈接會增多,直到收發不出信息。寫上disconnect後正常一些。
conn.disconnect();
System.out.println("完整結束");
//數據庫存入本次運行的accesstoken quartz中一小時五十九分鐘刷新一次
return sss;
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
微信所有的請求和方法都可以通過這個方法進行,舉例:
String wechatLogin = WechatUtils.interfaceUtil(
"https://api.weixin.qq.com/cgi-bin/token?grant_type=" + GRANTTYPE
+ "&appid=" + APPID
+ "&secret=" + APPSECRET
, "");
// 發起GET請求獲取憑證
JSONObject jsonObject = JSON.parseObject(wechatLogin);
使用上述的方法,將需要發送到微信服務器的url地址按照要求組裝好,即可接收到回傳的wechatLogin json字符串,再稍微進行處理即可使用回傳數據。
三、開始微信靜默授權。
開始靜默授權前,我們先整理一下這個過程。
首先,在我們的公衆號上通過代碼創建一個菜單
/**
* 創建菜單
*
* @param menu 菜單實例
* @param accessToken 有效的access_token
* @return 0表示成功,其他值表示失敗
*/
public static int createMenu(Menu menu, String accessToken) {
int result = 0;
// 拼裝創建菜單的url
String url = MENU_CREATE_URL.replace("ACCESS_TOKEN", accessToken);
System.out.println("url:"+url);
// 將菜單對象轉換成json字符串
String jsonMenu = JSONObject.toJSON(menu).toString();
System.out.println("jsonMenu"+jsonMenu);
// 調用接口創建菜單
JSONObject jsonObject = httpRequest(url, "POST", jsonMenu);
System.out.println("jsonObject"+jsonObject);
if (null != jsonObject) {
if (0 != jsonObject.getInteger("errcode")) {
result = jsonObject.getInteger("errcode");
logger.error("創建菜單失敗 errcode:{} errmsg:{}", jsonObject.getInteger("errcode"), jsonObject.getString("errmsg"));
}
}
return result;
}
這裏有兩個參數,menu和accessToken,accessToken根據微信官方文檔中的方法進行獲取,每個accessToken的使用時間爲兩小時,但是由於它的獲取次數有限制,所以推薦在服務器端將accessToken的獲取設置成一段時間獲取一次,需要的時候去數據庫裏取用即可。menu是我們再公衆號前臺展示的按鈕樣式,可以通過一個實體類進行組裝
//修改菜單
private static Menu getMenu(){
Button btn1 = new Button();
btn1.setName("配電巡檢");
btn1.setType("view");
btn1.setKey("11");
btn1.setUrl("https://open.weixin.qq.com/connect/oauth2/authorize?" +
"appid=************&" +
"redirect_uri=http%3A%2F%2Fwww.*******.cn%2F******&" +
"response_type=code&" +
"scope=snsapi_base&" +
"state=123#wechat_redirect");
System.out.println(btn1.getUrl());
Menu menu = new Menu();
menu.setButton(new Button[]{btn1});
return menu;
}
這裏重點說一下redirect_uri這個參數,uri中的參數路徑需要與微信公衆平臺中的 開發——接口權限——網頁授權獲得用戶基本信息——修改 中的網頁授權域名對應,否則點擊之前配置好的menu會出現redirect_url參數錯誤或redirect_url和服務器配置不一樣的錯誤,出現這兩個錯誤,只要關注改正menu參數的配置和網頁授權域名這兩個部分即可。
這些配置都成功以後,點擊菜單對應的按鈕後,用戶界面展示的應該是一個參數中完整的redirect_url+code=*********,
這裏的code就是拉取授權的關鍵,我們使用code組裝下一個uri,用於獲取用戶對公衆號唯一的openid。
(這裏附一個小方法,用於獲取回傳頁面上的code參數)
public static String getCode(HttpServletRequest request){
String code = request.getParameter("code");
System.out.println(code);
return code;
}
String code = WechatUtils.getCode(request);
System.out.println("code ----- " + code);
LoginAccessToken loginAccessToken = null;
String accessToken = WechatUtils.interfaceUtil(
"https://api.weixin.qq.com/sns/oauth2/access_token?" +
"appid=" + APPID +
"&secret=" + APPSECRET +
"&code=" + code +
"&grant_type=authorization_code"
,""
);
JSONObject jsonObject = JSON.parseObject(accessToken);
注意:這裏的accessToken和全局的accessToken不一樣。
至此,用戶的openid已經在不知不覺中被我們獲取到了,這個openid可以用做發送模板消息等操作,總結下來發現,微信公衆號的開發其實不難,無外乎 發送http請求+展示自己的頁面,遇到問題再補充。