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

版权声明:本文为博主原创文章,遵循 CC 4.0 by-sa 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/frankcheng5143/article/details/52858904

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

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

流程

这里写图片描述

注意点

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

必须是在微信客户端

第二

通过code换取网页授权access_token

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

在第二步中通过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%3DwxAdap
ter%26m%3DmobileDeal%26showwxpaytitle%3D1%26vb2ctag%3D4_2030_5_1194_60&response_type=code&scope=snsapi_bas
e&state=123#wechat_redirect

前缀为

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

后缀为

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

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

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

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

我们在这个地址下获取用户信息,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";
	}
}

用微信扫码之后就可以看到控制台输出了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"}

我写了一个方法

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;
	}
}

好了,有了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";
}

我们看一下效果

自定义菜单

这里写图片描述

点击后进入

这里写图片描述

点击最下面的按钮

这里写图片描述

确定后跳转到另一个页面

这里写图片描述

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

这里写图片描述

一切都很顺利

看一下打印输出

这里写图片描述

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

我们换成苹果的

再走一遍,先清空打印内容,再看一下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;
}

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

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;

@Component
public 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;
	}

}

WechatUser.java

package com.jrbac.model.wechat;

import java.io.Serializable;

/**
 * 微信用户详细信息
 * 
 * @author GWCheng
 *
 */
public class WechatUser implements Serializable {

	/**
	 * 用户的真实姓名,绑定后的名称
	 */
	private String name;

	/**
	 * 
	 */
	private static final long serialVersionUID = 8170898780424513965L;
	/**
	 * 用户是否订阅该公众号标识,值为0时,代表此用户没有关注该公众号,拉取不到其余信息。
	 */
	private int subscribe;
	/**
	 * 用户的标识,对当前公众号唯一
	 */
	private String openid;
	/**
	 * 用户的昵称
	 */
	private String nickname;
	/**
	 * 用户的性别,值为1时是男性,值为2时是女性,值为0时是未知
	 */
	private int sex;
	/**
	 * 用户的语言,简体中文为zh_CN
	 */
	private String language;
	/**
	 * 用户所在城市
	 */
	private String city;
	/**
	 * 用户所在省份
	 */
	private String province;
	/**
	 * 用户所在国家
	 */
	private String country;
	/**
	 * 用户头像,最后一个数值代表正方形头像大小(有0、46、64、96、132数值可选,0代表640*640正方形头像),用户没有头像时该项为空。
	 * 若用户更换头像,原有头像URL将失效。
	 */
	private String headimgurl;
	/**
	 * 用户关注时间,为时间戳。如果用户曾多次关注,则取最后关注时间
	 */
	private long subscribe_time;
	/**
	 * 只有在用户将公众号绑定到微信开放平台帐号后,才会出现该字段。详见:获取用户个人信息(UnionID机制)
	 */
	private String unionid;
	/**
	 * 公众号运营者对粉丝的备注,公众号运营者可在微信公众平台用户管理界面对粉丝添加备注
	 */
	private String remark;
	/**
	 * 用户所在的分组ID
	 */
	private long groupid;

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public int getSubscribe() {
		return subscribe;
	}

	public void setSubscribe(int subscribe) {
		this.subscribe = subscribe;
	}

	public String getOpenid() {
		return openid;
	}

	public void setOpenid(String openid) {
		this.openid = openid;
	}

	public String getNickname() {
		return nickname;
	}

	public void setNickname(String nickname) {
		this.nickname = nickname;
	}

	public int getSex() {
		return sex;
	}

	public void setSex(int sex) {
		this.sex = sex;
	}

	public String getLanguage() {
		return language;
	}

	public void setLanguage(String language) {
		this.language = language;
	}

	public String getCity() {
		return city;
	}

	public void setCity(String city) {
		this.city = city;
	}

	public String getProvince() {
		return province;
	}

	public void setProvince(String province) {
		this.province = province;
	}

	public String getCountry() {
		return country;
	}

	public void setCountry(String country) {
		this.country = country;
	}

	public String getHeadimgurl() {
		return headimgurl;
	}

	public void setHeadimgurl(String headimgurl) {
		this.headimgurl = headimgurl;
	}

	public long getSubscribe_time() {
		return subscribe_time;
	}

	public void setSubscribe_time(long subscribe_time) {
		this.subscribe_time = subscribe_time;
	}

	public String getUnionid() {
		return unionid;
	}

	public void setUnionid(String unionid) {
		this.unionid = unionid;
	}

	public String getRemark() {
		return remark;
	}

	public void setRemark(String remark) {
		this.remark = remark;
	}

	public long getGroupid() {
		return groupid;
	}

	public void setGroupid(long groupid) {
		this.groupid = groupid;
	}

	@Override
	public String 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 +
				'}';
	}
}

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

参考文献

微信官方文档–网页授权

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

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

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

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

展开阅读全文

没有更多推荐了,返回首页