微信公眾號開發之網頁授權登錄及code been used 解決!


首先微信公眾號開發網頁授權登錄使用環境:

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

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

微信網頁授權登錄首先以官方微信開發文檔為准,大體共分為4步:

先說第一步獲取code:

code說明:code作為換取access_token的票據,每次用戶授權帶上的code將不一樣,code只能使用一次,5扽這未被使用自動過期。

微信公眾開發文檔給的有獲取code的鏈接,建議直接復制來用,然后替換其中相應的參數即可。

鏈接為:

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這種情況了,至少本人測試沒有出現過。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM