微信公众号 网页授权登录及code been used解决详解
作者:咔咘奇諾 发布时间:2023-02-06 18:49:00
首先微信公众号开发网页授权登录使用环境:
开发工具: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实现方法:
/**
* 获取网页授权凭证
*
* @param appId 公众账号的唯一标识
* @param appSecret 公众账号的密钥
* @param code
* @return WeixinAouth2Token
*/
public static WeixinOauth2Token getOauth2AccessToken(String appId, String appSecret, String code) {
WeixinOauth2Token wat = null;
// 拼接请求地址
String requestUrl = "https://api.weixin.qq.com/sns/oauth2/access_token?appid=APPID&secret=SECRET&code=CODE&grant_type=authorization_code";
requestUrl = requestUrl.replace("APPID", appId);
requestUrl = requestUrl.replace("SECRET", appSecret);
requestUrl = requestUrl.replace("CODE", code);
// 获取网页授权凭证
JSONObject jsonObject = CommonUtil.httpsRequest(requestUrl, "GET", null);
if (null != jsonObject) {
try {
wat = new WeixinOauth2Token();
wat.setAccessToken(jsonObject.getString("access_token"));
wat.setExpiresIn(jsonObject.getInt("expires_in"));
wat.setRefreshToken(jsonObject.getString("refresh_token"));
wat.setOpenId(jsonObject.getString("openid"));
wat.setScope(jsonObject.getString("scope"));
} catch (Exception e) {
wat = null;
int errorCode = jsonObject.getInt("errcode");
String errorMsg = jsonObject.getString("errmsg");
log.error("获取网页授权凭证失败 errcode:{} errmsg:{}", errorCode, errorMsg);
}
}
return wat;
}
需要的参数为开发者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代码为:
/**
* 通过网页授权获取用户信息
*
* @param accessToken 网页授权接口调用凭证
* @param openId 用户标识
* @return SNSUserInfo
*/
public static SNSUserInfo getSNSUserInfo(String accessToken, String openId) {
SNSUserInfo snsUserInfo = null;
// 拼接请求地址
String requestUrl = "https://api.weixin.qq.com/sns/userinfo?access_token=ACCESS_TOKEN&openid=OPENID&lang=zh_CN";
requestUrl = requestUrl.replace("ACCESS_TOKEN", accessToken).replace("OPENID", openId);
// 通过网页授权获取用户信息
JSONObject jsonObject = CommonUtil.httpsRequest(requestUrl, "GET", null);
if (null != jsonObject) {
try {
snsUserInfo = new SNSUserInfo();
// 用户的标识
snsUserInfo.setOpenId(jsonObject.getString("openid"));
// 昵称
snsUserInfo.setNickname(jsonObject.getString("nickname"));
// 性别(1是男性,2是女性,0是未知)
snsUserInfo.setSex(jsonObject.getInt("sex"));
// 用户所在国家
snsUserInfo.setCountry(jsonObject.getString("country"));
// 用户所在省份
snsUserInfo.setProvince(jsonObject.getString("province"));
// 用户所在城市
snsUserInfo.setCity(jsonObject.getString("city"));
// 用户头像
snsUserInfo.setHeadImgUrl(jsonObject.getString("headimgurl"));
} catch (Exception e) {
snsUserInfo = null;
int errorCode = jsonObject.getInt("errcode");
String errorMsg = jsonObject.getString("errmsg");
log.error("获取用户信息失败 errcode:{} errmsg:{}", errorCode, errorMsg);
}
}
return snsUserInfo;
}
上面所述皆是根据微信公众号官网以及百度所写。另外还参考一篇很不错的微信公众号开发文档,可以说是带我入的门,给个链接:
下面说一下微信网页授权登录中遇到的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代码:
/**获取用户openId
* @throws IOException */
@RequestMapping(value = "/redirect", method = RequestMethod.GET)
public ModelAndView repairs(ModelAndView mav, HttpServletRequest request, HttpServletResponse resp) throws IOException{
String openId = (String) request.getSession().getAttribute("openId");//先从缓存中获取通过code得到的openID
System.out.println(openId);//测试输出openId
if(openId==null){//判断openId是否为空(判断code是否为第一次被使用)
RedirectUtils.redireUrl1(request, resp);//openid为空也就是code被第一次使用时跳转方法
return null;
}
mav.addObject("openId",openId);//没有被使用时
mav.setViewName("/weixin/repairs");//返回要跳转的视图页面
return mav;
}
RedirectUtils.redireUrl1(request, resp);为重定向跳转的路径。JAVA代码:
public static void redireUrl1(HttpServletRequest request,HttpServletResponse response){
System.out.println("跳转");//测试是否跳转过来了
String a="";
if(request.getQueryString()!=null){
a="?"+request.getQueryString();
}
String url = Base64.getBase64(request.getRequestURL()+a);//此为链接中带的一些参数 不需要可以不用写
System.out.println(request.getRequestURL()+a);
String basePath = WeChatConfig.URL+"weixin/wxyz?url="+url;//redirect_uri地址
System.out.println(basePath);
String urls="https://open.weixin.qq.com/connect/oauth2/authorize?appid=" + WeChatConfig.APP_ID+
"&redirect_uri=" + CommonUtil.urlEncodeUTF8(basePath)+
"&response_type=code" +
"&scope=snsapi_userinfo" +
"&state=STATE#wechat_redirect";
try {
response.sendRedirect(urls);//重定向执行url
}catch(Exception e){
e.printStackTrace();
}
}
其中Base64.getBase64为Base64加密方法,WeChatConfig.URL为你自己微信公众平台配置的安全域名,CommUtil.urlEncodeUTF8对重定向链接进行编码。
提供一下Base64方法中的加密解密方法,请先下载相应的jar包:
**
* Base64工具 CREATE 2016.12.14 form yjf
*
*/
public class Base64 {
/**
* Base64加密
*
*/
@SuppressWarnings("restriction")
public static String getBase64(String value) {
byte[] bytes = null;
String basevalue = null;
try {
bytes = value.getBytes("utf-8");
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
if (bytes != null) {
basevalue = new BASE64Encoder().encode(bytes);
}
return basevalue;
}
/**
* Base64解密
*
*/
@SuppressWarnings("restriction")
public static String getFromBase64(String basevalue) {
byte[] bytes = null;
String result = null;
if (basevalue != null) {
BASE64Decoder decoder = new BASE64Decoder();
try {
bytes = decoder.decodeBuffer(basevalue);
result = new String(bytes, "utf-8");
} catch (Exception e) {
e.printStackTrace();
}
}
return result;
}
}
然后时CommUtil.urlEncodeUTF8编码代码:
/**
* URL编码(utf-8)
*
* @param source
* @return
*/
public static String urlEncodeUTF8(String source) {
String result = source;
try {
result = java.net.URLEncoder.encode(source, "utf-8");
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
return result;
}
然后方法执行response.sendRedirect(urls);跳转回wxyz方法进行获取一系列参数,代码为:
@RequestMapping(value="/wxyz",method=RequestMethod.GET)
public ModelAndView wxYz(ModelAndView mvc,HttpServletRequest req,HttpServletResponse response){
System.out.println("微信验证");//测试是否跳转到此方法中
String code=req.getParameter("code");//获取url参数中的code
WeixinOauth2Token weixinOauth2Token = AdvancedUtil.getOauth2AccessToken(WeChatConfig.APP_ID, WeChatConfig.APP_SECRET, code);
if(weixinOauth2Token.getOpenId()!=null){
String openId = weixinOauth2Token.getOpenId();
req.getSession().setAttribute("openId",openId);//将获取到的openID存入session缓存中
System.out.println("openId"+openId);
mvc.addObject("openId",openId);
mvc.setViewName("redirect:/weixin/wxLogin");
return mvc;
}
return null;
}
其中AdvancedUtil.getOauth2AccessToken方法时获取网页授权access_token 方法,代码为:
/**
* 获取网页授权凭证
*
* @param appId 公众账号的唯一标识
* @param appSecret 公众账号的密钥
* @param code
* @return WeixinAouth2Token
*/
public static WeixinOauth2Token getOauth2AccessToken(String appId, String appSecret, String code) {
WeixinOauth2Token wat = null;
// 拼接请求地址
String requestUrl = "https://api.weixin.qq.com/sns/oauth2/access_token?appid=APPID&secret=SECRET&code=CODE&grant_type=authorization_code";
requestUrl = requestUrl.replace("APPID", appId);
requestUrl = requestUrl.replace("SECRET", appSecret);
requestUrl = requestUrl.replace("CODE", code);
// 获取网页授权凭证
JSONObject jsonObject = CommonUtil.httpsRequest(requestUrl, "GET", null);
if (null != jsonObject) {
try {
wat = new WeixinOauth2Token();
wat.setAccessToken(jsonObject.getString("access_token"));
wat.setExpiresIn(jsonObject.getInt("expires_in"));
wat.setRefreshToken(jsonObject.getString("refresh_token"));
wat.setOpenId(jsonObject.getString("openid"));
wat.setScope(jsonObject.getString("scope"));
} catch (Exception e) {
wat = null;
int errorCode = jsonObject.getInt("errcode");
String errorMsg = jsonObject.getString("errmsg");
log.error("获取网页授权凭证失败 errcode:{} errmsg:{}", errorCode, errorMsg);
}
}
return wat;
}
使用此方法替换上相应的参数即可。因为微信授权登录会跳转两次链接,所以当获取成功则跳转到wxLogin方法中进行验证:
String bassPath1 = WeChatConfig.URL+"weixin/wxyz";//定义的路径
@RequestMapping(value="wxLogin1",method=RequestMethod.GET)
public ModelAndView wxLogin(HttpServletRequest request,HttpServletResponse response){
String openId = (String) request.getSession().getAttribute("openId");//先从缓存中去拿openId
if(openId==null){//如果没有的话
String url="https://open.weixin.qq.com/connect/oauth2/authorize?appid=" + WeChatConfig.APP_ID +
"&redirect_uri=" + CommonUtil.urlEncodeUTF8(bassPath1)+
"&response_type=code" +
"&scope=snsapi_userinfo" +
"&state=STATE#wechat_redirect";
try {
response.sendRedirect(url);
} catch (IOException e) {
e.printStackTrace();
}
}else{
return new ModelAndView("weixin/repairs");//返回页面
}
return null;
}
至此,打开所需要的页面,无论时第一次进入还是刷新 都不会出现code been used这种情况了,至少本人测试没有出现过。
来源:https://www.cnblogs.com/ka-bu-qi-nuo/p/8920395.html


猜你喜欢
- 1.替换符的使用(1)在 app-android-defaultConfig (或者多渠道打包)下面可以这样使用android { &nbs
- 一、前言拷贝这个词想必大家都很熟悉,在工作中经常需要拷贝一份文件作为副本。拷贝的好处也很明显,相较于新建来说,可以节省很大的工作量。在Jav
- 前言本文将介绍通过Java编程在PDF文档中添加表格的方法。添加表格时,可设置表格边框、单元格对齐方式、单元格背景色、单元格合并、插入图片、
- 作为我最近一直在进行的一些编码访谈的一部分,有时会出现不变性问题。我自己并不过分教条,但每当不需要可变状态时,我会试图摆脱导致可变性的代码,
- 汉诺塔游戏一旦掌握了规律,其实是有点单调和无聊的,不过却是学习递归的一个绝佳例子,想当初学习老谭C的时候,就卡在这儿好长时间。对初学编程的人
- 本文简单分析了C/C++中常用函数的易错点,包括memset、sizeof、getchar等函数。分享给大家供大家参考之用。具体分析如下:1
- 本文实例为大家分享了Android实现类似微信视频接听的具体代码,供大家参考,具体内容如下1、背景需求:业务需要接入视频审核功能,在PC 端
- GraalVM安装GraalVM安装安装请前往GraalVM官网 下载 GraalVM Community 22.3,注意当前支持的Spri
- 苹果上的UI基本上都是这个效果,然而Android机上的顶部状态栏总是和app的主题颜色不搭。还好如今的api19以上的版本,我们也能做出这
- 前言AndroidStudio升级到3.0之后,gradle版本也随之升级到了3.0.0版本。当gradle插件升级到3.0.0及以上后,我
- 前言:最近公司C轮融资成功了,移动团队准备扩大一下,需要招聘Android开发工程师,陆陆续续面试了几位Android应聘者,面试过程中聊到
- 先看Demo运行效果SharedPreferences详解SharedPreferences是Android平台上一个轻量级的存储类,用来保
- Android通过scheme跳转界面,应该如何实现?需求通过后台返回链接地址eg: app://com.bobo.package/path
- spring cloud我想做成一个系列,所以spring cloud+eureka后面会慢慢说到的,有兴趣的小伙伴可以关注后续!这一节就简
- 在前面的文章<Mybatis配置之<properties>属性配置元素详述>,我们讲述了<properties
- 1、异常分类通常分为三类:系统异常(SystemException),业务异常(BusinessException)和其他异常(Except
- 一、前言目前大部分手机都是 60Hz 的刷新率,也就是 16.6ms 刷新一次,系统为了配合屏幕的刷新频率,将 Vsync 的周期也设置为
- 下面我通过代码为大家分享下C#模拟鼠标,具体内容如下:想必有很多人在项目开发中可能遇见需要做模拟鼠标点击的小功能,很多人会在百度过后采用mo
- 同类型对象的比较三个维度去比较同一性相等性相似性样例引入想象一下这样的一个场景:小王去图书馆借了一本java核心技术卷1,如图不幸的是小王把
- 实现Android studio设置自动导包及自动导包快捷键方式一:Android studio只有导单个包的快捷键:Alt+Enter。方