又有一段時間沒動筆了,確實工做後忙碌起來了,年底了,給你們拜年了,抽了這個空檔把最近作的一個東西分享分享。html
微信公衆號,相信很多人已經有接觸,其開發分爲許多種,本次主要說的是,經過微信認證後的公衆號(下圖,),使用Oauth網頁接口來獲取用戶信息的流程。
java
1、基礎準備web
首先,明確需求:有一個應用服務器,但願經過某個連接(菜單)進入此應用,在應用服務器端能夠得到訪問者的一些信息。json
微信是不會直接讓你在用戶在訪問應用服務器時直接取到用戶信息的,而須要經過他們的專門的認證服務器來操做。做爲開發者,目前有兩類獲取用戶信息的方法,這裏只介紹Oauth 2.0認證(其它還有一種CGI接口,是必須加了微信公衆號關注後纔可取到用戶信息,而使用Oauth則能夠不加關注也能取到),結合微信我整理了一個簡單的流程:api
一、用戶點開了某個菜單或者連接,進入到了咱們的後臺應用服務器(原始請求:URL_ORG);瀏覽器
二、服務器發現沒有用戶認證信息(微信所謂的認證code,使用一次後失效),因而返回了一個rediret頭給用戶,而且讓它去訪問微信的認證服務器;服務器
三、用戶的瀏覽器會去訪問微信的認證服務器(用戶會看到閃一下,這是重定向進行中);微信
https://open.weixin.qq.com/connect/oauth2/authorize?appid=APPID&redirect_uri=URL_ORG&response_type=code&scope=snsapi_userinfo&state=1#wechat_redirect
四、若是用戶受權(微信會詢問,是否受權,若是加了關注的用戶則不會詢問,默認受權),則微信認證服務器會返回一個redirect頭給用戶,同時包含了認證code,讓用戶瀏覽器去訪問原始請求的連接;session
五、這時用戶的瀏覽器會帶着code去訪問咱們的後臺應用(此時還會再閃一下,重定向:URL_ORG?code=xxxxxx);app
六、應用服務器拿到了code尚未用,還得由應用服務器去向微信認證服務器取token(ACCESS_TOKEN,一段時間內有效),在取得token時還會返回用戶的openId(OEPNID);
https://api.weixin.qq.com/sns/oauth2/access_token?appid=APPID&secret=SECRET&code=CODE&grant_type=authorization_code
七、在取到token、openId後,再發起一次請求,取得用戶的我的信息;
https://api.weixin.qq.com/sns/userinfo?access_token=ACCESS_TOKEN&openid=OPENID&lang=zh_CN
上面的過程,看起來很是複雜, 沒錯,有兩次重定向!可是好處在於,用戶訪問的原始連接確定是乾淨的,好比www.abc.com/xxx,微信那邊的連接或菜單在設置時不須要變更。若是你們願意去維護菜單的連接地址,也可使用微信官方文檔的方法,省去上面的第一、2步,直接把原始連接設置爲:微信認證接口地址+待回調原始地址,的模式,減小一次用戶可感知的閃爍。
2、核心代碼
轉java後,我也沒時間去寫成其它語言了,你們就看java代碼吧,第一個地方,對應用服務器需求使用用戶信息的url地址使用filter:
1 public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { 2 HttpServletRequest request = (HttpServletRequest) servletRequest; 3 HttpServletResponse response = (HttpServletResponse) servletResponse; 4 HttpSession session = request.getSession(); 5 if (request != null) { 6 // 嘗試公衆號 7 Object openId = session.getAttribute(WxSession.WX_OPENID_KEY); 8 // 未完成認證 9 if (openId == null) { 10 String code = request.getParameter("code"); 11 if (code != null) { 12 // 第二步,經過code換取access_token和OpenId 13 JSONObject ret = _authService.getAccessToken(code); 14 String token = ret.getString("access_token"); 15 openId = ret.getString("openid"); 16 // 第三步,拉取用戶信息 17 ret = _authService.getExternalUserInfo(token, openId.toString()); 18 String nickname = ret.getString("nickname"); 19 // 存session 20 session.setAttribute(WxSession.WX_OPENID_KEY, openId); 21 session.setAttribute(WxSession.WX_USER_NAME_KEY, nickname); 22 } else { 23 // 第一步,獲取code 24 response.sendRedirect(_authService.getExternalAuthUrl(request.getRequestURL().toString())); 25 return; 26 } 27 } 28 } 29 filterChain.doFilter(servletRequest, servletResponse); 30 }
上面的代碼有註釋,很是明白了,先看有沒有code,沒有code就重定向去取,取到後用戶再來;當有code時,服務器本身去取token和openid,取到後再取任何用戶信息(上面只取了用戶暱稱)。
第二個地方,上面代碼中的getExternalAuthUrl、getAccessToken、getExternalUserInfo三個方法:
1 /** 2 * 跳轉到OAuth登陸頁面,可有用戶的詳細信息 3 */ 4 public String getExternalAuthUrl(String redirectUri) { 5 String encodeUrl = null; 6 try { 7 encodeUrl = URLEncoder.encode(redirectUri, "UTF-8"); 8 } catch (UnsupportedEncodingException e) { 9 _log.error(e); 10 } 11 String url = "https://open.weixin.qq.com/connect/oauth2/authorize"; 12 url += "?appid=" + Constant.Subscribe.AppId; 13 url += "&redirect_uri=" + encodeUrl; 14 url += "&response_type=code"; 15 url += "&scope=snsapi_userinfo"; 16 url += "&state=123"; 17 url += "#wechat_redirect"; 18 return url; 19 } 20 21 /** 22 * 獲取OAuth的access_token,錯誤時返回null 23 */ 24 public JSONObject getAccessToken(String code) { 25 String url = "https://api.weixin.qq.com/sns/oauth2/access_token"; 26 url += "?appid=" + Constant.Subscribe.AppId; 27 url += "&secret=" + Constant.Subscribe.AppSecret; 28 url += "&code=" + code; 29 url += "&grant_type=authorization_code"; 30 try { 31 String responseStr = HttpInvork.getRequestWithoutHeader(url); 32 JSONObject json = JSONObject.fromObject(responseStr); 33 String access_token = json.getString("access_token"); 34 if (StringUtils.isNotEmpty(access_token)) { 35 return json; 36 } 37 } catch (Exception e) { 38 _log.error(e); 39 } 40 return null; 41 } 42 43 /** 44 * 獲取OAuth微信用戶信息,錯誤時返回null 45 */ 46 public JSONObject getExternalUserInfo(String accessToken, String openId) { 47 String url = "https://api.weixin.qq.com/sns/userinfo"; 48 url += "?access_token=" + accessToken; 49 url += "&openid=" + openId; 50 url += "&lang=zh_CN"; 51 String responseStr = HttpInvork.getRequestWithoutHeader(url); 52 try { 53 return JSONObject.fromObject(responseStr); 54 } catch (Exception e) { 55 _log.error(e); 56 } 57 return null; 58 }
第三個地方,上面的HttpInvork.getRequestWithoutHeader方法,我就不貼了,你們隨便寫/找個能夠發送get請求的方法代替一下吧。
3、福利?
你們不知足於Oauth?那我把CGI代碼也貼出來吧。先仍是說明一下:使用cgi方式時,openid仍是跟前面的相似,scope可改成snsapi_base(不改也無所謂啦,還有一種openid獲取方法前面沒有說,就是向公衆號發了消息,而後經過微信的公衆平臺的服務器配置綁定到了後臺應用,經過解析後也能夠取到openid),而後仍是要token才能要用戶信息,只是這裏就不能用Oauth的token了,而是普通的cgi token,代碼:
1 /** 2 * 獲取CGI的access_token,錯誤時返回null 3 */ 4 public String getCgiAccessToken() { 5 String url = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential"; 6 url += "&appid=" + Constant.Subscribe.AppId; 7 url += "&secret=" + Constant.Subscribe.AppSecret; 8 try { 9 String responseStr = HttpInvork.getRequestWithoutHeader(url); 10 JSONObject json = JSONObject.fromObject(responseStr); 11 String access_token = json.getString("access_token"); 12 if (StringUtils.isNotEmpty(access_token)) { 13 return access_token; 14 } 15 } catch (Exception e) { 16 _log.error(e); 17 } 18 return null; 19 } 20 21 /** 22 * 經過CGI獲取微信用戶信息,錯誤時返回null 23 */ 24 public JSONObject getCgiUserInfo(String accessToken, String openId) { 25 String url = "https://api.weixin.qq.com/cgi-bin/user/info"; 26 url += "?access_token=" + accessToken; 27 url += "&openid=" + openId; 28 url += "&lang=zh_CN"; 29 String responseStr = HttpInvork.getRequestWithoutHeader(url); 30 try { 31 return JSONObject.fromObject(responseStr); 32 } catch (Exception e) { 33 _log.error(e); 34 } 35 return null; 36 }
說了好多,我建議是,你們申請一個實戰演練演練,除了認證複雜了點,其它開發跟傳統的web開發沒啥太大區別了,微信JS-SDK都給你準備好了。過年快樂!