微信公眾號開發(fā):獲取openId和用戶信息


  • title: 微信公眾號開發(fā):獲取openId和用戶信息
  • tags: 微信公眾號
  • categories: 筆記
  • date: 2017-05-01 14:35:34

最近一直在做微信公眾號項目的開發(fā),會有一種感覺,就是微信相關(guān)的開發(fā)真TM的啰嗦麻煩啊。當首次接觸這個微信相關(guān)內(nèi)容的開發(fā)時候,需要按照其官方文檔一步一步的按部就班,按照他的要求去操作。若是中途需要什么其他的開發(fā)資質(zhì)或者權(quán)限,還要進行申請,不僅要花錢,重點還特么的很花時間,直接來幾個工作日,我暈菜了。。不過多吐槽一句,微信這邊的跟阿里云平臺那邊處理速度,真的差太遠了~~
不過,若是有一次的微信開發(fā)經(jīng)驗,我相信日后再次進行微信相關(guān)內(nèi)容的開發(fā)就不會有什么難度了,只要不在申請微信七七八八的東西上面花費太多時間就好了。(方式多種,符合微信官方規(guī)范即可,此處僅作參考)

image

要進行微信公眾號開發(fā),那么要申請一個微信公眾號就必不可少了。現(xiàn)在微信公眾號類型有這幾種:訂閱號,企業(yè)號,服務號。關(guān)于三者的差別和介紹可以參考微信公眾號官網(wǎng)微信公眾平臺。申請通過之后,就能登陸微信公眾平臺管理平臺進行開發(fā)設置了。

每個公眾號申請成功后,微信服務那邊就會給每個公眾號分配一個與公眾號唯一性綁定的APPID。結(jié)合我們公眾平臺自己生成的密鑰,兩者就可以唯一確定某個公眾號以及是否合法了。接下來就應該參照微信公眾平臺開發(fā)文檔來進行開發(fā)前的配置設置。

開發(fā)前服務器配置

按照文檔所說,接入微信公眾平臺開發(fā),開發(fā)者需要按照如下步驟完成:

1. 填寫服務器配置
2. 驗證服務器地址的有效性
3. 依據(jù)接口文檔實現(xiàn)業(yè)務邏輯

接下來就上述每個步驟進行說明好了。

(1) 填寫服務器配置
在申請成功公眾號后,登入web微信公眾號管理平臺,在管理平臺首頁左邊的導航欄選擇: 開發(fā) --> 基本配置。就會進入改公眾號的開發(fā)基本配置管理。就可以看到有個區(qū)域是服務器配置,這個服務器配置,就是填寫我們將開發(fā)測試完成后的項目部署所在的服務器,且要填寫已經(jīng)備案后域名地址(若是要進行微信支付開發(fā))。

image

在填寫完所有服務器配置信息后,點擊提交,那么微信服務器會發(fā)送一個GET請求到我們上述URL填寫的地址上,我們服務器上通過接收微信服務器發(fā)送請求進行按照某種規(guī)則進行處理,將得到的結(jié)果返回微信服務器,進行判斷我們的服務器是否通過校驗,那么我們服務器上的代碼要如何處理這個校驗請求呢,往下看。

(2) 驗證服務器地址的有效性:
開發(fā)者提交信息后,微信服務器將發(fā)送GET請求到填寫的服務器地址URL上,GET請求攜帶四個參數(shù):

參數(shù)          描述
signature   微信加密簽名,signature結(jié)合了開發(fā)者填寫的token參數(shù)和請求中的timestamp參數(shù)、nonce參數(shù)。
timestamp   時間戳
nonce       隨機數(shù)
echostr     隨機字符串

開發(fā)者通過檢驗signature對請求進行校驗,若確定此次GET請求來著微信服務器,請原樣返回echostr參數(shù)內(nèi)容,則接入生效,成為開發(fā)者成功,否則接入失敗。(這一步也就是向微信服務器說明,上述我們配置的服務器確實是我們項目所在服務器,在接下來的開發(fā),測試過程中,都會在外網(wǎng)的這個服務器上進行,微信那邊會認為是合法的)

加密/校驗流程如下:
1. 將token、timestamp、nonce三個參數(shù)進行字典序排序
2. 將三個參數(shù)字符串拼接成一個字符串進行sha1加密
3. 開發(fā)者獲得加密后的字符串可與signature對比,標識該請求來源于微信

好了,來看看我們在java后臺中,如何對微信這個請求進行處理的:結(jié)合上圖的URL地址,會通過springmvc被路由派發(fā)到指定類的指定方法中進行處理。

@Controller(value="WXConfigUtil")
@Qualifier(value="WXConfigUtil")
@RequestMapping(value="/wXConfigUtil")
public class WXConfigUtil{
    
    //這個token要與公眾平臺服務器配置填寫的token一致
    private final static String token = "wechat";
    private Log log = LogFactory.getLog(WXConfigUtil.class);
    
    @RequestMapping(value="/WXConfig",method=RequestMethod.GET)
    @ResponseBody
    public String verifyWXConfig(@RequestParam(value="signature",required=false) String signature,
                                    @RequestParam(value="timestamp",required=false) String timestamp,
                                    @RequestParam(value="nonce",required=false) String nonce,
                                    @RequestParam(value="echostr",required=false) String echostr) {
        System.out.println(" PARAM VAL: >>>" + signature + "\t" + timestamp + "\t" + nonce + "\t" + echostr);
        log.info("開始簽名驗證:"+" PARAM VAL: >>>" + signature + "\t" + timestamp + "\t" + nonce + "\t" + echostr);
        if (StringUtils.isNotEmpty(signature) && StringUtils.isNotEmpty(timestamp)
                &&StringUtils.isNotEmpty(nonce) && StringUtils.isNotEmpty(echostr)) {
            String sTempStr = "";
            try {
                sTempStr = SHA1.getSHA1(timestamp, nonce, token, "");
            } catch (Exception e) {
                e.printStackTrace();
            }
            if (StringUtils.isNotEmpty(sTempStr) && StringUtils.equals(signature, sTempStr)) {
                log.info("驗證成功:-----------:"+sTempStr);
                return echostr;
            } else {
                log.info("驗證失敗:-----------:00000");
                return "-1";
            }
        } else {
            log.info("驗證失敗:-----------:11111");
            return "-1";
        }
    }
    

通過校驗后,會在公眾號的服務器配置頁面有提示的,通過校驗后,后面就可以在此服務器上進行開發(fā)與測試了。
(3)依據(jù)接口文檔實現(xiàn)業(yè)務邏輯
用戶向公眾號發(fā)送消息時,公眾號方收到的消息發(fā)送者是一個OpenID,是使用用戶微信號加密后的結(jié)果,每個用戶對每個公眾號有一個唯一的OpenID。

此外,由于開發(fā)者經(jīng)常有需在多個平臺(移動應用、網(wǎng)站、公眾帳號)之間共通用戶帳號,統(tǒng)一帳號體系的需求,微信開放平臺(open.weixin.qq.com)提供了UnionID機制。開發(fā)者可通過OpenID來獲取用戶基本信息,而如果開發(fā)者擁有多個應用(移動應用、網(wǎng)站應用和公眾帳號,公眾帳號只有在被綁定到微信開放平臺帳號下后,才會獲取UnionID),可通過獲取用戶基本信息中的UnionID來區(qū)分用戶的唯一性,因為只要是同一個微信開放平臺帳號下的移動應用、網(wǎng)站應用和公眾帳號,用戶的UnionID是唯一的。換句話說,同一用戶,對同一個微信開放平臺帳號下的不同應用,UnionID是相同的。詳情請在微信開放平臺的資源中心-移動應用開發(fā)-微信登錄-授權(quán)關(guān)系接口調(diào)用指引-獲取用戶個人信息(UnionID機制)中查看。

網(wǎng)頁授權(quán)獲取用戶基本信息

如果用戶在微信客戶端中訪問第三方網(wǎng)頁,這個場景就是在我們公眾號中通過菜單或者其他連接地址訪問我們自己開發(fā)的三方網(wǎng)頁,公眾號可以通過微信網(wǎng)頁授權(quán)機制來獲取用戶基本信息,從而實現(xiàn)業(yè)務邏輯。詳情參考:網(wǎng)頁授權(quán)獲取用戶基本信息

微信對于這個微信用戶訪問的三方頁面的授權(quán)是通過OAuth2.0鑒權(quán)的,現(xiàn)在很普遍的一個用戶授權(quán)機制。在官方文檔中有說明,若是需要在網(wǎng)頁中授權(quán)操作,那么需要填寫配置授權(quán)回調(diào)域名。僅僅是填寫一個不帶http或者https的域名字符串。該授權(quán)回調(diào)域名會在下面的網(wǎng)頁授權(quán)過程中,重定向的時候進行域名校驗。若是不填寫,或者填寫有誤,則網(wǎng)頁授權(quán)接口調(diào)用會失敗。

在微信公眾號管理平臺首頁中,點擊左邊導航欄:"設置" ---> "公眾號設置" --->"功能設置"。就會看到包括圖片水印,業(yè)務域名,JS接口安全域名,網(wǎng)頁授權(quán)域名。我們要設置的就是網(wǎng)頁授權(quán)回調(diào)域名,點擊網(wǎng)頁授權(quán)域名欄的設置按鈕,進行域名設置:
(例如直接寫 wx.example.com)

image

只是需要下載圖片上的一個text文件,上傳到服務器指定位置,能通過url直接訪問,讓微信服務器可以訪問進行字符串對比校驗即可,配置成功。

在微信客戶端的網(wǎng)頁開發(fā)的授權(quán)中,有兩種授權(quán)范圍scope:snsapi_basesnsapi_userinfo

1、以snsapi_base為scope發(fā)起的網(wǎng)頁授權(quán),是用來獲取進入頁面的用戶的openid的,并且是靜默授權(quán)并自動跳轉(zhuǎn)到回調(diào)頁的。用戶感知的就是直接進入了回調(diào)頁(往往是業(yè)務頁面)
2、以snsapi_userinfo為scope發(fā)起的網(wǎng)頁授權(quán),是用來獲取用戶的基本信息的。但這種授權(quán)需要用戶手動同意,并且由于用戶同意過,所以無須關(guān)注,就可在授權(quán)后獲取該用戶的基本信息。
3、用戶管理類接口中的“獲取用戶基本信息接口”,是在用戶和公眾號產(chǎn)生消息交互或關(guān)注后事件推送后,
才能根據(jù)用戶OpenID來獲取用戶基本信息。這個接口,包括其他微信接口,都是需要該用戶(即openid)關(guān)注了公眾號后,才能調(diào)用成功的。

可以看到,在微信公眾號開發(fā)中,對與獲取微信用戶信息其實是有幾種不同的方式的。分別針對不同的實際場景下,若是要獲取的微信用戶并沒有關(guān)注我們的公眾號,我們只能通過網(wǎng)頁授權(quán)auth2.0,來讓頁面顯示提示獲取用戶信息,讓用戶來決定是否同意讓我們公眾號來獲取他信息;另一方面,若是微信用戶已經(jīng)關(guān)注我們的公眾號話,我們就有權(quán)限直接通過指定接口獲取用戶信息,而無需讓用戶授權(quán)。

下面來說說通過網(wǎng)頁授權(quán)方式,獲取未關(guān)注公眾號的微信用戶信息。

snsapi_base

這個授權(quán)叫"靜默授權(quán)",意思就是在用戶打開我們開發(fā)的三方網(wǎng)頁頁面的時候,并不會顯示的彈出一個授權(quán)頁面,讓用戶知道要授權(quán)。而是進入頁面就默認可以獲取用戶的openId。這個靜默授權(quán)僅僅只能拿到微信用戶的openId就結(jié)束了。

(1) 獲取code
必須是在微信客戶端,引導微信用戶訪問下面文檔指定的url。注意,我們可以修改的僅僅是接口中重定向redirect_uri部分,可以重定向到我們自己開發(fā)的頁面中。這里要注意的是:<font color="red">這個重定向的url,跳轉(zhuǎn)回調(diào)redirect_uri必須要經(jīng)過URLEncoder編碼</font>。

參考鏈接(請在微信客戶端中打開此鏈接體驗)
Scope為snsapi_base
https://open.weixin.qq.com/connect/oauth2/authorize?appid=wx520c15f417810387
&redirect_uri=https%3A%2F%2Fchong.qq.com%2Fphp%2Findex.php%3Fd%3D%26c%3DwxAdapter%26m%3DmobileDeal%26showwxpaytitle%3D1%26vb2ctag%3D4_2030_5_1194_60
&response_type=code&scope=snsapi_base&state=123#wechat_redirect

看看在java項目中,如何進行這個操作:

