網上搜資料時,網友都說官方文檔太垃圾了不易看懂,如何如何的。現在個人整理了一個通俗易懂易上手的,希望可以幫助到剛接觸微信接口的你。
請看流程圖!看懂圖,就懂了一半了:

其實整體流程大體只需三步:用戶點擊登錄按鈕(其實就相當於一個鏈接) ---》 用戶點擊授權登錄 ----》 實現獲取用戶信息代碼。
然后獲取用戶信息代碼只需三步:獲取code ----》 通過code獲取access_token和openId ---》 通過access_token和openId獲取用戶信息(包含union)。
以上便是整體套路,當然官網上也有,但具體如何實現呢?
不着急,咱們一步一步來!
第一步:微信登錄按鈕
它其實就是一個連接,不過想得到這個鏈接,有一點點麻煩。
1、設置。 微信公眾平台---》接口權限---》網頁授權---》修改 ---》設置網頁授權域名(域名,不含http://),其實就是微信調你的java方法的項目路徑或項目域名,如:www.zzff.net/pp ---》點擊設置后彈出頁面(大致意思,將MP_verify_31qRIDcjN8ZD1lVJ.txt放在你項目路徑下面,如:www.ffzz.net/pp/MP_verify_31qRIDcjN8ZD1lVJ.txt 能訪問到) ---》點擊確認,授權回調頁面域名設置成功!
2、拼鏈接。 https://open.weixin.qq.com/connect/oauth2/authorize?appid=xxx公眾號IDxxxxx & redirect_uri = 授權回調頁面域名/你的action(即微信授權后跳向的地址)
& 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:
package com.wfcm.wxUitls; /** * 類名: WeixinOauth2Token </br> * 描述: 網頁授權信息 </br> * 創建時間: 2015-11-27 </br> * 發布版本:V1.0 </br> */ public class Oauth2Token { // 網頁授權接口調用憑證 private String accessToken; // 憑證有效時長 private int expiresIn; // 用於刷新憑證 private String refreshToken; // 用戶標識 private String openId; // 用戶授權作用域 private String scope; public String getAccessToken() { return accessToken; } public void setAccessToken(String accessToken) { this.accessToken = accessToken; } public int getExpiresIn() { return expiresIn; } public void setExpiresIn(int 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; } } |
實體SNSUserInfo:
package com.wfcm.wxUitls; import java.util.List; /** * 類名: SNSUserInfo </br> * 描述: 通過網頁授權獲取的用戶信息 </br> * 開發人員: wzf </br> * 創建時間: 2015-11-27 </br> * 發布版本:V1.0 </br> */ public class SNSUserInfo { // 用戶標識 private String openId; // 用戶昵稱 private String nickname; // 性別(1是男性,2是女性,0是未知) private int sex; // 國家 private String country; // 省份 private String province; // 城市 private String city; // 用戶頭像鏈接 private String headImgUrl; // 用戶特權信息 private List<String> privilegeList; private String unionid; public String getUnionid() { return unionid; } public void setUnionid(String unionid) { this.unionid = unionid; } 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 getCountry() { return country; } public void setCountry(String country) { this.country = country; } 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 getHeadImgUrl() { return headImgUrl; } public void setHeadImgUrl(String headImgUrl) { this.headImgUrl = headImgUrl; } public List<String> getPrivilegeList() { return privilegeList; } public void setPrivilegeList(List<String> privilegeList) { this.privilegeList = privilegeList; } } |
方法WxController(其中authorize() 方法就是請求第一步獲取的鏈接):
package com.wfcm.controller; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.io.UnsupportedEncodingException; import java.net.URL; import java.net.URLConnection; import java.net.URLEncoder; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.util.ArrayList; import java.util.Formatter; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.UUID; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.apache.http.client.utils.URLEncodedUtils; import org.json.JSONObject; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.ResponseBody; import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONArray; import com.alibaba.fastjson.util.IOUtils; import com.wfcm.annotation.IgnoreSign; import com.wfcm.annotation.IgnoreToken; import com.wfcm.entity.WfMemberEntity; import com.wfcm.service.WfMemberService; import com.wfcm.service.WfMemberSessionService; import com.wfcm.utils.FastJSONUtils; import com.wfcm.utils.NetUtil; import com.wfcm.utils.R; import com.wfcm.wxUitls.AccessToken; import com.wfcm.wxUitls.JsapiTicket; import com.wfcm.wxUitls.Oauth2Token; import com.wfcm.wxUitls.SNSUserInfo; @Controller @RequestMapping("/wx") @ResponseBody public class WxController { private static Logger log = LoggerFactory.getLogger(WxController.class); /** * 向指定URL發送GET方法的請求 * * @param url * 發送請求的URL * @param param * 請求參數,請求參數應該是 name1=value1&name2=value2 的形式。 * @return URL 所代表遠程資源的響應結果 * * 用戶同意授權,獲取code */ @RequestMapping("/authorize") @ResponseBody @IgnoreToken public static R authorize() { String appid = "wxbb000000000e"; //String uri ="wftest.zzff.net/wx/weixinLogin"; String uri = urlEncodeUTF8("wftest.zzff.net/api/wx/weixinLogin"); String result = ""; BufferedReader in = null; try { 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"; URL realUrl = new URL(urlNameString); // 打開和URL之間的連接 URLConnection connection = realUrl.openConnection(); // 設置通用的請求屬性 connection.setRequestProperty("accept", "*/*"); connection.setRequestProperty("connection", "Keep-Alive"); connection.setRequestProperty("user-agent", "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)"); // 建立實際的連接 connection.connect(); // 獲取所有響應頭字段 Map<String, List<String>> map = connection.getHeaderFields(); // 遍歷所有的響應頭字段 for (String key : map.keySet()) { System.out.println(key + "--->" + map.get(key)); } // 定義 BufferedReader輸入流來讀取URL的響應 in = new BufferedReader(new InputStreamReader( connection.getInputStream())); String line =null; while ((line = in.readLine()) != null) { result += line; } /* com.alibaba.fastjson.JSONObject jsonObj= FastJSONUtils.getJSONObject(result); String access_token = jsonObj.getString("access_token"); long expires_in = Long.valueOf(jsonObj.getString("expires_in")); */ } catch (Exception e) { System.out.println("發送GET請求出現異常!" + e); e.printStackTrace(); } // 使用finally塊來關閉輸入流 finally { try { if (in != null) { in.close(); } } catch (Exception e2) { e2.printStackTrace(); } } return R.ok(result); } @RequestMapping("/weixinLogin") @ResponseBody @IgnoreToken @IgnoreSign public void weixinLogin(HttpServletRequest request,HttpServletResponse response) throws Exception { // 用戶同意授權后,能獲取到code Map<String, String[]> params = request.getParameterMap();//針對get獲取get參數 String[] codes = params.get("code");//拿到code的值 String code = codes[0];//code //String[] states = params.get("state"); //String state = states[0];//state System.out.println("****************code:"+code); // 用戶同意授權 if (!"authdeny".equals(code)) { // 獲取網頁授權access_token Oauth2Token oauth2Token = getOauth2AccessToken("wxb0000000000e", "4c22222233333335555a9", code); System.out.println("***********************************oauth2Token信息:"+oauth2Token.toString()); // 網頁授權接口訪問憑證 String accessToken = oauth2Token.getAccessToken(); // 用戶標識 String openId = oauth2Token.getOpenId(); // 獲取用戶信息 SNSUserInfo snsUserInfo = getSNSUserInfo(accessToken, openId); System.out.println("***********************************用戶信息unionId:"+snsUserInfo.getUnionid()+"***:"+snsUserInfo.getNickname()); // 設置要傳遞的參數 //具體業務start //具體業務end String url = "http://wftest.zzff.net/#/biddd?from=login&tokenId="+snsUserInfo.getUnionid(); response.sendRedirect(url); return ; } } /** * 獲取網頁授權憑證 * * @param appId 公眾賬號的唯一標識 * @param appSecret 公眾賬號的密鑰 * @param code * @return WeixinAouth2Token */ public static Oauth2Token getOauth2AccessToken(String appId, String appSecret, String code) { Oauth2Token wat = null; // 拼接請求地址 String requestUrl = "https://api.weixin.qq.com/sns/oauth2/access_token?appid=APPID&secret=SECRET&code=CODE&grant_type=authorization_code"; requestUrl = requestUrl.replace("APPID", appId); requestUrl = requestUrl.replace("SECRET", appSecret); requestUrl = requestUrl.replace("CODE", code); // 獲取網頁授權憑證 com.alibaba.fastjson.JSONObject jsonObject = JSON.parseObject(NetUtil.get(requestUrl)); if (null != jsonObject) { try { wat = new Oauth2Token(); wat.setAccessToken(jsonObject.getString("access_token")); wat.setExpiresIn(jsonObject.getInteger("expires_in")); wat.setRefreshToken(jsonObject.getString("refresh_token")); wat.setOpenId(jsonObject.getString("openid")); wat.setScope(jsonObject.getString("scope")); } catch (Exception e) { wat = null; int errorCode = jsonObject.getInteger("errcode"); String errorMsg = jsonObject.getString("errmsg"); log.error("獲取網頁授權憑證失敗 errcode:{} errmsg:{}", errorCode, errorMsg); } } return wat; } /** * 通過網頁授權獲取用戶信息 * * @param accessToken 網頁授權接口調用憑證 * @param openId 用戶標識 * @return SNSUserInfo */ public static SNSUserInfo getSNSUserInfo(String accessToken, String openId) { SNSUserInfo snsUserInfo = null; // 拼接請求地址 String requestUrl = "https://api.weixin.qq.com/sns/userinfo?access_token=ACCESS_TOKEN&openid=OPENID"; requestUrl = requestUrl.replace("ACCESS_TOKEN", accessToken).replace("OPENID", openId); // 通過網頁授權獲取用戶信息 com.alibaba.fastjson.JSONObject jsonObject = JSON.parseObject(NetUtil.get(requestUrl)); if (null != jsonObject) { try { snsUserInfo = new SNSUserInfo(); // 用戶的標識 snsUserInfo.setOpenId(jsonObject.getString("openid")); // 昵稱 snsUserInfo.setNickname(jsonObject.getString("nickname")); // 性別(1是男性,2是女性,0是未知) snsUserInfo.setSex(jsonObject.getInteger("sex")); // 用戶所在國家 snsUserInfo.setCountry(jsonObject.getString("country")); // 用戶所在省份 snsUserInfo.setProvince(jsonObject.getString("province")); // 用戶所在城市 snsUserInfo.setCity(jsonObject.getString("city")); // 用戶頭像 snsUserInfo.setHeadImgUrl(jsonObject.getString("headimgurl")); // 用戶特權信息 List<String> list = JSON.parseArray(jsonObject.getString("privilege"),String.class); snsUserInfo.setPrivilegeList(list); //與開放平台共用的唯一標識,只有在用戶將公眾號綁定到微信開放平台帳號后,才會出現該字段。 snsUserInfo.setUnionid(jsonObject.getString("unionid")); } catch (Exception e) { snsUserInfo = null; int errorCode = jsonObject.getInteger("errcode"); String errorMsg = jsonObject.getString("errmsg"); log.error("獲取用戶信息失敗 errcode:{} errmsg:{}", errorCode, errorMsg); } } return snsUserInfo; } /** * URL編碼(utf-8) * * @param source * @return */ public static String urlEncodeUTF8(String source) { String result = source; try { result = java.net.URLEncoder.encode(source, "utf-8"); } catch (UnsupportedEncodingException e) { e.printStackTrace(); } return result; } private static String byteToHex(final byte[] hash) { Formatter formatter = new Formatter(); for (byte b : hash) { formatter.format("%02x", b); } String result = formatter.toString(); formatter.close(); return result; } private static String create_nonce_str() { return UUID.randomUUID().toString(); } private static String create_timestamp() { return Long.toString(System.currentTimeMillis() / 1000); } } |
工具NetUtil:
package com.wfcm.utils; import java.io.BufferedInputStream; import java.io.IOException; import java.io.InputStreamReader; import java.io.UnsupportedEncodingException; import java.util.ArrayList; import java.util.Collection; import java.util.List; import java.util.Map; import org.apache.commons.httpclient.NameValuePair; import org.apache.http.HttpEntity; import org.apache.http.HttpResponse; import org.apache.http.client.entity.UrlEncodedFormEntity; import org.apache.http.client.methods.HttpGet; import org.apache.http.client.methods.HttpPost; import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClientBuilder; import org.apache.http.message.BasicNameValuePair; /** * Created by Song on 2016/11/28. * 基於HttpClient提供網絡訪問工具 */ public final class NetUtil { public static CloseableHttpClient httpClient = HttpClientBuilder.create().build(); /** * get請求獲取String類型數據 * @param url 請求鏈接 * @return */ public static String get(String url){ StringBuffer sb = new StringBuffer(); HttpGet httpGet = new HttpGet(url); try { HttpResponse response = httpClient.execute(httpGet); //1 HttpEntity entity = response.getEntity(); InputStreamReader reader = new InputStreamReader(entity.getContent(),"utf-8"); char [] charbufer; while (0<reader.read(charbufer=new char[10])){ sb.append(charbufer); } }catch (IOException e){//1 e.printStackTrace(); }finally { httpGet.releaseConnection(); } return sb.toString(); } /** * post方式請求數據 * @param url 請求鏈接 * @param data post數據體 * @return */ @SuppressWarnings("unchecked") public static String post(String url, Map<String,String> data){ StringBuffer sb = new StringBuffer(); HttpPost httpPost = new HttpPost(url); List<NameValuePair> valuePairs = new ArrayList<NameValuePair>(); if(null != data) { for (String key : data.keySet()) { valuePairs.addAll((Collection<? extends NameValuePair>) new BasicNameValuePair(key, data.get(key))); } } try { httpPost.setEntity(new UrlEncodedFormEntity((List<? extends org.apache.http.NameValuePair>) valuePairs)); HttpResponse response = httpClient.execute(httpPost); HttpEntity httpEntity = response.getEntity(); BufferedInputStream bis = new BufferedInputStream(httpEntity.getContent()); byte [] buffer; while (0<bis.read(buffer=new byte[128])){ sb.append(new String(buffer,"utf-8")); } }catch (UnsupportedEncodingException e){//數據格式有誤 e.printStackTrace(); }catch (IOException e){//請求出錯 e.printStackTrace(); }finally { httpPost.releaseConnection(); } return sb.toString(); } } |
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,大功告成!整體流程已經搭建起來,讀懂了這些代碼差不多就明白了整個流程了,然后再看官方文檔,你會覺得讀起來很順暢,而不是剛開始那種味同嚼蠟的感覺。你只需再根據官方文檔仔細檢查檢查流程,有沒有需要完善的地方,就可以了。
還等什么呢,趕快敲實現功能吧!!!