微信公众号开发--自定义菜单跳转页面并获取用户信息 续

                       

之前写过一篇微信公众号开发–自定义菜单跳转页面并获取用户信息

由于当时是刚学习微信公众号开发当时的思路虽然可行,不过不是最好的,最近也用到了需要获取用户信息的地方,再次整理一下。

流程

这里写图片描述

注意点

###第一
如果用户在微信客户端中访问第三方网页,公众号可以通过微信网页授权机制,来获取用户基本信息,进而实现业务逻辑。

必须是在微信客户端

第二

通过code换取网页授权access_tokencode作为换取access_token的票据,每次用户授权带上的code将不一样,code只能使用一次,5分钟未被使用自动过期。
  
  • 1
  • 2
  • 3

在第二步中通过code换取网页授权access_token,这个access_token和其他接口中使用的access_token,不一样。

如果是静默授权,这里的access_token没什么用,反而是换取access_token时得到的openid非常有用

code只能用一次,注意这里的access_token和基础接口中的access_token不一样

实现步骤

第一步: 配置网页授权回调域名

开发 - 接口权限 - 网页服务 - 网页授权 - 网页授权获取用户基本信息

配置网页授权域名

我这个是测试号,花生壳做的域名

这里写图片描述

域名不需要配置http://开头,也不需要项目路径

第二步: 生成网页链接

这里有两种一种是scope为snsapi_base另一种是scope为snsapi_userinfo

snsapi_base (不弹出授权页面,直接跳转,只能获取用户openid)

snsapi_userinfo (弹出授权页面,可通过openid拿到昵称、性别、所在地。并且,即使在未关注的情况下,只要用户授权,也能获取其信息)

先看第一种

官方给的示例

https://open.weixin.qq.com/connect/oauth2/authorize?appid=wx520c15f417810387&redirect_uri=https%3A%2F%2Fchong.qq.com%2Fphp%2Findex.php%3Fd%3D%26c%3DwxAdapter%26m%3DmobileDeal%26showwxpaytitle%3D1%26vb2ctag%3D4_2030_5_1194_60&response_type=code&scope=snsapi_base&state=123#wechat_redirect
  
  • 1
  • 2
  • 3

前缀为

https://open.weixin.qq.com/connect/oauth2/authorize?appid=你的appId&redirect_uri=
  
  • 1

后缀为

&response_type=code&scope=snsapi_base&state=123#wechat_redirect
  
  • 1

顺序必能乱,中间是你的网址。网址还经过了转码处理,java使用java.net.URLEncoder提供的encode方法即可

好了,这里我们拼接一个我们的链接

http://gwchsk.imwork.net/myair/auth/user
  
  • 1

我们在这个地址下获取用户信息,encode之后为

http%3A%2F%2Fgwchsk.imwork.net%2Fmyair%2Fauth%2Fuser

然后将前缀和后缀都拼接好,不要忘了前缀中的appId

拼接好之后在浏览器输入,找个能生成二维码的浏览器,再扫描一下

一个简单的Controller如下,我们只是看第一步是否可以得到code

package com.airport.controller;import org.springframework.stereotype.Controller;import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.RequestMethod;@Controller@RequestMapping("/auth")public class WechatUserController { @RequestMapping(path = "/user", method = RequestMethod.GET) public String user(String code) {  System.out.println("得到的code:"+code);  return "user"; }}
  
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

用微信扫码之后就可以看到控制台输出了code

第三步:通过code换取网页授权access_token,这里会得到openId

通过code就可以换取access_token了,这里其实openId比access_token更有用

换取的结果

{"access_token":"0W71gcY7yvLBBL0JzxQC-sGJnBBOHcx95sBZqpWOTJpNm5C1Tm00UnE1MsNXaFb0JQmDwWHFtGi7vkuHT0ohdf5a8w2FI-JXKYtOKe5ehSs","expires_in":7200,"refresh_token":"QhEl0tbQERkdD-7-JKFZfYh68wqx-Frrs1OnHv5Wv7CarYhDhtk2nATc_x2q5liPMs0Pdlh-FOgJgcIDy0d57wqa431BJ2NVjZKWyWA-hpQ","openid":"oJhxHv8xKWX6ZYo4aeqBhTko94jk","scope":"snsapi_base"}
  
  • 1

