• 微信公众号开发之网页授权登录及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这种情况了,至少本人测试没有出现过。

  • 相关阅读:
    mybatis中的缓存
    mybatis中的延迟加载
    mybatis中的ResultMap关联映射
    mubatis中为什么实体类要继承Serializable
    【经验总结-markdown】markdown字体和颜色设置
    【算法】动态规划
    【刷题-PAT】A1135 Is It A Red-Black Tree (30 分)
    【刷题-PAT】A1126 Eulerian Path (25 分)
    【刷题-PAT】A1119 Pre- and Post-order Traversals (30 分)
    【刷题-PAT】A1114 Family Property (25 分)
  • 原文地址:https://www.cnblogs.com/unidentified/p/8920395.html
Copyright © 2020-2023  润新知