網上搜資料時,網友都說官方文檔太垃圾了不易看懂,如何如何的。如今我的整理了一個通俗易懂易上手的,但願能夠幫助到剛接觸微信接口的你。php
請看流程圖!看懂圖,就懂了一半了:java
其實總體流程大致只需三步:用戶點擊登陸按鈕(其實就至關於一個連接) ---》 用戶點擊受權登陸 ----》 實現獲取用戶信息代碼。web
而後獲取用戶信息代碼只需三步:獲取code ----》 經過code獲取access_token和openId ---》 經過access_token和openId獲取用戶信息(包含union)。spring
以上即是總體套路,固然官網上也有,但具體如何實現呢?apache
不着急,我們一步一步來!json
第一步:微信登陸按鈕api
它其實就是一個鏈接,不過想獲得這個連接,有一點點麻煩。微信
一、設置。 微信公衆平臺---》接口權限---》網頁受權---》修改 ---》設置網頁受權域名(域名,不含http://),其實就是微信調你的java方法的項目路徑或項目域名,如:www.zzff.net/pp ---》點擊設置後彈出頁面(大體意思,將MP_verify_31qRIDcjN8ZD1lVJ.txt放在你項目路徑下面,如:www.ffzz.net/pp/MP_verify_31qRIDcjN8ZD1lVJ.txt 能訪問到) ---》點擊確認,受權回調頁面域名設置成功!網絡
二、拼連接。 https://open.weixin.qq.com/connect/oauth2/authorize?appid=xxx公衆號IDxxxxx & redirect_uri = 受權回調頁面域名/你的action(即微信受權後跳向的地址)app
& response_type=code(固定的) & scope = snsapi_userinfo(或者snsapi_base默認受權) & state=STATE#wechat_redirect
如:https://open.weixin.qq.com/connect/oauth2/authorize?appid=wxf0e81c3bee622d60&redirect_uri=http%3A%2F%2Fnba.bluewebgame.com%2Foauth_response.php&response_type=code&scope=snsapi_userinfo&state=STATE#wechat_redirect
這個連接中參數的具體含義,官方解釋以下:
參數 | 是否必須 | 說明 |
---|---|---|
appid | 是 | 公衆號的惟一標識 |
redirect_uri | 是 | 受權後重定向的回調連接地址,請使用urlencode對連接進行處理 |
response_type | 是 | 返回類型,請填寫code |
scope | 是 | 應用受權做用域,snsapi_base (不彈出受權頁面,直接跳轉,只能獲取用戶openid),snsapi_userinfo (彈出受權頁面,可經過openid拿到暱稱、性別、所在地。而且,即便在未關注的狀況下,只要用戶受權,也能獲取其信息) |
state | 否 | 重定向後會帶上state參數,開發者能夠填寫a-zA-Z0-9的參數值,最多128字節 |
#wechat_redirect | 是 | 不管直接打開仍是作頁面302重定向時候,必須帶此參數 |
第二步:受權確認登陸
這一步最簡單,第一步登陸連接拼好後,在手機微信中打開,微信便會跳轉到確認受權頁面,點擊確認受權便可。(這一步,不用開發者作處理!)
第三步:獲取用戶信息 (重點)
這一步即是真正的代碼實現的地方。開篇便講了,它只需三步:獲取code ----》 經過code獲取access_token和openId ---》 經過access_token和openId獲取用戶信息。
First: 獲取code
Second: 獲取網頁受權access_token和openId
Third:經過access_token和openId獲取用戶信息。
關於union機制的細節:若是開發者須要公衆號微信登陸和APP微信登陸共用一個微信ID,那個就須要union機制了。其實很簡單,只需在微信開放平臺(open.weixin.qq.com)綁定公衆號,獲取用戶信息時就會返回union字段。
具體代碼實現爲:實體 ---- 方法 --- 工具
實體Oauth2Token:
1 package com.wfcm.wxUitls; 2 3 /** 4 * 類名: WeixinOauth2Token </br> 5 * 描述: 網頁受權信息 </br> 6 * 建立時間: 2015-11-27 </br> 7 * 發佈版本:V1.0 </br> 8 */ 9 public class Oauth2Token { 10 // 網頁受權接口調用憑證 11 private String accessToken; 12 // 憑證有效時長 13 private int expiresIn; 14 // 用於刷新憑證 15 private String refreshToken; 16 // 用戶標識 17 private String openId; 18 // 用戶受權做用域 19 private String scope; 20 21 public String getAccessToken() { 22 return accessToken; 23 } 24 25 public void setAccessToken(String accessToken) { 26 this.accessToken = accessToken; 27 } 28 29 public int getExpiresIn() { 30 return expiresIn; 31 } 32 33 public void setExpiresIn(int expiresIn) { 34 this.expiresIn = expiresIn; 35 } 36 37 public String getRefreshToken() { 38 return refreshToken; 39 } 40 41 public void setRefreshToken(String refreshToken) { 42 this.refreshToken = refreshToken; 43 } 44 45 public String getOpenId() { 46 return openId; 47 } 48 49 public void setOpenId(String openId) { 50 this.openId = openId; 51 } 52 53 public String getScope() { 54 return scope; 55 } 56 57 public void setScope(String scope) { 58 this.scope = scope; 59 } 60 }
實體SNSUserInfo:
1 package com.wfcm.wxUitls; 2 3 import java.util.List; 4 5 /** 6 * 類名: SNSUserInfo </br> 7 * 描述: 經過網頁受權獲取的用戶信息 </br> 8 * 開發人員: wzf </br> 9 * 建立時間: 2015-11-27 </br> 10 * 發佈版本:V1.0 </br> 11 */ 12 public class SNSUserInfo { 13 // 用戶標識 14 private String openId; 15 // 用戶暱稱 16 private String nickname; 17 // 性別(1是男性,2是女性,0是未知) 18 private int sex; 19 // 國家 20 private String country; 21 // 省份 22 private String province; 23 // 城市 24 private String city; 25 // 用戶頭像連接 26 private String headImgUrl; 27 // 用戶特權信息 28 private List<String> privilegeList; 29 30 private String unionid; 31 32 public String getUnionid() { 33 return unionid; 34 } 35 36 public void setUnionid(String unionid) { 37 this.unionid = unionid; 38 } 39 40 public String getOpenId() { 41 return openId; 42 } 43 44 public void setOpenId(String openId) { 45 this.openId = openId; 46 } 47 48 public String getNickname() { 49 return nickname; 50 } 51 52 public void setNickname(String nickname) { 53 this.nickname = nickname; 54 } 55 56 public int getSex() { 57 return sex; 58 } 59 60 public void setSex(int sex) { 61 this.sex = sex; 62 } 63 64 public String getCountry() { 65 return country; 66 } 67 68 public void setCountry(String country) { 69 this.country = country; 70 } 71 72 public String getProvince() { 73 return province; 74 } 75 76 public void setProvince(String province) { 77 this.province = province; 78 } 79 80 public String getCity() { 81 return city; 82 } 83 84 public void setCity(String city) { 85 this.city = city; 86 } 87 88 public String getHeadImgUrl() { 89 return headImgUrl; 90 } 91 92 public void setHeadImgUrl(String headImgUrl) { 93 this.headImgUrl = headImgUrl; 94 } 95 96 public List<String> getPrivilegeList() { 97 return privilegeList; 98 } 99 100 public void setPrivilegeList(List<String> privilegeList) { 101 this.privilegeList = privilegeList; 102 } 103 }
方法WxController(其中authorize() 方法就是請求第一步獲取的連接):
1 package com.wfcm.controller; 2 3 import java.io.BufferedReader; 4 import java.io.IOException; 5 import java.io.InputStreamReader; 6 import java.io.UnsupportedEncodingException; 7 import java.net.URL; 8 import java.net.URLConnection; 9 import java.net.URLEncoder; 10 import java.security.MessageDigest; 11 import java.security.NoSuchAlgorithmException; 12 import java.util.ArrayList; 13 import java.util.Formatter; 14 import java.util.HashMap; 15 import java.util.List; 16 import java.util.Map; 17 import java.util.UUID; 18 19 import javax.servlet.http.HttpServletRequest; 20 import javax.servlet.http.HttpServletResponse; 21 22 import org.apache.http.client.utils.URLEncodedUtils; 23 import org.json.JSONObject; 24 import org.slf4j.Logger; 25 import org.slf4j.LoggerFactory; 26 import org.springframework.beans.factory.annotation.Autowired; 27 import org.springframework.stereotype.Controller; 28 import org.springframework.web.bind.annotation.RequestMapping; 29 import org.springframework.web.bind.annotation.ResponseBody; 30 31 import com.alibaba.fastjson.JSON; 32 import com.alibaba.fastjson.JSONArray; 33 import com.alibaba.fastjson.util.IOUtils; 34 import com.wfcm.annotation.IgnoreSign; 35 import com.wfcm.annotation.IgnoreToken; 36 import com.wfcm.entity.WfMemberEntity; 37 import com.wfcm.service.WfMemberService; 38 import com.wfcm.service.WfMemberSessionService; 39 import com.wfcm.utils.FastJSONUtils; 40 import com.wfcm.utils.NetUtil; 41 import com.wfcm.utils.R; 42 import com.wfcm.wxUitls.AccessToken; 43 import com.wfcm.wxUitls.JsapiTicket; 44 import com.wfcm.wxUitls.Oauth2Token; 45 import com.wfcm.wxUitls.SNSUserInfo; 46 47 @Controller 48 @RequestMapping("/wx") 49 @ResponseBody 50 public class WxController { 51 52 private static Logger log = LoggerFactory.getLogger(WxController.class); 53 54 55 /** 56 * 向指定URL發送GET方法的請求 57 * 58 * @param url 59 * 發送請求的URL 60 * @param param 61 * 請求參數,請求參數應該是 name1=value1&name2=value2 的形式。 62 * @return URL 所表明遠程資源的響應結果 63 * 64 * 用戶贊成受權,獲取code 65 */ 66 @RequestMapping("/authorize") 67 @ResponseBody 68 @IgnoreToken 69 public static R authorize() { 70 String appid = "wxbb000000000e"; 71 //String uri ="wftest.zzff.net/wx/weixinLogin"; 72 String uri = urlEncodeUTF8("wftest.zzff.net/api/wx/weixinLogin"); 73 String result = ""; 74 BufferedReader in = null; 75 try { 76 String urlNameString = "https://open.weixin.qq.com/connect/oauth2/authorize?appid="+appid+"&redirect_uri="+uri+"&response_type=code&scope=snsapi_userinfo&state=STATE#wechat_redirect"; 77 78 URL realUrl = new URL(urlNameString); 79 // 打開和URL之間的鏈接 80 URLConnection connection = realUrl.openConnection(); 81 // 設置通用的請求屬性 82 connection.setRequestProperty("accept", "*/*"); 83 connection.setRequestProperty("connection", "Keep-Alive"); 84 connection.setRequestProperty("user-agent", 85 "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)"); 86 // 創建實際的鏈接 87 connection.connect(); 88 // 獲取全部響應頭字段 89 Map<String, List<String>> map = connection.getHeaderFields(); 90 // 遍歷全部的響應頭字段 91 for (String key : map.keySet()) { 92 System.out.println(key + "--->" + map.get(key)); 93 } 94 // 定義 BufferedReader輸入流來讀取URL的響應 95 in = new BufferedReader(new InputStreamReader( 96 connection.getInputStream())); 97 String line =null; 98 while ((line = in.readLine()) != null) { 99 result += line; 100 } 101 /* com.alibaba.fastjson.JSONObject jsonObj= FastJSONUtils.getJSONObject(result); 102 String access_token = jsonObj.getString("access_token"); 103 long expires_in = Long.valueOf(jsonObj.getString("expires_in")); 104 */ 105 } catch (Exception e) { 106 System.out.println("發送GET請求出現異常!" + e); 107 e.printStackTrace(); 108 } 109 // 使用finally塊來關閉輸入流 110 finally { 111 try { 112 if (in != null) { 113 in.close(); 114 } 115 } catch (Exception e2) { 116 e2.printStackTrace(); 117 } 118 } 119 return R.ok(result); 120 } 121 122 @RequestMapping("/weixinLogin") 123 @ResponseBody 124 @IgnoreToken 125 @IgnoreSign 126 public void weixinLogin(HttpServletRequest request,HttpServletResponse response) throws Exception { 127 // 用戶贊成受權後,能獲取到code 128 Map<String, String[]> params = request.getParameterMap();//針對get獲取get參數 129 String[] codes = params.get("code");//拿到code的值 130 String code = codes[0];//code 131 //String[] states = params.get("state"); 132 //String state = states[0];//state 133 134 System.out.println("****************code:"+code); 135 // 用戶贊成受權 136 if (!"authdeny".equals(code)) { 137 // 獲取網頁受權access_token 138 Oauth2Token oauth2Token = getOauth2AccessToken("wxb0000000000e", "4c22222233333335555a9", code); 139 System.out.println("***********************************oauth2Token信息:"+oauth2Token.toString()); 140 // 網頁受權接口訪問憑證 141 String accessToken = oauth2Token.getAccessToken(); 142 // 用戶標識 143 String openId = oauth2Token.getOpenId(); 144 // 獲取用戶信息 145 SNSUserInfo snsUserInfo = getSNSUserInfo(accessToken, openId); 146 System.out.println("***********************************用戶信息unionId:"+snsUserInfo.getUnionid()+"***:"+snsUserInfo.getNickname()); 147 // 設置要傳遞的參數 148 149 //具體業務start 150 151 //具體業務end 152 153 String url = "http://wftest.zzff.net/#/biddd?from=login&tokenId="+snsUserInfo.getUnionid(); 154 155 response.sendRedirect(url); 156 return ; 157 } 158 } 159 160 161 /** 162 * 獲取網頁受權憑證 163 * 164 * @param appId 公衆帳號的惟一標識 165 * @param appSecret 公衆帳號的密鑰 166 * @param code 167 * @return WeixinAouth2Token 168 */ 169 public static Oauth2Token getOauth2AccessToken(String appId, String appSecret, String code) { 170 Oauth2Token wat = null; 171 // 拼接請求地址 172 String requestUrl = "https://api.weixin.qq.com/sns/oauth2/access_token?appid=APPID&secret=SECRET&code=CODE&grant_type=authorization_code"; 173 requestUrl = requestUrl.replace("APPID", appId); 174 requestUrl = requestUrl.replace("SECRET", appSecret); 175 requestUrl = requestUrl.replace("CODE", code); 176 // 獲取網頁受權憑證 177 com.alibaba.fastjson.JSONObject jsonObject = JSON.parseObject(NetUtil.get(requestUrl)); 178 if (null != jsonObject) { 179 try { 180 wat = new Oauth2Token(); 181 wat.setAccessToken(jsonObject.getString("access_token")); 182 wat.setExpiresIn(jsonObject.getInteger("expires_in")); 183 wat.setRefreshToken(jsonObject.getString("refresh_token")); 184 wat.setOpenId(jsonObject.getString("openid")); 185 wat.setScope(jsonObject.getString("scope")); 186 } catch (Exception e) { 187 wat = null; 188 int errorCode = jsonObject.getInteger("errcode"); 189 String errorMsg = jsonObject.getString("errmsg"); 190 log.error("獲取網頁受權憑證失敗 errcode:{} errmsg:{}", errorCode, errorMsg); 191 } 192 } 193 return wat; 194 } 195 196 /** 197 * 經過網頁受權獲取用戶信息 198 * 199 * @param accessToken 網頁受權接口調用憑證 200 * @param openId 用戶標識 201 * @return SNSUserInfo 202 */ 203 public static SNSUserInfo getSNSUserInfo(String accessToken, String openId) { 204 SNSUserInfo snsUserInfo = null; 205 // 拼接請求地址 206 String requestUrl = "https://api.weixin.qq.com/sns/userinfo?access_token=ACCESS_TOKEN&openid=OPENID"; 207 requestUrl = requestUrl.replace("ACCESS_TOKEN", accessToken).replace("OPENID", openId); 208 // 經過網頁受權獲取用戶信息 209 com.alibaba.fastjson.JSONObject jsonObject = JSON.parseObject(NetUtil.get(requestUrl)); 210 211 if (null != jsonObject) { 212 try { 213 snsUserInfo = new SNSUserInfo(); 214 // 用戶的標識 215 snsUserInfo.setOpenId(jsonObject.getString("openid")); 216 // 暱稱 217 snsUserInfo.setNickname(jsonObject.getString("nickname")); 218 // 性別(1是男性,2是女性,0是未知) 219 snsUserInfo.setSex(jsonObject.getInteger("sex")); 220 // 用戶所在國家 221 snsUserInfo.setCountry(jsonObject.getString("country")); 222 // 用戶所在省份 223 snsUserInfo.setProvince(jsonObject.getString("province")); 224 // 用戶所在城市 225 snsUserInfo.setCity(jsonObject.getString("city")); 226 // 用戶頭像 227 snsUserInfo.setHeadImgUrl(jsonObject.getString("headimgurl")); 228 // 用戶特權信息 229 List<String> list = JSON.parseArray(jsonObject.getString("privilege"),String.class); 230 snsUserInfo.setPrivilegeList(list); 231 //與開放平臺共用的惟一標識,只有在用戶將公衆號綁定到微信開放平臺賬號後,纔會出現該字段。 232 snsUserInfo.setUnionid(jsonObject.getString("unionid")); 233 } catch (Exception e) { 234 snsUserInfo = null; 235 int errorCode = jsonObject.getInteger("errcode"); 236 String errorMsg = jsonObject.getString("errmsg"); 237 log.error("獲取用戶信息失敗 errcode:{} errmsg:{}", errorCode, errorMsg); 238 } 239 } 240 return snsUserInfo; 241 } 242 243 /** 244 * URL編碼(utf-8) 245 * 246 * @param source 247 * @return 248 */ 249 public static String urlEncodeUTF8(String source) { 250 String result = source; 251 try { 252 result = java.net.URLEncoder.encode(source, "utf-8"); 253 } catch (UnsupportedEncodingException e) { 254 e.printStackTrace(); 255 } 256 return result; 257 } 258 259 private static String byteToHex(final byte[] hash) { 260 Formatter formatter = new Formatter(); 261 for (byte b : hash) 262 { 263 formatter.format("%02x", b); 264 } 265 String result = formatter.toString(); 266 formatter.close(); 267 return result; 268 } 269 270 private static String create_nonce_str() { 271 return UUID.randomUUID().toString(); 272 } 273 274 private static String create_timestamp() { 275 return Long.toString(System.currentTimeMillis() / 1000); 276 } 277 }
工具NetUtil:
1 package com.wfcm.utils; 2 3 import java.io.BufferedInputStream; 4 import java.io.IOException; 5 import java.io.InputStreamReader; 6 import java.io.UnsupportedEncodingException; 7 import java.util.ArrayList; 8 import java.util.Collection; 9 import java.util.List; 10 import java.util.Map; 11 12 import org.apache.commons.httpclient.NameValuePair; 13 import org.apache.http.HttpEntity; 14 import org.apache.http.HttpResponse; 15 import org.apache.http.client.entity.UrlEncodedFormEntity; 16 import org.apache.http.client.methods.HttpGet; 17 import org.apache.http.client.methods.HttpPost; 18 import org.apache.http.impl.client.CloseableHttpClient; 19 import org.apache.http.impl.client.HttpClientBuilder; 20 import org.apache.http.message.BasicNameValuePair; 21 22 /** 23 * Created by Song on 2016/11/28. 24 * 基於HttpClient提供網絡訪問工具 25 */ 26 public final class NetUtil { 27 public static CloseableHttpClient httpClient = HttpClientBuilder.create().build(); 28 29 /** 30 * get請求獲取String類型數據 31 * @param url 請求連接 32 * @return 33 */ 34 public static String get(String url){ 35 StringBuffer sb = new StringBuffer(); 36 HttpGet httpGet = new HttpGet(url); 37 try { 38 HttpResponse response = httpClient.execute(httpGet); //1 39 40 HttpEntity entity = response.getEntity(); 41 InputStreamReader reader = new InputStreamReader(entity.getContent(),"utf-8"); 42 char [] charbufer; 43 while (0<reader.read(charbufer=new char[10])){ 44 sb.append(charbufer); 45 } 46 }catch (IOException e){//1 47 e.printStackTrace(); 48 }finally { 49 httpGet.releaseConnection(); 50 } 51 return sb.toString(); 52 } 53 54 /** 55 * post方式請求數據 56 * @param url 請求連接 57 * @param data post數據體 58 * @return 59 */ 60 @SuppressWarnings("unchecked") 61 public static String post(String url, Map<String,String> data){ 62 StringBuffer sb = new StringBuffer(); 63 HttpPost httpPost = new HttpPost(url); 64 List<NameValuePair> valuePairs = new ArrayList<NameValuePair>(); 65 if(null != data) { 66 for (String key : data.keySet()) { 67 valuePairs.addAll((Collection<? extends NameValuePair>) new BasicNameValuePair(key, data.get(key))); 68 } 69 } 70 try { 71 httpPost.setEntity(new UrlEncodedFormEntity((List<? extends org.apache.http.NameValuePair>) valuePairs)); 72 HttpResponse response = httpClient.execute(httpPost); 73 HttpEntity httpEntity = response.getEntity(); 74 BufferedInputStream bis = new BufferedInputStream(httpEntity.getContent()); 75 byte [] buffer; 76 while (0<bis.read(buffer=new byte[128])){ 77 sb.append(new String(buffer,"utf-8")); 78 } 79 }catch (UnsupportedEncodingException e){//數據格式有誤 80 e.printStackTrace(); 81 }catch (IOException e){//請求出錯 82 e.printStackTrace(); 83 }finally { 84 httpPost.releaseConnection(); 85 } 86 return sb.toString(); 87 } 88 89 90 }
R類:
package com.wfcm.utils; import java.util.HashMap; import java.util.Map; /** * 返回數據 * * @author xlf * @email xlfbe696@gmail.com * @date 2017年4月19日 上午11:58:56 */ public class R extends HashMap<String, Object> { private static final long serialVersionUID = 1L; public static final String SUCCESS = "success"; public static final String EXCEPTION = "exception"; public static final Integer SUCCESSCODE = 0; public static final Integer EXCEPTIONCODE = 500; public R() { put("errCode", 0); put("msg", SUCCESS); } public R(int code, String msg){ put("errCode", code); put("msg", msg); } public static R error() { return error(500, "未知異常,請聯繫管理員"); } public static R error(String msg) { return error(500, msg); } public static R error(int code, String msg) { R r = new R(); r.put("errCode", code); r.put("msg", msg); return r; } public static R ok(String msg) { R r = new R(); r.put("msg", msg); return r; } public static R ok(Map<String, Object> map) { R r = new R(); r.putAll(map); return r; } public static R ok() { return new R(); } public R put(String key, Object value) { super.put(key, value); return this; } }
OK,大功告成!總體流程已經搭建起來,讀懂了這些代碼差很少就明白了整個流程了,而後再看官方文檔,你會以爲讀起來很順暢,而不是剛開始那種味同嚼蠟的感受。你只需再根據官方文檔仔細檢查檢查流程,有沒有須要完善的地方,就能夠了。
還等什麼呢,趕快敲實現功能吧!!!