    @RequestMapping(value="/routerToMyPage.html",method=RequestMethod.GET)
    public void redirectToMyPage(HttpServletRequest request,HttpServletResponse response){
        StringBuffer sb = new StringBuffer();
        StringBuffer encodeUrl = new StringBuffer(300);
        //公眾號中配置的回調(diào)域名(網(wǎng)頁授權(quán)回調(diào)域名)
        String doname = ConfigService.getValue("DONAME", "doname");
        String root = request.getContextPath();
        String appId = Constants.APPID;

        sb.append("https://open.weixin.qq.com/connect/oauth2/authorize?appid=");
        sb.append(appId);
        String url = "";
        try {
            //對重定向url進行編碼,官方文檔要求
            encodeUrl.append(doname).append(root).append("/my/myPage.html");
//          url = URLEncoder.encode(encodeUrl.toString(), "utf-8");
            url = URLEncoder.encode("https://domain/xbdWXBG(項目名稱)/my/myPage.html", "utf-8");
            sb.append("&redirect_uri=").append(url);
            //網(wǎng)頁授權(quán)的靜默授權(quán)snsapi_base
            sb.append("&response_type=code&scope=snsapi_base&state=123#wechat_redirect");
            response.sendRedirect(sb.toString());
        } catch (UnsupportedEncodingException e) {
            log.error("重定向url編碼失敗:>>" + e.getMessage());   
            e.printStackTrace();
        } catch (Exception e) {
            log.error("response重定向失敗:>>" + e.getMessage());   
            e.printStackTrace();
        }
    }

在上述代碼中,當用戶進入到routerToMyPage.html頁面進行請求,那么就能通過回調(diào)uri進行我們其他的頁面中,就能拿到code。因為在跳轉(zhuǎn)到重定向頁面過程中,微信服務器會將一個請求參數(shù)code值攜帶在請求url中。

(2) 得到openId
在靜默授權(quán)的第(1)步中,頁面會攜帶code參數(shù),重定向跳轉(zhuǎn)到myPage.html頁面中,那么我們就可以在這個頁面中獲取code的值,并根據(jù)該值調(diào)用指定接口獲得當前微信用戶的openId。

