微信公衆號開發之網頁受權登陸及code been used 解決!

首先微信公衆號開發網頁受權登陸使用環境:html

開發工具:eclipse;服務器:tomcat8,開發語言:JAVA。java

我寫的網頁受權登陸時用開發者模式自定義view類型按鈕點擊跳轉連接的。ios

微信網頁受權登陸首先以官方微信開發文檔爲準,大致共分爲4步:spring

先說第一步獲取code:json

code說明:code做爲換取access_token的票據,每次用戶受權帶上的code將不同,code只能使用一次,5扽這未被使用自動過時。api

微信公衆開發文檔給的有獲取code的連接,建議直接複製來用,而後替換其中相應的參數便可。緩存

連接爲:tomcat

https://open.weixin.qq.com/connect/oauth2/authorize?appid=APPID&redirect_uri=REDIRECT_URI&response_type=code&scope=SCOPE&state=STATE#wechat_redirect

其中參數說明:安全

這官網上都有,這裏展現是想說明一下scope參數,請注意看官網上給出的demo:服務器

請注意微信受權登陸scope兩種redirect_url後面跟的連接使用的協議。

這個協議使用不當可能會在項目部署到服務器上測試時在安卓和ios上出現問題。

至此,以snsapi_base爲scope發起的網頁受權,是用來獲取進入頁面的用戶的openid的,而且是靜默受權並自動跳轉到回調頁的。用戶感知的就是直接進入了回調頁(每每是業務頁面);

以snsapi_userinfo爲scope發起的網頁受權,是用來獲取用戶的基本信息的。但這種受權須要用戶手動贊成,而且因爲用戶贊成過,因此無須關注,就可在受權後獲取該用戶的基本信息。

參數替換完畢若是以snsapi_userinfo爲scope發起的網頁受權,是在PC端點擊菜單會跳出提示用戶贊成受權登陸,若是用戶未關注公衆號時一樣也會提示,示例頁面:

若是是在移動端用戶關注狀況下則不會出現此頁面。

若是用戶贊成受權,頁面將跳轉至 redirect_uri/?code=CODE&state=STATE,若跳轉錯誤請根據日誌輸出的錯誤碼在官網上查看相應的說明,附上官網上錯誤返回碼說明:

而後是第二步根據連接傳過來的code去獲取網頁受權access_token:

官網上給出的連接:

https://api.weixin.qq.com/sns/oauth2/access_token?appid=APPID&secret=SECRET&code=CODE&grant_type=authorization_code

這個access_token和基本的access_token不一樣,具體請參考官網說明,這裏給出獲取網頁受權access_token的JAVA實現方法:

 1 /**
 2      * 獲取網頁受權憑證
 3      * 
 4      * @param appId 公衆帳號的惟一標識
 5      * @param appSecret 公衆帳號的密鑰
 6      * @param code
 7      * @return WeixinAouth2Token
 8      */
 9     public static WeixinOauth2Token getOauth2AccessToken(String appId, String appSecret, String code) {
10         WeixinOauth2Token wat = null;
11         // 拼接請求地址
12         String requestUrl = "https://api.weixin.qq.com/sns/oauth2/access_token?appid=APPID&secret=SECRET&code=CODE&grant_type=authorization_code";
13         requestUrl = requestUrl.replace("APPID", appId);
14         requestUrl = requestUrl.replace("SECRET", appSecret);
15         requestUrl = requestUrl.replace("CODE", code);
16         // 獲取網頁受權憑證
17         JSONObject jsonObject = CommonUtil.httpsRequest(requestUrl, "GET", null);
18         if (null != jsonObject) {
19             try {
20                 wat = new WeixinOauth2Token();
21                 wat.setAccessToken(jsonObject.getString("access_token"));
22                 wat.setExpiresIn(jsonObject.getInt("expires_in"));
23                 wat.setRefreshToken(jsonObject.getString("refresh_token"));
24                 wat.setOpenId(jsonObject.getString("openid"));
25                 wat.setScope(jsonObject.getString("scope"));
26             } catch (Exception e) {
27                 wat = null;
28                 int errorCode = jsonObject.getInt("errcode");
29                 String errorMsg = jsonObject.getString("errmsg");
30                 log.error("獲取網頁受權憑證失敗 errcode:{} errmsg:{}", errorCode, errorMsg);
31             }
32         }
33         return wat;
34     }

