在我們本身的程序裏面編寫servlet以響應微信號
在接下來的步驟中,咱們將在測試號裏面設置接口配置信息的URL,一經設置,微信公衆號便會發請求到咱們設置好的URL去,咱們必須編寫程序應答才能順利連通微信公衆號,所以我們須要編寫相應的響應程序
須要編寫兩個類java
【SignUtil】git
1 /** 2 * 微信請求校驗工具類 3 */ 4 public class SignUtil { 5 // 與接口配置信息中的Token要一致 6 private static String token = "myo2o"; 7 8 /** 9 * 驗證簽名 10 * 11 * @param signature 12 * @param timestamp 13 * @param nonce 14 * @return 15 */ 16 public static boolean checkSignature(String signature, String timestamp, String nonce) { 17 String[] arr = new String[] { token, timestamp, nonce }; 18 // 將token、timestamp、nonce三個參數進行字典序排序 19 Arrays.sort(arr); 20 StringBuilder content = new StringBuilder(); 21 for (int i = 0; i < arr.length; i++) { 22 content.append(arr[i]); 23 } 24 MessageDigest md = null; 25 String tmpStr = null; 26 27 try { 28 md = MessageDigest.getInstance("SHA-1"); 29 // 將三個參數字符串拼接成一個字符串進行sha1加密 30 byte[] digest = md.digest(content.toString().getBytes()); 31 tmpStr = byteToStr(digest); 32 } catch (NoSuchAlgorithmException e) { 33 e.printStackTrace(); 34 } 35 36 content = null; 37 // 將sha1加密後的字符串可與signature對比,標識該請求來源於微信 38 return tmpStr != null ? tmpStr.equals(signature.toUpperCase()) : false; 39 } 40 41 /** 42 * 將字節數組轉換爲十六進制字符串 43 * 44 * @param byteArray 45 * @return 46 */ 47 private static String byteToStr(byte[] byteArray) { 48 String strDigest = ""; 49 for (int i = 0; i < byteArray.length; i++) { 50 strDigest += byteToHexStr(byteArray[i]); 51 } 52 return strDigest; 53 } 54 55 /** 56 * 將字節轉換爲十六進制字符串 57 * 58 * @param mByte 59 * @return 60 */ 61 private static String byteToHexStr(byte mByte) { 62 char[] Digit = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' }; 63 char[] tempArr = new char[2]; 64 tempArr[0] = Digit[(mByte >>> 4) & 0X0F]; 65 tempArr[1] = Digit[mByte & 0X0F]; 66 67 String s = new String(tempArr); 68 return s; 69 } 70 }
【WechatController】web
1 @Controller 2 //一會在設置的URL裏面就設置上這個路由 3 @RequestMapping("wechat") 4 public class WechatController { 5 6 private static Logger log = LoggerFactory.getLogger(WechatController.class); 7 8 @RequestMapping(method = { RequestMethod.GET }) 9 public void doGet(HttpServletRequest request, HttpServletResponse response) { 10 log.debug("weixin get..."); 11 // 微信加密簽名,signature結合了開發者填寫的token參數和請求中的timestamp參數、nonce參數。 12 String signature = request.getParameter("signature"); 13 // 時間戳 14 String timestamp = request.getParameter("timestamp"); 15 // 隨機數 16 String nonce = request.getParameter("nonce"); 17 // 隨機字符串 18 String echostr = request.getParameter("echostr"); 19 20 // 經過檢驗signature對請求進行校驗,若校驗成功則原樣返回echostr,表示接入成功,不然接入失敗 21 PrintWriter out = null; 22 try { 23 out = response.getWriter(); 24 if (SignUtil.checkSignature(signature, timestamp, nonce)) { 25 log.debug("weixin get success...."); 26 out.print(echostr); 27 } 28 } catch (IOException e) { 29 e.printStackTrace(); 30 } finally { 31 if (out != null) 32 out.close(); 33 } 34 } 35 }
以後從新部署一版最新的程序
訪問微信測試號登陸頁面,經過打開本身手機的微信,掃一掃登陸數據庫
https://mp.weixin.qq.com/debug/cgi-bin/sandbox?t=sandbox/loginjson
進入到測試號頁面後,分別看到以下信息
【測試號信息】
appID:開發者ID,是公衆號開發識別碼,配合開發者密碼能夠調用微信公衆號接口,如獲取微信暱稱等
appsecret:開發者密碼,是檢驗公衆號開發者身份的密碼,具備極高的安全性。切記不要把密碼交給第三方開發者或者編寫到代碼裏api
【接口配置信息】
URL: 是開發者用來接收微信消息和事件的接口URL
Token:由開發者能夠任意填寫,用做生成簽名(該Token會和接口URL中包含的Token進行比對,從而驗證安全性)
數組
有不清楚的地方能夠直接參考微信官方文檔
https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421135319
接下來須要編寫本身的程序以獲取關注此公衆號的用戶信息
須要編寫5個類 WechatLoginController.java,UserAccessToken.java,WechatUser.java,WechatUtil.java以及MyX509TrustManager.java
【WechatLoginController】主要用來獲取已關注此微信號的用戶信息並作相應處理瀏覽器
1 @Controller 2 @RequestMapping("wechatlogin") 3 /** 4 * 獲取關注公衆號以後的微信用戶信息的接口,若是在微信瀏覽器裏訪問 5 * 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 6 * 則這裏將會獲取到code,以後再能夠經過code獲取到access_token 進而獲取到用戶信息 7 * 8 * @author 9 * 10 */ 11 public class WechatLoginController { 12 13 private static Logger log = LoggerFactory.getLogger(WechatLoginController.class); 14 15 @RequestMapping(value = "/logincheck", method = { RequestMethod.GET }) 16 public String doGet(HttpServletRequest request, HttpServletResponse response) { 17 log.debug("weixin login get..."); 18 // 獲取微信公衆號傳輸過來的code,經過code可獲取access_token,進而獲取用戶信息 19 String code = request.getParameter("code"); 20 // 這個state能夠用來傳咱們自定義的信息,方便程序調用,這裏也能夠不用 21 // String roleType = request.getParameter("state"); 22 log.debug("weixin login code:" + code); 23 WechatUser user = null; 24 String openId = null; 25 if (null != code) { 26 UserAccessToken token; 27 try { 28 // 經過code獲取access_token 29 token = WeiXinUserUtil.getUserAccessToken(code); 30 log.debug("weixin login token:" + token.toString()); 31 // 經過token獲取accessToken 32 String accessToken = token.getAccessToken(); 33 // 經過token獲取openId 34 openId = token.getOpenId(); 35 // 經過access_token和openId獲取用戶暱稱等信息 36 user = WeiXinUserUtil.getUserInfo(accessToken, openId); 37 log.debug("weixin login user:" + user.toString()); 38 request.getSession().setAttribute("openId", openId); 39 } catch (IOException e) { 40 log.error("error in getUserAccessToken or getUserInfo or findByOpenId: " + e.toString()); 41 e.printStackTrace(); 42 } 43 } 44 // ======todo begin====== 45 // 前面我們獲取到openId後,能夠經過它去數據庫判斷該微信賬號是否在咱們網站裏有對應的賬號了, 46 // 沒有的話這裏能夠自動建立上,直接實現微信與我們網站的無縫對接。 47 // ======todo end====== 48 if (user != null) { 49 // 獲取到微信驗證的信息後返回到指定的路由(須要本身設定) 50 return "frontend/index"; 51 } else { 52 return null; 53 } 54 } 55 }
【UserAccessToken】用戶AccessToken實體類,用來接收accesstoken以及openid等信息 Dtotomcat
1 /** 2 * 用戶受權token 3 * 4 * @author 5 * 6 */ 7 public class UserAccessToken { 8 9 // 獲取到的憑證 10 @JsonProperty("access_token") 11 private String accessToken; 12 // 憑證有效時間,單位:秒 13 @JsonProperty("expires_in") 14 private String expiresIn; 15 // 表示更新令牌,用來獲取下一次的訪問令牌,這裏沒太大用處 16 @JsonProperty("refresh_token") 17 private String refreshToken; 18 // 該用戶在此公衆號下的身份標識,對於此微信號具備惟一性 19 @JsonProperty("openid") 20 private String openId; 21 // 表示權限範圍,這裏可省略 22 @JsonProperty("scope") 23 private String scope; 24 25 public String getAccessToken() { 26 return accessToken; 27 } 28 29 public void setAccessToken(String accessToken) { 30 this.accessToken = accessToken; 31 } 32 33 public String getExpiresIn() { 34 return expiresIn; 35 } 36 37 public void setExpiresIn(String expiresIn) { 38 this.expiresIn = expiresIn; 39 } 40 41 public String getRefreshToken() { 42 return refreshToken; 43 } 44 45 public void setRefreshToken(String refreshToken) { 46 this.refreshToken = refreshToken; 47 } 48 49 public String getOpenId() { 50 return openId; 51 } 52 53 public void setOpenId(String openId) { 54 this.openId = openId; 55 } 56 57 public String getScope() { 58 return scope; 59 } 60 61 public void setScope(String scope) { 62 this.scope = scope; 63 } 64 65 @Override 66 public String toString() { 67 return "accessToken:" + this.getAccessToken() + ",openId:" + this.getOpenId(); 68 } 69 70 }
【WechatUser】微信用戶實體類,用來接收暱稱 openid等用戶信息 Dto安全
1 /** 2 * 微信用戶實體類 3 * 4 * @author 5 * 6 */ 7 public class WechatUser implements Serializable { 8 9 /** 10 * 11 */ 12 private static final long serialVersionUID = -4684067645282292327L; 13 14 // openId,標識該公衆號下面的該用戶的惟一Id 15 @JsonProperty("openid") 16 private String openId; 17 // 用戶暱稱 18 @JsonProperty("nickname") 19 private String nickName; 20 // 性別 21 @JsonProperty("sex") 22 private int sex; 23 // 省份 24 @JsonProperty("province") 25 private String province; 26 // 城市 27 @JsonProperty("city") 28 private String city; 29 // 區 30 @JsonProperty("country") 31 private String country; 32 // 頭像圖片地址 33 @JsonProperty("headimgurl") 34 private String headimgurl; 35 // 語言 36 @JsonProperty("language") 37 private String language; 38 // 用戶權限,這裏沒什麼做用 39 @JsonProperty("privilege") 40 private String[] privilege; 41 42 public String getOpenId() { 43 return openId; 44 } 45 46 public void setOpenId(String openId) { 47 this.openId = openId; 48 } 49 50 public String getNickName() { 51 return nickName; 52 } 53 54 public void setNickName(String nickName) { 55 this.nickName = nickName; 56 } 57 58 public int getSex() { 59 return sex; 60 } 61 62 public void setSex(int sex) { 63 this.sex = sex; 64 } 65 66 public String getProvince() { 67 return province; 68 } 69 70 public void setProvince(String province) { 71 this.province = province; 72 } 73 74 public String getCity() { 75 return city; 76 } 77 78 public void setCity(String city) { 79 this.city = city; 80 } 81 82 public String getCountry() { 83 return country; 84 } 85 86 public void setCountry(String country) { 87 this.country = country; 88 } 89 90 public String getHeadimgurl() { 91 return headimgurl; 92 } 93 94 public void setHeadimgurl(String headimgurl) { 95 this.headimgurl = headimgurl; 96 } 97 98 public String getLanguage() { 99 return language; 100 } 101 102 public void setLanguage(String language) { 103 this.language = language; 104 } 105 106 public String[] getPrivilege() { 107 return privilege; 108 } 109 110 public void setPrivilege(String[] privilege) { 111 this.privilege = privilege; 112 } 113 114 @Override 115 public String toString() { 116 return "openId:" + this.getOpenId() + ",nikename:" + this.getNickName(); 117 } 118 }
【WechatUtil】主要用來提交https請求給微信獲取用戶信息
1 /** 2 * 微信工具類 3 * 4 * @author 5 * 6 */ 7 public class WechatUtil { 8 9 private static Logger log = LoggerFactory.getLogger(WechatUtil.class); 10 11 /** 12 * 獲取UserAccessToken實體類 13 * 14 * @param code 15 * @return 16 * @throws IOException 17 */ 18 public static UserAccessToken getUserAccessToken(String code) throws IOException { 19 // 測試號信息裏的appId 20 String appId = "您的appId"; 21 log.debug("appId:" + appId); 22 // 測試號信息裏的appsecret 23 String appsecret = "您的appsecret"; 24 log.debug("secret:" + appsecret); 25 // 根據傳入的code,拼接出訪問微信定義好的接口的URL 26 String url = "https://api.weixin.qq.com/sns/oauth2/access_token?appid=" + appId + "&secret=" + appsecret 27 + "&code=" + code + "&grant_type=authorization_code"; 28 // 向相應URL發送請求獲取token json字符串 29 String tokenStr = httpsRequest(url, "GET", null); 30 log.debug("userAccessToken:" + tokenStr); 31 UserAccessToken token = new UserAccessToken(); 32 ObjectMapper objectMapper = new ObjectMapper(); 33 try { 34 // 將json字符串轉換成相應對象 35 token = objectMapper.readValue(tokenStr, UserAccessToken.class); 36 } catch (JsonParseException e) { 37 log.error("獲取用戶accessToken失敗: " + e.getMessage()); 38 e.printStackTrace(); 39 } catch (JsonMappingException e) { 40 log.error("獲取用戶accessToken失敗: " + e.getMessage()); 41 e.printStackTrace(); 42 } catch (IOException e) { 43 log.error("獲取用戶accessToken失敗: " + e.getMessage()); 44 e.printStackTrace(); 45 } 46 if (token == null) { 47 log.error("獲取用戶accessToken失敗。"); 48 return null; 49 } 50 return token; 51 } 52 53 /** 54 * 獲取WechatUser實體類 55 * 56 * @param accessToken 57 * @param openId 58 * @return 59 */ 60 public static WechatUser getUserInfo(String accessToken, String openId) { 61 // 根據傳入的accessToken以及openId拼接出訪問微信定義的端口並獲取用戶信息的URL 62 String url = "https://api.weixin.qq.com/sns/userinfo?access_token=" + accessToken + "&openid=" + openId 63 + "&lang=zh_CN"; 64 // 訪問該URL獲取用戶信息json 字符串 65 String userStr = httpsRequest(url, "GET", null); 66 log.debug("user info :" + userStr); 67 WechatUser user = new WechatUser(); 68 ObjectMapper objectMapper = new ObjectMapper(); 69 try { 70 // 將json字符串轉換成相應對象 71 user = objectMapper.readValue(userStr, WechatUser.class); 72 } catch (JsonParseException e) { 73 log.error("獲取用戶信息失敗: " + e.getMessage()); 74 e.printStackTrace(); 75 } catch (JsonMappingException e) { 76 log.error("獲取用戶信息失敗: " + e.getMessage()); 77 e.printStackTrace(); 78 } catch (IOException e) { 79 log.error("獲取用戶信息失敗: " + e.getMessage()); 80 e.printStackTrace(); 81 } 82 if (user == null) { 83 log.error("獲取用戶信息失敗。"); 84 return null; 85 } 86 return user; 87 } 88 89 /** 90 * 發起https請求並獲取結果 91 * 92 * @param requestUrl 93 * 請求地址 94 * @param requestMethod 95 * 請求方式(GET、POST) 96 * @param outputStr 97 * 提交的數據 98 * @return json字符串 99 */ 100 public static String httpsRequest(String requestUrl, String requestMethod, String outputStr) { 101 StringBuffer buffer = new StringBuffer(); 102 try { 103 // 建立SSLContext對象,並使用咱們指定的信任管理器初始化 104 TrustManager[] tm = { new MyX509TrustManager() }; 105 SSLContext sslContext = SSLContext.getInstance("SSL", "SunJSSE"); 106 sslContext.init(null, tm, new java.security.SecureRandom()); 107 // 從上述SSLContext對象中獲得SSLSocketFactory對象 108 SSLSocketFactory ssf = sslContext.getSocketFactory(); 109 110 URL url = new URL(requestUrl); 111 HttpsURLConnection httpUrlConn = (HttpsURLConnection) url.openConnection(); 112 httpUrlConn.setSSLSocketFactory(ssf); 113 114 httpUrlConn.setDoOutput(true); 115 httpUrlConn.setDoInput(true); 116 httpUrlConn.setUseCaches(false); 117 // 設置請求方式(GET/POST) 118 httpUrlConn.setRequestMethod(requestMethod); 119 120 if ("GET".equalsIgnoreCase(requestMethod)) 121 httpUrlConn.connect(); 122 123 // 當有數據須要提交時 124 if (null != outputStr) { 125 OutputStream outputStream = httpUrlConn.getOutputStream(); 126 // 注意編碼格式,防止中文亂碼 127 outputStream.write(outputStr.getBytes("UTF-8")); 128 outputStream.close(); 129 } 130 131 // 將返回的輸入流轉換成字符串 132 InputStream inputStream = httpUrlConn.getInputStream(); 133 InputStreamReader inputStreamReader = new InputStreamReader(inputStream, "utf-8"); 134 BufferedReader bufferedReader = new BufferedReader(inputStreamReader); 135 136 String str = null; 137 while ((str = bufferedReader.readLine()) != null) { 138 buffer.append(str); 139 } 140 bufferedReader.close(); 141 inputStreamReader.close(); 142 // 釋放資源 143 inputStream.close(); 144 inputStream = null; 145 httpUrlConn.disconnect(); 146 log.debug("https buffer:" + buffer.toString()); 147 } catch (ConnectException ce) { 148 log.error("Weixin server connection timed out."); 149 } catch (Exception e) { 150 log.error("https request error:{}", e); 151 } 152 return buffer.toString(); 153 } 154 }
【MyX509TrustManager】主要繼承X509TrustManager作https證書信任管理器
1 /** 2 * 證書信任管理器(用於https請求) 3 * 4 * @author 5 * 6 */ 7 public class MyX509TrustManager implements X509TrustManager { 8 9 public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException { 10 } 11 12 public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException { 13 } 14 15 public X509Certificate[] getAcceptedIssuers() { 16 return null; 17 } 18 }
以後從新打包一個新的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
以後查看日誌信息,便能發現確實可以獲取到用戶的信息了