    @RequestMapping(value="/myPage.html",method=RequestMethod.GET)
    public ModelAndView toMyPage(HttpServletRequest request,HttpServletResponse response){
        ....
        
        ModelAndView mv = new ModelAndView("/mypage/mypage");
        //獲取重定向攜帶的code參數(shù)值
        String code = request.getParameter("code");

        Object openId = "";
        
        if (null == openId) {
            /*
             * 根據(jù)得到的code參數(shù),內(nèi)部請求獲取openId的方法。
             */
            openId = getOpenId(request,response,code);
        }
        
        log.info("session中得到的openId值為:>>" + String.valueOf(openId));
        
        //根據(jù)openId查詢用戶信息
        Users user = myPageService.getUserByOpenId(String.valueOf(openId));

        ...
        return mv;
    }

    //發(fā)送請求,根據(jù)code獲取openId
    public String getOpenId(HttpServletRequest request, HttpServletResponse response,String code) {
        
        String content = "";
        String openId = "";
        String unionId = "";
        //封裝獲取openId的微信API
        StringBuffer url = new StringBuffer();
        url.append("https://api.weixin.qq.com/sns/oauth2/access_token?appid=")
        .append(Constants.APPID)
        .append("&secret=")
        .append(Constants.APPSECRET)
        .append("&code=")
        .append(code)
        .append("&grant_type=authorization_code");
        
        ObjectMapper objectMapper = new ObjectMapper();
        try {
            content = HttpClient.requestGet(url.toString());
            Map map = objectMapper.readValue(content, Map.class);
            openId = String.valueOf(map.get("openid"));
            unionId = String.valueOf(map.get("unionid"));
            
            log.info("獲取的openID:" + openId);
            /*
             * 將openId保存到session中,當其他業(yè)務獲取openId時,
             * 可先從session中獲取openId.
             */
            request.getSession().setAttribute("openId", openId);
        } catch (JsonParseException e) {
            log.error("json解析失敗:", e);
        } catch (JsonMappingException e) {
            log.error("map轉(zhuǎn)換成json失敗:", e);
        } catch (Exception e) {
            log.error("http獲取openId請求失敗:", e);
        }
        return openId;
    }

可以看到getOpenId方法中,通過code值和appid,secret發(fā)送了一個http請求,用于獲取用戶的openId,請求成功返回如下格式內(nèi)容:

{
   "access_token":"ACCESS_TOKEN",
   "expires_in":7200,
   "refresh_token":"REFRESH_TOKEN",
   "openid":"OPENID",
   "scope":"SCOPE"
}
##參數(shù)說明
access_token    網(wǎng)頁授權(quán)接口調(diào)用憑證,注意:此access_token與基礎(chǔ)支持的access_token不同
expires_in      access_token接口調(diào)用憑證超時時間,單位(秒)
refresh_token   用戶刷新access_token
openid          用戶唯一標識,請注意,在未關(guān)注公眾號時,用戶訪問公眾號的網(wǎng)頁,也會產(chǎn)生一個用戶和公眾號唯一的OpenID
scope           用戶授權(quán)的作用域,使用逗號(,)分隔

因為同一個微信用戶對每一個微信公眾號來說,都有唯一的標識就是這個openId。所以,我們拿到openId就能針對這個指定用戶來做一些其他的業(yè)務操作,靜默授權(quán)snsapi_base就到此結(jié)束了,并不會獲取得到微信的用戶信息。

snsapi_userinfo

若是不僅僅想要獲取微信用戶的openId,還想獲取未關(guān)注公眾號的微信用戶信息,那么可以通過snsapi_userinfo授權(quán)來實現(xiàn)。
(1) 重定向獲取code

Scope為snsapi_userinfo
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

當微信用戶點擊這個頁面,會跳出授權(quán)頁面,如下圖:


image

(2) 得到openId
與靜默授權(quán)一樣,重定向到我們開發(fā)的三方頁面,則可以先獲取code值,在根據(jù)code值發(fā)送下面的連接請求,得到openId:

//通過request.getParameter("code")獲取code
獲取code后,請求以下鏈接獲取access_token: 
https://api.weixin.qq.com/sns/oauth2/access_token?appid=APPID&secret=SECRET&code=CODE&grant_type=authorization_code

可以參考靜默授權(quán)java代碼實現(xiàn)詳情,此處省略。

(3) 獲取個人信息
在拿到微信用戶對應的openId之后,因為之前用戶已經(jīng)同意授權(quán)后,就能通過以下請求連接獲取未關(guān)注的用戶個人信息:

http:GET(請使用https協(xié)議)
https://api.weixin.qq.com/sns/userinfo?access_token=ACCESS_TOKEN&openid=OPENID&lang=zh_CN

此處要注意的是access_token值,這個access_token的值是上面(2)方法調(diào)用返回的token,并不是JSSDK網(wǎng)頁調(diào)用基礎(chǔ)支持全局的token。響應返回json格式的用戶個人信息。

用戶管理-獲取用戶基本信息

微信公眾號開發(fā)文檔中,有指定的api 接口可以讓我們調(diào)用,獲取微信用戶的基本信息。這個接口調(diào)用的成功的前提條件是:所要獲取的微信用戶信息是已經(jīng)關(guān)注了我們的微信公眾號的。若是該微信用戶沒有關(guān)注,則不能通過此接口調(diào)用,只能通過上述的網(wǎng)頁授權(quán)方式獲取用戶信息。詳情參考:用戶管理-獲取用戶基本信息

(1) 獲取用戶基本信息api
文檔中聲明的調(diào)用接口如下:

http請求方式: GET
https://api.weixin.qq.com/cgi-bin/user/info?access_token=ACCESS_TOKEN&openid=OPENID&lang=zh_CN

## 參數(shù)說明
access_token    是   調(diào)用接口憑證
openid  是   普通用戶的標識,對當前公眾號唯一
lang    否   返回國家地區(qū)語言版本,zh_CN 簡體,zh_TW 繁體,en 英語

這里要注意的是這個access_token是微信接口調(diào)用的憑證。與網(wǎng)頁授權(quán)通過code拿到的access_token是不同的。這個接口憑證,是全局token,也就是說,若是項目中要調(diào)用所有微信其他jssdk等接口,請求url參數(shù)中多數(shù)時候都是需要這個參數(shù)token值的。具體的可以查看下面的第(2)步獲取全局token。再次,就設定已經(jīng)拿到了全局接口調(diào)用的access_token,并將該token保存到內(nèi)存中。看看java中如何調(diào)用接口獲取微信用戶信息。

