shop--12.微信測試號的申請和獲取微信用戶信息

在我們本身的程序裏面編寫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進行比對,從而驗證安全性)
數組

【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】主要用來獲取已關注此微信號的用戶信息並作相應處理瀏覽器

 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


以後查看日誌信息,便能發現確實可以獲取到用戶的信息了

相關文章
相關標籤/搜索