阿里雲部署Java網站和微信開發調試心得技巧(下)

阿里雲部署Java網站和微信開發調試心得技巧(下)
5、微信測試號的申請與鏈接以獲取微信用戶信息
在我們本身的程序裏面編寫servlet以響應微信號
在接下來的步驟中,咱們將在測試號裏面設置接口配置信息的URL,一經設置,微信公衆號便會發請求到咱們設置好的URL去,咱們必須編寫程序應答才能順利連通微信公衆號,所以我們須要編寫相應的響應程序
須要編寫兩個類
【SignUtil】java

package com.imooc.o2o.util.weixin; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.util.Arrays; /** * 微信請求校驗工具類 */ public class SignUtil { // 與接口配置信息中的Token要一致 private static String token = "myo2o"; /** * 驗證簽名 * * @param signature * @param timestamp * @param nonce * @return */ public static boolean checkSignature(String signature, String timestamp, String 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對比,標識該請求來源於微信 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; } }

【WechatController】git

package com.imooc.o2o.web.wechat; import java.io.IOException; import java.io.PrintWriter; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import com.imooc.o2o.util.wechat.SignUtil; @Controller //一會在設置的URL裏面就設置上這個路由 @RequestMapping("wechat") public class WechatController { private static Logger log = LoggerFactory.getLogger(WechatController.class); @RequestMapping(method = { RequestMethod.GET }) public void doGet(HttpServletRequest request, HttpServletResponse response) { log.debug("weixin get..."); // 微信加密簽名,signature結合了開發者填寫的token參數和請求中的timestamp參數、nonce參數。 String signature = request.getParameter("signature"); // 時間戳 String timestamp = request.getParameter("timestamp"); // 隨機數 String nonce = request.getParameter("nonce"); // 隨機字符串 String echostr = request.getParameter("echostr"); // 經過檢驗signature對請求進行校驗,若校驗成功則原樣返回echostr,表示接入成功,不然接入失敗 PrintWriter out = null; try { out = response.getWriter(); if (SignUtil.checkSignature(signature, timestamp, nonce)) { log.debug("weixin get success...."); out.print(echostr); } } catch (IOException e) { e.printStackTrace(); } finally { if (out != null) out.close(); } } }

以後從新部署一版最新的程序web

訪問微信測試號登陸頁面,經過打開本身手機的微信,掃一掃登陸
https://mp.weixin.qq.com/debug/cgi-bin/sandbox?t=sandbox/login
圖片描述
進入到測試號頁面後,分別看到以下信息
【測試號信息】
appID:開發者ID,是公衆號開發識別碼,配合開發者密碼能夠調用微信公衆號接口,如獲取微信暱稱等
appsecret:開發者密碼,是檢驗公衆號開發者身份的密碼,具備極高的安全性。切記不要把密碼交給第三方開發者或者編寫到代碼裏
圖片描述
【接口配置信息】
URL: 是開發者用來接收微信消息和事件的接口URL
Token:由開發者能夠任意填寫,用做生成簽名(該Token會和接口URL中包含的Token進行比對,從而驗證安全性)
圖片描述
【JS接口安全域名】
域名:想調用jssdk(如想要經過微信公衆號js接口獲取地圖等工具)必須得填寫此域名,在此域名的範圍內才能調用jssdk工具,注意這裏必須是域名,不是帶有http之類的URL,這裏直接填寫o2o.yitiaojieinfo.com
圖片描述
【測試號二維碼】
裏面包含了測試號二維碼以及已經關注了的用戶信息
圖片描述
【體驗接口權限表】
這裏主要介紹【網頁服務】裏面的【網頁賬號】
網頁賬號主要用來設置OAuth2.0裏面的網頁受權域名,用戶在網頁受權頁贊成受權給公衆號後,微信會將受權數據傳給一個回調頁面,回調頁面需在此域名下,以確保安全可靠。沙盒號回調地址支持域名和ip,正式公衆號回調地址只支持域名。這裏直接設置爲o2o.yitiaojieinfo.com
圖片描述
圖片描述
有不清楚的地方能夠直接參考微信官方文檔
https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421135319
接下來須要編寫本身的程序以獲取關注此公衆號的用戶信息
須要編寫5個類 WechatLoginController.java,UserAccessToken.java,WechatUser.java,WechatUtil.java以及MyX509TrustManager.java
【WechatLoginController】主要用來獲取已關注此微信號的用戶信息並作相應處理spring

package com.imooc.o2o.web.wechat; import java.io.IOException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import com.imooc.o2o.dto.UserAccessToken; import com.imooc.o2o.dto.WechatUser; import com.imooc.o2o.util.wechat.WeiXinUserUtil; @Controller @RequestMapping("wechatlogin") /** * 獲取關注公衆號以後的微信用戶信息的接口,若是在微信瀏覽器裏訪問 * https://open.weixin.qq.com/connect/oauth2/authorize?appid=您的appId&redirect_uri=http://o2o.yitiaojieinfo.com/o2o/wechatlogin/logincheck&role_type=1&response_type=code&scope=snsapi_userinfo&state=1#wechat_redirect * 則這裏將會獲取到code,以後再能夠經過code獲取到access_token 進而獲取到用戶信息 * * @author xiangze * */ public class WechatLoginController { private static Logger log = LoggerFactory.getLogger(WechatLoginController.class); @RequestMapping(value = "/logincheck", method = { RequestMethod.GET }) public String doGet(HttpServletRequest request, HttpServletResponse response) { log.debug("weixin login get..."); // 獲取微信公衆號傳輸過來的code,經過code可獲取access_token,進而獲取用戶信息 String code = request.getParameter("code"); // 這個state能夠用來傳咱們自定義的信息,方便程序調用,這裏也能夠不用 // String roleType = request.getParameter("state"); log.debug("weixin login code:" + code); WechatUser user = null; String openId = null; if (null != code) { UserAccessToken token; try { // 經過code獲取access_token token = WeiXinUserUtil.getUserAccessToken(code); log.debug("weixin login token:" + token.toString()); // 經過token獲取accessToken String accessToken = token.getAccessToken(); // 經過token獲取openId openId = token.getOpenId(); // 經過access_token和openId獲取用戶暱稱等信息 user = WeiXinUserUtil.getUserInfo(accessToken, openId); log.debug("weixin login user:" + user.toString()); request.getSession().setAttribute("openId", openId); } catch (IOException e) { log.error("error in getUserAccessToken or getUserInfo or findByOpenId: " + e.toString()); e.printStackTrace(); } } // ======todo begin====== // 前面我們獲取到openId後,能夠經過它去數據庫判斷該微信賬號是否在咱們網站裏有對應的賬號了, // 沒有的話這裏能夠自動建立上,直接實現微信與我們網站的無縫對接。 // ======todo end====== if (user != null) { // 獲取到微信驗證的信息後返回到指定的路由(須要本身設定) return "frontend/index"; } else { return null; } } }

【UserAccessToken】用戶AccessToken實體類,用來接收accesstoken以及openid等信息數據庫

package com.imooc.o2o.dto; import com.fasterxml.jackson.annotation.JsonProperty; /** * 用戶受權token * * @author xiangze * */ public class UserAccessToken { // 獲取到的憑證 @JsonProperty("access_token") private String accessToken; // 憑證有效時間,單位:秒 @JsonProperty("expires_in") private String expiresIn; // 表示更新令牌,用來獲取下一次的訪問令牌,這裏沒太大用處 @JsonProperty("refresh_token") private String refreshToken; // 該用戶在此公衆號下的身份標識,對於此微信號具備惟一性 @JsonProperty("openid") private String openId; // 表示權限範圍,這裏可省略 @JsonProperty("scope") private String scope; public String getAccessToken() { return accessToken; } public void setAccessToken(String accessToken) { this.accessToken = accessToken; } public String getExpiresIn() { return expiresIn; } public void setExpiresIn(String expiresIn) { this.expiresIn = expiresIn; } public String getRefreshToken() { return refreshToken; } public void setRefreshToken(String refreshToken) { this.refreshToken = refreshToken; } public String getOpenId() { return openId; } public void setOpenId(String openId) { this.openId = openId; } public String getScope() { return scope; } public void setScope(String scope) { this.scope = scope; } @Override public String toString() { return "accessToken:" + this.getAccessToken() + ",openId:" + this.getOpenId(); } }

【WechatUser】微信用戶實體類,用來接收暱稱 openid等用戶信息json

package com.imooc.o2o.dto; import java.io.Serializable; import com.fasterxml.jackson.annotation.JsonProperty; /** * 微信用戶實體類 * * @author xiangze * */ public class WechatUser implements Serializable { /** * */ private static final long serialVersionUID = -4684067645282292327L; // openId,標識該公衆號下面的該用戶的惟一Id @JsonProperty("openid") private String openId; // 用戶暱稱 @JsonProperty("nickname") private String nickName; // 性別 @JsonProperty("sex") private int sex; // 省份 @JsonProperty("province") private String province; // 城市 @JsonProperty("city") private String city; // 區 @JsonProperty("country") private String country; // 頭像圖片地址 @JsonProperty("headimgurl") private String headimgurl; // 語言 @JsonProperty("language") private String language; // 用戶權限,這裏沒什麼做用 @JsonProperty("privilege") private String[] privilege; public String getOpenId() { return openId; } public void setOpenId(String openId) { this.openId = openId; } public String getNickName() { return nickName; } public void setNickName(String nickName) { this.nickName = nickName; } public int getSex() { return sex; } public void setSex(int sex) { this.sex = sex; } public String getProvince() { return province; } public void setProvince(String province) { this.province = province; } public String getCity() { return city; } public void setCity(String city) { this.city = city; } public String getCountry() { return country; } public void setCountry(String country) { this.country = country; } public String getHeadimgurl() { return headimgurl; } public void setHeadimgurl(String headimgurl) { this.headimgurl = headimgurl; } public String getLanguage() { return language; } public void setLanguage(String language) { this.language = language; } public String[] getPrivilege() { return privilege; } public void setPrivilege(String[] privilege) { this.privilege = privilege; } @Override public String toString() { return "openId:" + this.getOpenId() + ",nikename:"+this.getNickName();}}

【WechatUtil】主要用來提交https請求給微信獲取用戶信息api

package com.imooc.o2o.util.wechat; import java.io.BufferedReader; import java.io.IOException; 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 org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.fasterxml.jackson.core.JsonParseException; import com.fasterxml.jackson.databind.JsonMappingException; import com.fasterxml.jackson.databind.ObjectMapper; import com.imooc.o2o.dto.UserAccessToken; import com.imooc.o2o.dto.WechatUser; /** * 微信工具類 * * @author xiangze * */ public class WechatUtil { private static Logger log = LoggerFactory.getLogger(WechatUtil.class); /** * 獲取UserAccessToken實體類 * * @param code * @return * @throws IOException */ public static UserAccessToken getUserAccessToken(String code) throws IOException { // 測試號信息裏的appId String appId = "您的appId"; log.debug("appId:" + appId); // 測試號信息裏的appsecret String appsecret = "您的appsecret"; log.debug("secret:" + appsecret); // 根據傳入的code,拼接出訪問微信定義好的接口的URL String url = "https://api.weixin.qq.com/sns/oauth2/access_token?appid=" + appId + "&secret=" + appsecret + "&code=" + code + "&grant_type=authorization_code"; // 向相應URL發送請求獲取token json字符串 String tokenStr = httpsRequest(url, "GET", null); log.debug("userAccessToken:" + tokenStr); UserAccessToken token = new UserAccessToken(); ObjectMapper objectMapper = new ObjectMapper(); try { // 將json字符串轉換成相應對象 token = objectMapper.readValue(tokenStr, UserAccessToken.class); } catch (JsonParseException e) { log.error("獲取用戶accessToken失敗: " + e.getMessage()); e.printStackTrace(); } catch (JsonMappingException e) { log.error("獲取用戶accessToken失敗: " + e.getMessage()); e.printStackTrace(); } catch (IOException e) { log.error("獲取用戶accessToken失敗: " + e.getMessage()); e.printStackTrace(); } if (token == null) { log.error("獲取用戶accessToken失敗。"); return null; } return token; } /** * 獲取WechatUser實體類 * * @param accessToken * @param openId * @return */ public static WechatUser getUserInfo(String accessToken, String openId) { // 根據傳入的accessToken以及openId拼接出訪問微信定義的端口並獲取用戶信息的URL String url = "https://api.weixin.qq.com/sns/userinfo?access_token=" + accessToken + "&openid=" + openId + "&lang=zh_CN"; // 訪問該URL獲取用戶信息json 字符串 String userStr = httpsRequest(url,"GET",null); log.debug("user info :"+ userStr);WechatUser user =newWechatUser();ObjectMapper objectMapper =newObjectMapper();try{// 將json字符串轉換成相應對象 user = objectMapper.readValue(userStr,WechatUser.class);}catch(JsonParseException e){ log.error("獲取用戶信息失敗: "+ e.getMessage()); e.printStackTrace();}catch(JsonMappingException e){ log.error("獲取用戶信息失敗: "+ e.getMessage()); e.printStackTrace();}catch(IOException e){ log.error("獲取用戶信息失敗: "+ e.getMessage()); e.printStackTrace();}if(user ==null){ log.error("獲取用戶信息失敗。");returnnull;}return user;}/** * 發起https請求並獲取結果 * * @param requestUrl * 請求地址 * @param requestMethod * 請求方式(GET、POST) * @param outputStr * 提交的數據 * @return json字符串 */publicstaticString httpsRequest(String requestUrl,String requestMethod,String outputStr){StringBuffer buffer =newStringBuffer();try{// 建立SSLContext對象,並使用咱們指定的信任管理器初始化TrustManager[] tm ={newMyX509TrustManager()};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 =newInputStreamReader(inputStream,"utf-8");BufferedReader bufferedReader =newBufferedReader(inputStreamReader);String str =null;while((str = bufferedReader.readLine())!=null){ buffer.append(str);} bufferedReader.close(); inputStreamReader.close();// 釋放資源 inputStream.close(); inputStream =null; httpUrlConn.disconnect(); log.debug("https buffer:"+ buffer.toString());}catch(ConnectException ce){ log.error("Weixin server connection timed out.");}catch(Exception e){ log.error("https request error:{}", e);}return buffer.toString();}}

【MyX509TrustManager】主要繼承X509TrustManager作https證書信任管理器數組

package com.imooc.o2o.util.wechat; import java.security.cert.CertificateException; import java.security.cert.X509Certificate; import javax.net.ssl.X509TrustManager; /** * 證書信任管理器(用於https請求) * * @author xiangze * */ public class MyX509TrustManager implements X509TrustManager { public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException { } public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException { } public X509Certificate[] getAcceptedIssuers() { return null; } }

以後從新打包一個新的war包併發布到服務器tomcat webapps目錄下
發佈成功後,關注你本身的測試號(即掃描測試號的那個二維碼),而後在手機微信裏面或者微信開發者工具裏訪問相應連接:
https://open.weixin.qq.com/connect/oauth2/authorize?appid=您的appid&redirect_uri=http://o2o.yitiaojieinfo.com/o2o/wechatlogin/logincheck&role_type=1&response_type=code&scope=snsapi_userinfo&state=1#wechat_redirect
圖片描述
以後查看日誌信息,便能發現確實可以獲取到用戶的信息了
圖片描述
至此我們已經從無到有完成了阿里雲部署Java網站和微信公衆號的開發,文章雖長,可是乾貨多多,都是傻瓜式的配置,但願你們喜歡。
注意事項:瀏覽器

  1. 因爲tomcat我們沒有調優,啓動較慢,建議每次重啓等待幾分鐘再去訪問,不然會假死,須要kill掉再重啓
  2. 通過認證的微信服務號跟測試號實際上是同樣的,你們能夠按照上述去配置
相關文章
相關標籤/搜索