    public Users getWechatUserInfo(String openId) {
        //獲取保存在內(nèi)存中的全局接口調(diào)用access_token
        String accessToken = Constants.ACCESS_TOKEN;
        log.info("全局token>>" + accessToken);

        //構(gòu)造獲取用戶基本信息api
        StringBuffer url = new StringBuffer();
        url.append("https://api.weixin.qq.com/cgi-bin/user/info?")
           .append("access_token=").append(accessToken)
           .append("&openid=").append(openId).append("&lang=zh_CN");
        
        String content = "";
        ObjectMapper objectMapper = new ObjectMapper();
        Users user = null;
        try {
            for (int i = 1; i <= 3; i++) {
                //content就是json格式的用戶信息
                content = httpUtil.executeGet(url.toString());
                log.info("獲取微信用戶請求響應信息:>>" + content);
                Map map = objectMapper.readValue(content, Map.class);
                Object mopenId = map.get("openid");
                Object nickName = map.get("nickname");
                log.info("第" + i + "次獲取openId=" + openId + "的微信用戶昵稱:>>"+ nickName);
                if (openId.equals(mopenId) && nickName != null) {
                    /*
                     * 獲取微信用戶基本信息成功,并將信息封裝到平臺用戶對象中。
                     */
//                  user = myPageDao.getUserByOpenId(openId);
                    user = new Users();
                    if(user != null) {
                        user.setNickname(String.valueOf(nickName));
//                      user.setName(String.valueOf(nickName));
                        user.setSex((Integer) map.get("sex"));
                        user.setPictureURL(String.valueOf(map.get("headimgurl")));
                        user.setOpenid(String.valueOf(mopenId));
                        user.setUnionID(String.valueOf(map.get("unionid")));
                    }
                    log.info("調(diào)用微信得到的用戶信息:>>" + user.getNickname() + ",photo>>"+ user.getPictureURL());
                    return user;
                }
                log.info("第" + i + "次獲取openId=" + openId + "的微信用戶信息失敗!!");
            }

        } catch (JsonParseException e) {
            log.error("獲取微信基本用戶信息時,json轉(zhuǎn)換失敗:>>", e);
            e.printStackTrace();
        } catch (Exception e) {
            log.error("http請求執(zhí)行錯誤:>>", e);
            e.printStackTrace();
        }
        return user == null ? new Users() : user;
    }

(2) 獲取全局接口調(diào)用token
access_token是公眾號的全局唯一票據(jù),公眾號調(diào)用各接口時都需使用access_token。開發(fā)者需要進行妥善保存。access_token的存儲至少要保留512個字符空間。access_token的有效期目前為2個小時,需定時刷新,重復獲取將導致上次獲取的access_token失效。詳情:獲取access token

我們可以通過定時器定時調(diào)用獲取token的api請求,得到這個access_token就保存在內(nèi)存中,若是其他接口需要使用,直接調(diào)用即可。這里官方也有說明:如果第三方不使用中控服務器,而是選擇各個業(yè)務邏輯點各自去刷新access_token,那么就可能會產(chǎn)生沖突,導致服務不穩(wěn)定。,也就是不建議我們頻繁的手動調(diào)用這個api來更新access_token。

http請求方式: GET
https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=APPID&secret=APPSECRET

## 參數(shù)說明
grant_type  是   獲取access_token填寫client_credential
appid   是   第三方用戶唯一憑證
secret  是   第三方用戶唯一憑證密鑰,即appsecret

## 返回
{"access_token":"ACCESS_TOKEN","expires_in":7200}

來看看java中如何獲取這個全局接口調(diào)用token:

