關于微信開發的話題,例子確實已經有不少,但大部分都是人云亦云,很多小細節或者需要注意的地方卻大多沒有講清楚,這令很多剛開始開發的人感覺大很迷茫。而我今天要說的話題,主要著眼于兩個方面。
一:如何存儲獲取用戶信息及調用第三方接口所需要的token.
二 : 第三方頁面授權,如何減少從微信服務器獲取用戶openid的次數以及減少獲取用戶信息的次數,加速第三方頁面的加載速速。
(注:演示所使用的是java語言,其他語言可與此類似)
下面我將開始講述第一個問題。
如何存儲獲取用戶信息及調用第三方接口所需要的token?
從微信的官方文檔上,我們知道,獲取token的次數為1天2000次,每兩小時token失效一次,對于那種一天沒有幾個人訪問的微信公眾號而言,他們可能只是簡單的每次調用高級接口都會去獲取一遍token.眾所周知,這種做法極其的耗費時間。從網上其他網友給出的存儲方案大概有如下幾種:
- 數據庫:通過微信接口獲取到 Token 之后,將 Token存儲到數據庫,每次需要時從數據庫取出。采用定時任務的方法每隔一個固定的時間去獲取一次token.
- NoSQl:這里以 Redis 為例子。通過微信接口獲取到 Token 之后,存入 Redis,可以通過設置redis的過期時間,每次需要token時從redis中取出來,若沒有,則證明Token 已過期可重新獲取(當然也可采用上面的定時任務的方式定期獲取)。
- 文件存儲:這個比較適合單一公眾號的情況。通過微信接口獲取到 Token 之后,存入文件,采用定時任務的方法每隔一個固定的時間去獲取一次token.
第三方頁面授權,如何減少從微信服務器獲取用戶openid的次數以及減少獲取用戶信息的次數,加速第三方頁面的加載速速?
從諸多的博客中,我們了解到,第三方頁面授權獲取用戶信息,我們要調用兩次微信接口。
- 第一次:構造應用授權的url,通過返回的code,換取用戶的openid.
- 第二次:通過用戶的openid與token獲取用戶信息。
對于頁面比較多的應用,每個頁面請求時都需要調用兩個方法,于用戶而言這是極其耗費時間的。
然而諸多的博客只是告訴我們改如何處理用戶信息,諸如將用戶信息先拉取下來存儲到自己的數據庫,然后每次需要時從自己的數據庫中通過openid來獲取。殊不知這種博客只說了一點,他們沒有考慮到的問題是:
- 用戶若修改了自己的信息,該如何同步到自己的表里面.
- 用戶的信息是獲取到了,但是每次用戶訪問網頁,標識用戶身份的openid依舊每次都要去調用接口獲取。(獲取用戶信息的次數微信API規定為500000次)
那么接下來我要說的這個就是如何解決上面兩個問題,處理過程大致如下:
a.添加攔截器,攔截需要授權頁面的controller
攔截器:
package com.fdc.home.dec.wx.filter; import com.alibaba.dubbo.common.utils.StringUtils; import com.fdc.platform.common.yfutil.PropertyReader; import org.springframework.web.method.HandlerMethod; import org.springframework.web.servlet.ModelAndView; import org.springframework.web.servlet.handler.HandlerInterceptorAdapter; import javax.servlet.http.Cookie; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.util.Arrays; /** * Created by pl on 2017/1/13. */ public class OAuth2Interceptor extends HandlerInterceptorAdapter { public static String indexUrl = PropertyReader.getValue("indexUrl");//從配置文件中讀取域名 // private static String[] arrQueController = {"newquestion", "mycenter","testCookie"}; public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { Cookie[] cookies = request.getCookies(); String openId=null; //判斷cookie中是否存在openid 若存在則直接跳過,不存在則獲取一次 if(cookies!=null){ for(Cookie cookie : cookies){ if(cookie.getName().equals("openId")){ openId = cookie.getValue(); } } } if (StringUtils.isEmpty(openId)) { if (handler instanceof HandlerMethod) { HandlerMethod handlerMethod = (HandlerMethod) handler; String methodName = handlerMethod.getMethod().getName(); String uri = request.getRequestURI(); // if (checkList(arrQueController, methodName)) { // System.out.println("執行了"); response.sendRedirect(indexUrl + "/oauth2Api?resultUrl=" + indexUrl + uri); return false; // } } return true; } else { return true; } } public void postHandle( HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception { } public void afterCompletion( HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { } public boolean checkList(String[] arr, String targetValue) { return Arrays.asList(arr).contains(targetValue); } }
<mvc:interceptors> <mvc:interceptor> <mvc:mapping path="/testCookie"/> <mvc:mapping path="/testCookie1"/> <bean class="com.fdc.home.dec.wx.filter.OAuth2Interceptor"/> </mvc:interceptor> </mvc:interceptors>
b.用戶首次訪問,調用微信接口獲取openid和用戶信息。將openid寫入服務器端的cookie,將用戶的信息寫入redis緩存中,以openid作為redis的key。
package com.fdc.home.dec.wx.controller; import com.fdc.home.dec.service.inter.service.DecWxService; import com.fdc.home.dec.wx.service.CheckUserInfo; import com.fdc.home.dec.wx.utils.JSONHelper; import com.fdc.home.dec.wx.utils.WxUtils; import com.fdc.home.dec.wx.vo.token.WeixinOauth2Token; import com.fdc.home.dec.wx.vo.user.WeixinUserInfo; import com.fdc.platform.common.yfutil.PropertyReader; 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.RequestParam; import javax.servlet.http.Cookie; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; import java.io.IOException; /** * Created by pl on 2017/1/13. */ @Controller public class OAuth2Controller { @Autowired private DecWxService decWxService; //判斷用戶是否登錄的公用方法 CheckUserInfo checkUserInfo = new CheckUserInfo(); //從配置文件獲取appid public static String appid = PropertyReader.getValue("appid"); //從配置文件獲取appsecret public static String appsecret = PropertyReader.getValue("appsecret"); //從配置文件獲取主域名 public static String indexUrl = PropertyReader.getValue("indexUrl"); public static String wtoken = PropertyReader.getValue("wholetoken"); /** * 組裝授權url * @param request * @param resultUrl * @return */ @RequestMapping(value ="/oauth2Api") public String oauth2API(HttpServletRequest request, @RequestParam String resultUrl) { String redirectUrl = ""; if (resultUrl != null) { String backUrl =indexUrl+"/oauth2MeUrl?oauth2url="+resultUrl; //組裝授權url redirectUrl = WxUtils.oAuth2Url(appid, backUrl); } return "redirect:" + redirectUrl; } /** * 獲取用戶信息 * @param request * @param response * @param code * @param oauth2url * @return * @throws IOException */ @RequestMapping(value = "/oauth2MeUrl") public String oauth2MeUrl(HttpServletRequest request,HttpServletResponse response, @RequestParam String code, @RequestParam String oauth2url) throws IOException { request.setCharacterEncoding("utf-8"); response.setCharacterEncoding("utf-8"); HttpSession session = request.getSession(); session.setAttribute("code",code); // 用戶同意授權 if (!"authdeny".equals(code)) { // 獲取網頁授權access_token WeixinOauth2Token weixinOauth2Token = WxUtils.getOauth2AccessToken(appid, appsecret, code); // 網頁授權接口訪問憑證 String accessToken = weixinOauth2Token.getAccessToken(); // 用戶標識 String openId = weixinOauth2Token.getOpenId(); String wholetoken = decWxService.getToken(wtoken); //獲取微信用戶openid存儲在cookie中的信息 Cookie userCookie=new Cookie("openId",openId); userCookie.setMaxAge(-1); userCookie.setPath("/"); response.addCookie(userCookie); WeixinUserInfo weixinUserInfo = WxUtils.getWeixinUserInfo(wholetoken, openId); //將用戶信息寫入redis decWxService.setToken(openId, JSONHelper.beanToJson(weixinUserInfo)); }else { return "redirect:"+indexUrl+"/error404"; } return "redirect:" + oauth2url; } }
(注:WxUtils中封裝各種請求微信服務器的接口,具體可自行百度)
以上兩步基本可以解決用戶授權的問題。基于此需要說明的是:
- 開發中要設置cookie過期時間,設置為負數,表明當用戶關閉瀏覽器的時候自動清空cookie,但在實際的測試中,微信瀏覽器并不會立刻清理cookie,你可以自行清理cookie.每次用戶訪問時直接從cookie中獲取openid,若沒有,才會調用微信接口獲取。在獲取openid的同時,更新redis緩存中的用戶信息,這樣達到及時同步用戶信息的效果,也減少了對微信服務器的訪問。
- 有部分網頁在博客中提到微信瀏覽器沒有cookie和session,基于這類問題,還請自己動手驗證嗎,微信瀏覽器是有cookie和session的(請區分服務端session和客戶端session),這里之所以沒有將openid存儲在session中,其主要是考慮到分布式多點項目中session比較難以處理。
- 比較重要的一點是,當用戶更換微信登錄時,cookie會自動清除,登錄成功后,會重新獲取新登錄的用戶的openid。
結語
如果你還需要了解更多技術文章信息,請繼續關注白衣秀才的博客
個人網站:http://penglei.top/
Github:https://github.com/whitescholars
微博:http://weibo.com/u/3034107691?refer_flag=1001030102_&is_all=1
文章列表