我写了一个方法

ExchangeCode2OpenId.java

package com.airport.util;import org.json.JSONException;import org.json.JSONObject;import org.slf4j.Logger;import org.slf4j.LoggerFactory;import com.airport.context.WechatConst;/** * 通过code换取openId *  * @author 程高伟 * * @date 2016年10月17日 下午7:05:34 */public class ExchangeCode2OpenId { private static Logger logger = LoggerFactory.getLogger(ExchangeCode2OpenId.class); public static String exchange(String code) {  String openid = "";  String appId = WechatConst.appId;  String appSecret = WechatConst.appSecret;  // 换取access_token 其中包含了openid  // 这里通过code换取的是一个特殊的网页授权access_token,与基础支持中的access_token(该access_token用于调用其他接口)不同。  String URL = "https://api.weixin.qq.com/sns/oauth2/access_token?appid=APPID&secret=SECRET&code=CODE&grant_type=authorization_code"    .replace("APPID", appId).replace("SECRET", appSecret).replace("CODE", code);  String jsonStr = HttpUtil.sendGet(URL);  logger.info("----------换取openid返回的结果:{}----------", jsonStr);  JSONObject jsonObj = new JSONObject(jsonStr);  try {   openid = jsonObj.getString("openid");  } catch (JSONException e) {   logger.info("----------换取openid发生了异常:{}----------", e.getMessage());  }  return openid; }}
  
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39

好了,有了openid,我们就可以调用基础接口(用户管理)中的获取用户信息方法了。

这里获取用户信息需要access_token,注意,这里的access_token不是上面通过code换取到的,而是要自己去获取。

获取地址

https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=APPID&secret=APPSECRET

这样就可以获取用户信息了。

整个流程就是这个样子。

自定义菜单获取用户信息

将上一步我们拼接的url作为view型按钮的url

这里如果生成自定义菜单

如果报40033错误,请参考

微信公众号开发–自定义菜单失败40033解决方案

自定义菜单创建好之后,就可以当用户点击自定义菜单的时候获取用户信息了。

具体的获取用户信息接口地址

@RequestMapping(value = "/user", method = RequestMethod.GET)public String user(String code, Model model, HttpSession session) { String openid = ExchangeCode2OpenId.exchange(code); WechatUser wechatUser = WechatUserUtil.getWechatUser(openid); if (null == wechatUser) {  model.addAttribute("title", "请从微信公众号进入");  return "notInWechat"; } System.out.println("wechatUser:" + wechatUser); session.setAttribute("wechatUser", wechatUser); model.addAttribute("wechatUser", wechatUser); model.addAttribute("title", "用户信息"); return "user";}
  
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

我们看一下效果

自定义菜单

这里写图片描述

点击后进入

这里写图片描述

点击最下面的按钮

这里写图片描述

确定后跳转到另一个页面

这里写图片描述

然后返回,仍然回到了用户信息页面

这里写图片描述

一切都很顺利

看一下打印输出

这里写图片描述

如果你也很正常,那恭喜你,你用的安卓设备

我们换成苹果的

再走一遍,先清空打印内容,再看一下iPhone操作的打印输出内容

这里写图片描述

iPhone会在点击返回的时候再请求一次,而上面我们说过code只能用一次,第二次已经不能用了,所以获取不到用户信息,结果导致出错

如果你用iPad,效果和安卓手机一样。

搞不清楚为什么苹果iPhone和iPad居然不一样。

好了,言归正传,如何解决这个问题,其实很简单,在第一次获取到用户信息后保存起来,具体存哪里根据你的喜好(session也好,缓存也好)

这里我将它放在session里。

献上关键代码

@RequestMapping(value = "/user", method = RequestMethod.GET)public String user(String code, Model model, HttpSession session) { WechatUser wechatUser = getWechatUser(code, session); if (null == wechatUser) {  model.addAttribute("title", "请从微信公众号进入");  return "notInWechat"; } System.out.println("wechatUser:" + wechatUser); session.setAttribute("wechatUser", wechatUser); model.addAttribute("wechatUser", wechatUser); model.addAttribute("title", "用户信息"); return "user";}private WechatUser getWechatUser(String code, HttpSession session) { String openid = ""; WechatUser wechatUser = null; WechatUser wechatUserInSession = (WechatUser) session.getAttribute("wechatUser"); // 先看session if (null != wechatUserInSession) {  wechatUser = wechatUserInSession;  System.out.println("session中有用户信息"); } else {// session中没有 去获取  System.out.println("得到的code:" + code);  if (StringUtils.isBlank(code)) {   return null;  }else{   openid = ExchangeCode2OpenId.exchange(code);   if (StringUtils.isBlank(openid)) {    return null;   }  }  System.out.println("得到的openid:" + openid);  // 这里使用的是普通接口(用户管理,获取用户基本信息)  wechatUser = WechatUserUtil.getWechatUser(openid);  String headImgurl = wechatUser.getHeadimgurl();  // 132*132的头像  if (StringUtils.isNotBlank(wechatUser.getHeadimgurl())) {   headImgurl = headImgurl.substring(0, headImgurl.length() - 1);   headImgurl += "132";   wechatUser.setHeadimgurl(headImgurl);  } else {   headImgurl = "/assets/img/default_head.png";  }  wechatUser.setHeadimgurl(headImgurl);  System.out.println("得到的用户信息:" + wechatUser); } return wechatUser;}
  
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49

还有一种就是需要用户点击同意后再获取用户信息,就不写了,用到了再说,思路都是一样的。

WechatUserUtil.java

import java.io.UnsupportedEncodingException;import org.apache.commons.lang3.StringUtils;import org.slf4j.Logger;import org.slf4j.LoggerFactory;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.stereotype.Component;import com.google.gson.Gson;import com.google.gson.JsonSyntaxException;@Componentpublic class WechatUserUtil { @Autowired private AccessTokenUtil accessToken; private final Logger logger = LoggerFactory.getLogger(this.getClass()); public WechatUser getWechatUser(String openid) {  String token = accessToken.getAccessToken();  String URL = "https://api.weixin.qq.com/cgi-bin/user/info?access_token=ACCESS_TOKEN&openid=OPENID&lang=zh_CN";  String jsonResult = HttpUtil.sendGet(URL.replace("OPENID", openid).replace("ACCESS_TOKEN", token));  logger.info("----------获取到的用户信息,{}----------", jsonResult);  Gson gson = new Gson();  WechatUser wechatUser = null;  try {   wechatUser = gson.fromJson(new String(jsonResult.getBytes("ISO-8859-1"), "UTF-8"), WechatUser.class);   // 错误的openId   if (StringUtils.isBlank(wechatUser.getOpenid())) {    wechatUser = null;   }  } catch (JsonSyntaxException e) {   logger.info("----------解析json出错----------");   e.printStackTrace();  } catch (UnsupportedEncodingException e) {   e.printStackTrace();  }  return wechatUser; }}
  
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44

WechatUser.java

packagecom.jrbac.model.wechat;importjava.io.Serializable;/** * 微信用户详细信息 *  * @author GWCheng * */publicclassWechatUserimplementsSerializable{ /**  * 用户的真实姓名,绑定后的名称  */ privateString name; /**  *   */ privatestaticfinallongserialVersionUID =8170898780424513965L; /**  * 用户是否订阅该公众号标识,值为0时,代表此用户没有关注该公众号,拉取不到其余信息。  */ privateintsubscribe; /**  * 用户的标识,对当前公众号唯一  */ privateString openid; /**  * 用户的昵称  */ privateString nickname; /**  * 用户的性别,值为1时是男性,值为2时是女性,值为0时是未知  */ privateintsex; /**  * 用户的语言,简体中文为zh_CN  */ privateString language; /**  * 用户所在城市  */ privateString city; /**  * 用户所在省份  */ privateString province; /**  * 用户所在国家  */ privateString country; /**  * 用户头像,最后一个数值代表正方形头像大小(有0、46、64、96、132数值可选,0代表640*640正方形头像),用户没有头像时该项为空。  * 若用户更换头像,原有头像URL将失效。  */ privateString headimgurl; /**  * 用户关注时间,为时间戳。如果用户曾多次关注,则取最后关注时间  */ privatelongsubscribe_time; /**  * 只有在用户将公众号绑定到微信开放平台帐号后,才会出现该字段。详见:获取用户个人信息(UnionID机制)  */ privateString unionid; /**  * 公众号运营者对粉丝的备注,公众号运营者可在微信公众平台用户管理界面对粉丝添加备注  */ privateString remark; /**  * 用户所在的分组ID  */ privatelonggroupid; publicString getName(){  returnname; } publicvoidsetName(String name){  this.name =name; } publicintgetSubscribe(){  returnsubscribe; } publicvoidsetSubscribe(intsubscribe){  this.subscribe =subscribe; } publicString getOpenid(){  returnopenid; } publicvoidsetOpenid(String openid){  this.openid =openid; } publicString getNickname(){  returnnickname; } publicvoidsetNickname(String nickname){  this.nickname =nickname; } publicintgetSex(){  returnsex; } publicvoidsetSex(intsex){  this.sex =sex; } publicString getLanguage(){  returnlanguage; } publicvoidsetLanguage(String language){  this.language =language; } publicString getCity(){  returncity; } publicvoidsetCity(String city){  this.city =city; } publicString getProvince(){  returnprovince; } publicvoidsetProvince(String province){  this.province =province; } publicString getCountry(){  returncountry; } publicvoidsetCountry(String country){  this.country =country; } publicString getHeadimgurl(){  returnheadimgurl; } publicvoidsetHeadimgurl(String headimgurl){  this.headimgurl =headimgurl; } publiclonggetSubscribe_time(){  returnsubscribe_time; } publicvoidsetSubscribe_time(longsubscribe_time){  this.subscribe_time =subscribe_time; } publicString getUnionid(){  returnunionid; } publicvoidsetUnionid(String unionid){  this.unionid =unionid; } publicString getRemark(){  returnremark; } publicvoidsetRemark(String remark){  this.remark =remark; } publiclonggetGroupid(){  returngroupid; } publicvoidsetGroupid(longgroupid){  this.groupid =groupid; } @Override publicString toString(){  return"WechatUser{"+    "name='"+name +'\''+    ", subscribe="+subscribe +    ", openid='"+openid +'\''+    ", nickname='"+nickname +'\''+    ", sex="+sex +    ", language='"+language +'\''+    ", city='"+city +'\''+    ", province='"+province +'\''+    ", country='"+country +'\''+    ", headimgurl='"+headimgurl +'\''+    ", subscribe_time="+subscribe_time +    ", unionid='"+unionid +'\''+    ", remark='"+remark +'\''+    ", groupid="+groupid +    '}'; }}
  
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 149
  • 150
  • 151
  • 152
  • 153
  • 154
  • 155
  • 156
  • 157
  • 158
  • 159
  • 160
  • 161
  • 162
  • 163
  • 164
  • 165
  • 166
  • 167
  • 168
  • 169
  • 170
  • 171
  • 172
  • 173
  • 174
  • 175
  • 176
  • 177
  • 178
  • 179
  • 180
  • 181
  • 182
  • 183
  • 184
  • 185
  • 186
  • 187
  • 188
  • 189
  • 190
  • 191
  • 192
  • 193
  • 194
  • 195
  • 196
  • 197
  • 198
  • 199
  • 200
  • 201
  • 202
  • 203
  • 204
  • 205
  • 206
  • 207
  • 208

发送http请求的HttpUtil获取accessToken的AccessTokenUtiljson字符串到对象的转换

参考文献

微信官方文档–网页授权

微信官方文档–获取用户基本信息(UnionID机制)

微信公众号开发–自定义菜单跳转页面并获取用户信息

微信公众号开发–自定义菜单失败40033解决方案

微信公众号开发–获取用户信息中文乱码的解决方案

           
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值