須要的參數爲開發者ID(AppID),開發者密碼(AppSecret),和獲取到的code。正確返回json數據包爲:

而後第三步,若是須要的話進行,方法和第二步相似,所需連接官網給的有。

最後一步是獲取用戶的信息(須要scope爲snsapi_userinfo,snsapi_base只能獲取到用戶的openId):

所須要的請求方法:

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

而後替換成相應的參數,JAVA代碼爲:

 1     /**
 2      * 經過網頁受權獲取用戶信息
 3      * 
 4      * @param accessToken 網頁受權接口調用憑證
 5      * @param openId 用戶標識
 6      * @return SNSUserInfo
 7      */
 8     public static SNSUserInfo getSNSUserInfo(String accessToken, String openId) {
 9         SNSUserInfo snsUserInfo = null;
10         // 拼接請求地址
11         String requestUrl = "https://api.weixin.qq.com/sns/userinfo?access_token=ACCESS_TOKEN&openid=OPENID&lang=zh_CN";
12         requestUrl = requestUrl.replace("ACCESS_TOKEN", accessToken).replace("OPENID", openId);
13         // 經過網頁受權獲取用戶信息
14         JSONObject jsonObject = CommonUtil.httpsRequest(requestUrl, "GET", null);
15 
16         if (null != jsonObject) {
17             try {
18                 snsUserInfo = new SNSUserInfo();
19                 // 用戶的標識
20                 snsUserInfo.setOpenId(jsonObject.getString("openid"));
21                 // 暱稱
22                 snsUserInfo.setNickname(jsonObject.getString("nickname"));
23                 // 性別(1是男性,2是女性,0是未知)
24                 snsUserInfo.setSex(jsonObject.getInt("sex"));
25                 // 用戶所在國家
26                 snsUserInfo.setCountry(jsonObject.getString("country"));
27                 // 用戶所在省份
28                 snsUserInfo.setProvince(jsonObject.getString("province"));
29                 // 用戶所在城市
30                 snsUserInfo.setCity(jsonObject.getString("city"));
31                 // 用戶頭像
32                 snsUserInfo.setHeadImgUrl(jsonObject.getString("headimgurl"));
33             } catch (Exception e) {
34                 snsUserInfo = null;
35                 int errorCode = jsonObject.getInt("errcode");
36                 String errorMsg = jsonObject.getString("errmsg");
37                 log.error("獲取用戶信息失敗 errcode:{} errmsg:{}", errorCode, errorMsg);
38             }
39         }
40         return snsUserInfo;
41     }

上面所述皆是根據微信公衆號官網以及百度所寫。另外還參考一篇很不錯的微信公衆號開發文檔,能夠說是帶我入的門,給個連接:

http://www.cnblogs.com/liuhongfeng/p/4846260.html

 

下面說一下微信網頁受權登陸中遇到的code  been used問題:


我在微信網頁受權登陸寫完以後開始測試,在保證代碼的正確性與準確性後,打開微信公衆號,點擊本身定義跳轉連接的菜單,併成功進入,可是在點擊刷新或者回退是會報錯,錯誤的信息就是code  been used。

官網上給出的說明很詳細,code只能被使用一次,若是顯示code  been used則說明code被重複使用了。

首先說一個簡單的code  been used錯誤的產生:

有的開發者在寫網頁受權登陸時會出現這樣的頁面:

這是在微信開發公衆號上沒有配置安全域名,致使微信網頁受權登陸時會顯示這樣的頁面,url跳轉了兩次,傳入的code被重複使用了,遇到這種的能夠如今微信開發公衆號裏面配置安全域名。

而後說廣泛遇到的code  been used問題。

基本思路時:當我點擊菜單按鈕進入頁面時,先去sssion緩存中去那由code獲取到的openId,若是openId不存在,則證實code爲首次使用,能夠根據傳過來的code獲取相應的access_token和openId。

若是存在,則直接使用獲取到的openId去獲取用戶的一系列信息。

我用的時springMVC,簡單實現代碼爲:

首先在開發者定義的菜單路徑上配置域名和跳轉的控制器方法:

前面模糊的是本身配置的域名,後面/weixin/redirect則是要跳轉的方法

跳轉到的方法爲:

JAVA代碼:

 1 /**獲取用戶openId
 2      * @throws IOException */
 3     @RequestMapping(value = "/redirect", method = RequestMethod.GET)
 4     public ModelAndView repairs(ModelAndView mav, HttpServletRequest request, HttpServletResponse resp) throws IOException{
 5         String openId = (String) request.getSession().getAttribute("openId");//先從緩存中獲取經過code獲得的openID
 6         System.out.println(openId);//測試輸出openId
 7         if(openId==null){//判斷openId是否爲空(判斷code是否爲第一次被使用)
 8             RedirectUtils.redireUrl1(request, resp);//openid爲空也就是code被第一次使用時跳轉方法
 9              return null;
10         }
11             mav.addObject("openId",openId);//沒有被使用時
12             mav.setViewName("/weixin/repairs");//返回要跳轉的視圖頁面
13         return mav;
14     }

 

RedirectUtils.redireUrl1(request, resp);爲重定向跳轉的路徑。JAVA代碼:
 1 public static void redireUrl1(HttpServletRequest request,HttpServletResponse response){
 2         System.out.println("跳轉");//測試是否跳轉過來了
 3         String a="";
 4         if(request.getQueryString()!=null){
 5             a="?"+request.getQueryString();
 6         }
 7         String url = Base64.getBase64(request.getRequestURL()+a);//此爲連接中帶的一些參數  不須要能夠不用寫
 8         System.out.println(request.getRequestURL()+a);
 9          String basePath = WeChatConfig.URL+"weixin/wxyz?url="+url;//redirect_uri地址  
10          System.out.println(basePath);
11             String urls="https://open.weixin.qq.com/connect/oauth2/authorize?appid=" + WeChatConfig.APP_ID+
12                     "&redirect_uri=" + CommonUtil.urlEncodeUTF8(basePath)+
13                     "&response_type=code" +
14                     "&scope=snsapi_userinfo" +
15                     "&state=STATE#wechat_redirect";
16             try {
17                 response.sendRedirect(urls);//重定向執行url
18             }catch(Exception e){
19                 e.printStackTrace();
20             }
21     }

其中Base64.getBase64爲Base64加密方法,WeChatConfig.URL爲你本身微信公衆平臺配置的安全域名,CommUtil.urlEncodeUTF8對重定向連接進行編碼。

提供一下Base64方法中的加密解密方法,請先下載相應的jar包:

 1 **
 2  * Base64工具 CREATE 2016.12.14 form yjf
 3  * 
 4  */
 5 public class Base64 {
 6     /**
 7      * Base64加密
 8      * 
 9      */
10     @SuppressWarnings("restriction")
11     public static String getBase64(String value) {
12         byte[] bytes = null;
13         String basevalue = null;
14         try {
15             bytes = value.getBytes("utf-8");
16         } catch (UnsupportedEncodingException e) {
17             e.printStackTrace();
18         }
19         if (bytes != null) {
20             basevalue = new BASE64Encoder().encode(bytes);
21         }
22         return basevalue;
23     }
24 
25     /**
26      * Base64解密
27      * 
28      */
29     @SuppressWarnings("restriction")
30     public static String getFromBase64(String basevalue) {
31         byte[] bytes = null;
32         String result = null;
33         if (basevalue != null) {
34             BASE64Decoder decoder = new BASE64Decoder();
35             try {
36                 bytes = decoder.decodeBuffer(basevalue);
37                 result = new String(bytes, "utf-8");
38             } catch (Exception e) {
39                 e.printStackTrace();
40             }
41         }
42         return result;
43     }
44 
45 }

而後時CommUtil.urlEncodeUTF8編碼代碼:

 1 /**
 2      * URL編碼(utf-8)
 3      * 
 4      * @param source
 5      * @return
 6      */
 7     public static String urlEncodeUTF8(String source) {
 8         String result = source;
 9         try {
10             result = java.net.URLEncoder.encode(source, "utf-8");
11         } catch (UnsupportedEncodingException e) {
12             e.printStackTrace();
13         }
14         return result;
15     }