    private void getAccessToken() {
        StringBuffer url = new StringBuffer();
        url.append("https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential")
           .append("&appid=").append(Constants.APPID)
           .append("&secret=").append(Constants.APPSECRET);
        log.info("獲取全局accesss_token的請求:>>" + url.toString());
        try {
            String content;
            ObjectMapper objectMapper = new ObjectMapper();
            /*
             * 發(fā)送請求獲取access_token,最多發(fā)送3次請求進行獲取。
             */
            for(int i = 1; i <= 3; i++) {
                if(httpUtil == null) {
                    httpUtil = new HttpUtil();
                }
                content = httpUtil.executeGet(url.toString());
                try {
                    Map map = objectMapper.readValue(content, Map.class);
                    Object at = map.get("access_token");
                    log.info("第" + i + "次定時器獲取全局access_token:>>" + at);
                    if(null != at) {
                        //刷新內(nèi)存中的全局ACCESS_TOKEN值。
                        Constants.ACCESS_TOKEN = String.valueOf(at);
                        log.info("全局access_token刷新成功!!");
                        break;
                    }
                    log.info("全局access_token刷新失敗!!");
                } catch (Exception e) {
                    log.error("獲取全局access_token時,json轉(zhuǎn)換失敗:" + e.getMessage());
                    break;
                }
            }
    
        } catch (Exception e) {
            log.error("獲取全局access_token失敗:" + e.getMessage());
        }
        
    }

在來多說幾句,這個如何用定時器來統(tǒng)一刷新內(nèi)存中全局token,主要結(jié)合quartz定時器來實現(xiàn)。
第一步,要先引入定時器quartz依賴庫:

        <!-- 定時器 -->
        <dependency>
            <groupId>org.quartz-scheduler</groupId>
            <artifactId>quartz</artifactId>
            <version>1.8.6</version>
        </dependency>

第二步,實現(xiàn)定時器調(diào)用的job具體實現(xiàn)類:

@Service("accessTokenService")
public class AccessTokenService {

    //定時器調(diào)用方法
    public void execute() {
        getAccessToken();//上述代碼已經(jīng)列出
    }

    /**
     * refresh: 提供一個入口,進行強制手動刷新token。
     */
    public static void refresh() {
        new AccessTokenService().getAccessToken();
    }

}

第三步,在spring配置文件中配置定時器:

## applicationContext.xml
    <!-- 應用程序定時器配置 -->
    <bean class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
        <property name="triggers">
            <list>
                <ref bean="accessTokenTrigger" />
            </list>
        </property>
        <property name="autoStartup" value="true" />
    </bean>

    <!-- 配置定時器:每2小時刷新一次微信接口調(diào)用全局token -->
    <bean id="accessTokenTrigger" class="org.springframework.scheduling.quartz.CronTriggerBean">
        <property name="jobDetail" ref="tokenJobDetail" />
        <property name="cronExpression" value="0 0 */2 * * ?" /><!-- 每隔2個小時觸發(fā)一次 -->
    </bean>
    <bean id="tokenJobDetail"
        class="org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean">
        <property name="targetObject" ref="accessTokenService" />
        <property name="targetMethod" value="execute" />
        <property name="concurrent" value="false" />
        <!-- 是否允許任務并發(fā)執(zhí)行。當值為false時,表示必須等到前一個線程處理完畢后才再啟一個新的線程 -->
    </bean>

第四步,要使這個定時器在服務器啟動時候,必須調(diào)用一次。基于服務器啟動時間,每間隔2個小時就進行token刷新:
需要我們在web.xml配置文件中配置監(jiān)聽器listener,定義一個在服務器啟動時候,就進行調(diào)用的類方法。

## web.xml
<listener>
    <listener-class>com.cybbj.utils.AccessTokenInit</listener-class>
</listener>


## AccessTokenInit.java 集成ServletContextListener實現(xiàn)服務器啟動監(jiān)聽
public class AccessTokenInit implements ServletContextListener{

    public void contextInitialized(ServletContextEvent sce) {
            AccessTokenService.refresh();
    }

    public void contextDestroyed(ServletContextEvent sce) {
        
    }

}

總結(jié)

至此,這個微信公眾號的基本基礎(chǔ)開發(fā)配置完成了,后面還有JSSDK頁面開發(fā)要進行說明。在微信開發(fā)過程中,只要嚴格按照微信官方的開發(fā)文檔進行操作就應該沒有什么大的問題,感覺都是調(diào)用接口api,沒啥難度....

不過,還是有很長的路要走啊....

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,316評論 6 531
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 98,481評論 3 415
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,241評論 0 374
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,939評論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,697評論 6 409
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 55,182評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,247評論 3 441
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 42,406評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 48,933評論 1 334
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 40,772評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,973評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,516評論 5 359
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 44,209評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,638評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,866評論 1 285
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,644評論 3 391
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,953評論 2 373

推薦閱讀更多精彩內(nèi)容