1. 程式人生 > >微信網頁授權,獲取微信code,獲取access tocken 獲取使用者資訊

微信網頁授權,獲取微信code,獲取access tocken 獲取使用者資訊

微信開發中,經常有這樣的需求:獲得使用者頭像、繫結微訊號給使用者發信息.. 那麼實現這些的前提就是授權!

1.配置安全回撥域名:

在微信公眾號請求使用者網頁授權之前,開發者需要先到公眾平臺官網中的“開發 - 介面許可權 - 網頁服務 - 網頁帳號 - 網頁授權獲取使用者基本資訊”的配置選項中,修改授權回撥域名,值得注意的是這裡就是直接寫全域名,如: www.liliangel.cn。然而我們開發h5中一般用的是二級域名,如:h5.liliangel.cn 也同樣在安全回撥域名中。

2.使用者級授權和靜默授權

1、以snsapi_base為scope發起的網頁授權,是用來獲取進入頁面的使用者的openid的,並且是靜默授權並自動跳轉到回撥頁的。使用者感知的就是直接進入了回撥頁。2、以snsapi_userinfo為scope發起的網頁授權,是用來獲取使用者的基本資訊的。但這種授權需要使用者手動同意,並且由於使用者同意過,所以無須關注,就可在授權後獲取該使用者的基本資訊。 

3.網頁授權access_token和普通access_token的區別

1、微信網頁授權是通過OAuth2.0機制實現的,在使用者授權給公眾號後,公眾號可以獲取到一個網頁授權特有的介面呼叫憑證(網頁授權access_token),通過網頁授權access_token可以進行授權後接口呼叫,如獲取使用者基本資訊; 2、其他微信介面,需要通過基礎支援中的“獲取access_token”介面來獲取到的普通access_token呼叫。 

4.引導使用者進入授權頁面同意授權,獲取code 

微信更新後,授權頁也變化了。其實習慣了綠色的那個經典頁面..

js:

複製程式碼
var center = {        init: function(){            .....        },        enterWxAuthor: 
function(){            var wxUserInfo = localStorage.getItem("wxUserInfo");            if (!wxUserInfo) {                var code = common.getUrlParameter('code');                if (code) {                    common.getWxUserInfo();                    center.init();                }else{                   
//沒有微信使用者資訊,沒有授權-->> 需要授權,跳轉授權頁面                    window.location.href = 'https://open.weixin.qq.com/connect/oauth2/authorize?appid='+ WX_APPID +'&redirect_uri='+ window.location.href +'&response_type=code&scope=snsapi_userinfo#wechat_redirect';                }            }else{                center.init();            }        }}$(document).ready(function() {     center.enterWxAuthor();}   
複製程式碼以scope=snsapi_userinfo為例,頁面載入的時候進入授權方法,首先從快取獲取wxUserInfo物件,如果有說明之前已經授權過,直接進入初始化方法。如果沒有,判斷url是否包含code,有code說明是進入授權頁回撥後的頁面,那麼通過code換取使用者資訊即可。沒有code,即使用者第一次進入該頁面,引導去授權頁,redirect_uri為當前頁面地址。

getWxUserInfo方法:

複製程式碼
/**     * 授權後獲取使用者的基本資訊     */    getWxUserInfo:function(par){        var code = common.getUrlParameter("code");                if (par) code = par;                $.ajax({            async: false,            data: {code:code},            type : "GET",            url : WX_ROOT + "wechat/authorization",            success : function(json) {                if (json){                    try {                        //保證寫入的wxUserInfo是正確的                        var data = JSON.parse(json);                        if (data.openid) {                            localStorage.setItem('wxUserInfo',json);//寫快取--微信使用者資訊                        }                    } catch (e) {                        // TODO: handle exception                    }                }            }        });    },
複製程式碼

5.後臺restful-- /wechat/authorization,根據code換取使用者資訊

複製程式碼
  /**     * 微信授權     * @param code 使用一次後失效     *      * @return 使用者基本資訊     * @throws IOException      */    @RequestMapping(value = "/authorization", method = RequestMethod.GET)    public void authorizationWeixin(            @RequestParam String code,            HttpServletRequest request,             HttpServletResponse response) throws IOException{        request.setCharacterEncoding("UTF-8");          response.setCharacterEncoding("UTF-8");         PrintWriter out = response.getWriter();        LOGGER.info("RestFul of authorization parameters code:{}",code);                try {            String rs = wechatService.getOauthAccessToken(code);            out.write(rs);            LOGGER.info("RestFul of authorization is successful.",rs);        } catch (Exception e) {            LOGGER.error("RestFul of authorization is error.",e);        }finally{            out.close();        }    }
複製程式碼這裡有一個授權access_token,切記:授權access_token非全域性access_token ,需要使用快取,這裡我使用的redis,具體配置不多說後面寫相關配置博文,當然也可以使用ehcache,關於ehcahe配置在我的第一篇部落格中有詳細介紹。複製程式碼
  /**     * 根據code 獲取授權的token 僅限授權時使用,與全域性的access_token不同     * @param code     * @return     * @throws IOException      * @throws ClientProtocolException      */    public String getOauthAccessToken(String code) throws ClientProtocolException, IOException{        String data = redisService.get("WEIXIN_SQ_ACCESS_TOKEN");        String rs_access_token = null;        String rs_openid = null;        String url = WX_OAUTH_ACCESS_TOKEN_URL + "?appid="+WX_APPID+"&secret="+WX_APPSECRET+"&code="+code+"&grant_type=authorization_code";        if (StringUtils.isEmpty(data)) {            synchronized (this) {                //已過期,需要重新整理                String hs = apiService.doGet(url);                JSONObject json = JSONObject.parseObject(hs);                String refresh_token = json.getString("refresh_token");                    String refresh_url = "https://api.weixin.qq.com/sns/oauth2/refresh_token?appid="+WX_APPID+"&grant_type=refresh_token&refresh_token="+refresh_token;                String r_hs = apiService.doGet(refresh_url);                JSONObject r_json = JSONObject.parseObject(r_hs);                String r_access_token = r_json.getString("access_token");                String r_expires_in = r_json.getString("expires_in");                rs_openid = r_json.getString("openid");                                rs_access_token = r_access_token;                redisService.set("WEIXIN_SQ_ACCESS_TOKEN", r_access_token, Integer.parseInt(r_expires_in) - 3600);                LOGGER.info("Set sq access_token to redis is successful.parameters time:{},realtime",Integer.parseInt(r_expires_in), Integer.parseInt(r_expires_in) - 3600);            }        }else{            //還沒有過期            String hs = apiService.doGet(url);            JSONObject json = JSONObject.parseObject(hs);            rs_access_token = json.getString("access_token");            rs_openid = json.getString("openid");            LOGGER.info("Get sq access_token from redis is successful.rs_access_token:{},rs_openid:{}",rs_access_token,rs_openid);        }                return getOauthUserInfo(rs_access_token,rs_openid);    }  /**     * 根據授權token獲取使用者資訊     * @param access_token     * @param openid     * @return     */    public String getOauthUserInfo(String access_token,String openid){        String url = "https://api.weixin.qq.com/sns/userinfo?access_token="+ access_token +"&openid="+ openid +"&lang=zh_CN";        try {            String hs = apiService.doGet(url);                        //儲存使用者資訊            saveWeixinUser(hs);                        return hs;        } catch (IOException e) {            LOGGER.error("RestFul of authorization is error.",e);        }        return null;    }
複製程式碼當時趕時間,程式碼命名較亂。可以看到,我用了一個同步的方法,先從快取中獲取key為WEIXIN_SQ_ACCESS_TOKEN,如果取到了說明沒有過期,直接通過httpclient呼叫微信提供的介面,返回使用者資訊的字串給前端。如果沒有取到,說明沒有或者已經過期,則根據refresh_token重新整理access_token,再寫快取,由於access_token擁有較短的有效期,為了保險我這裡設定了快取的失效時間微信給的時間再減一個小時。回過頭來看程式碼發現,上面的邏輯有點點小問題,這樣寫會導致第一次獲取或者快取失效後第一次獲取access_token都會去重新整理一次,暫時不影響使用,後面做優化修改 TODO。

6:儲存使用者資訊

通常情況下,授權後我們會將使用者資訊儲存資料庫表,openid為唯一主鍵,外來鍵關聯起我們自己的使用者表,這樣一來,無論是後續要開展什麼業務,還是做運營資料統計,都有了一個跟微信公眾號的關聯關係。值得注意的是:我們獲取到的headimgurl是微信提供的一個url地址,當用戶修改頭像後可能導致原來的地址失效,所以最好是通過將圖片儲存到本地伺服器然後儲存本地的地址url!微信返回的值:

參考連結:

微信公眾平臺官方文件:https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421140842&token=&lang=zh_CN

線上介面除錯工具:http://mp.weixin.qq.com/debug

沒有公眾號福利:測試賬號申請 http://mp.weixin.qq.com/debug/cgi-bin/sandbox?t=sandbox/login