而後方法執行response.sendRedirect(urls);跳轉回wxyz方法進行獲取一系列參數,代碼爲:

 1 @RequestMapping(value="/wxyz",method=RequestMethod.GET)
 2     public ModelAndView wxYz(ModelAndView mvc,HttpServletRequest req,HttpServletResponse response){
 3          System.out.println("微信驗證");//測試是否跳轉到此方法中
 4             String code=req.getParameter("code");//獲取url參數中的code
 5             WeixinOauth2Token weixinOauth2Token  = AdvancedUtil.getOauth2AccessToken(WeChatConfig.APP_ID, WeChatConfig.APP_SECRET, code);
 6             if(weixinOauth2Token.getOpenId()!=null){
 7                 String openId = weixinOauth2Token.getOpenId();
 8                 req.getSession().setAttribute("openId",openId);//將獲取到的openID存入session緩存中
 9                 System.out.println("openId"+openId);
10                 mvc.addObject("openId",openId);
11                 mvc.setViewName("redirect:/weixin/wxLogin");
12                 return mvc;
13             }
14             return null;
15     }

其中AdvancedUtil.getOauth2AccessToken方法時獲取網頁受權access_token 方法,代碼爲:

 1 /**
 2      * 獲取網頁受權憑證
 3      * 
 4      * @param appId 公衆帳號的惟一標識
 5      * @param appSecret 公衆帳號的密鑰
 6      * @param code
 7      * @return WeixinAouth2Token
 8      */
 9     public static WeixinOauth2Token getOauth2AccessToken(String appId, String appSecret, String code) {
10         WeixinOauth2Token wat = null;
11         // 拼接請求地址
12         String requestUrl = "https://api.weixin.qq.com/sns/oauth2/access_token?appid=APPID&secret=SECRET&code=CODE&grant_type=authorization_code";
13         requestUrl = requestUrl.replace("APPID", appId);
14         requestUrl = requestUrl.replace("SECRET", appSecret);
15         requestUrl = requestUrl.replace("CODE", code);
16         // 獲取網頁受權憑證
17         JSONObject jsonObject = CommonUtil.httpsRequest(requestUrl, "GET", null);
18         if (null != jsonObject) {
19             try {
20                 wat = new WeixinOauth2Token();
21                 wat.setAccessToken(jsonObject.getString("access_token"));
22                 wat.setExpiresIn(jsonObject.getInt("expires_in"));
23                 wat.setRefreshToken(jsonObject.getString("refresh_token"));
24                 wat.setOpenId(jsonObject.getString("openid"));
25                 wat.setScope(jsonObject.getString("scope"));
26             } catch (Exception e) {
27                 wat = null;
28                 int errorCode = jsonObject.getInt("errcode");
29                 String errorMsg = jsonObject.getString("errmsg");
30                 log.error("獲取網頁受權憑證失敗 errcode:{} errmsg:{}", errorCode, errorMsg);
31             }
32         }
33         return wat;
34     }

使用此方法替換上相應的參數便可。由於微信受權登陸會跳轉兩次連接,因此當獲取成功則跳轉到wxLogin方法中進行驗證:

 1 String bassPath1 = WeChatConfig.URL+"weixin/wxyz";//定義的路徑
 2     @RequestMapping(value="wxLogin1",method=RequestMethod.GET)
 3     public ModelAndView wxLogin(HttpServletRequest request,HttpServletResponse response){
 4         String openId = (String) request.getSession().getAttribute("openId");//先從緩存中去拿openId
 5         
 6         if(openId==null){//若是沒有的話
 7             String url="https://open.weixin.qq.com/connect/oauth2/authorize?appid=" +  WeChatConfig.APP_ID +
 8                         "&redirect_uri=" + CommonUtil.urlEncodeUTF8(bassPath1)+
 9                         "&response_type=code" +
10                         "&scope=snsapi_userinfo" +
11                         "&state=STATE#wechat_redirect";
12             try {
13                 response.sendRedirect(url);
14             } catch (IOException e) {
15                 e.printStackTrace();
16             }
17                 
18         }else{
19             
20             return new ModelAndView("weixin/repairs");//返回頁面
21         }
22         return null;
23     }

至此,打開所須要的頁面,不管時第一次進入仍是刷新 都不會出現code been used這種狀況了,至少本人測試沒有出現過。

相關文章
相關標籤/搜索