微信公众号开发之获取微信用户详细信息

1、由来以及项目环境

之所以写这个是因为,在工作当中突然就收到消息要做一个抽奖的活动,需要获取用户信息,再次就记录一下获取微信用户信息的整个流程,以及代码分享一下。
这个项目是使用spring boot的前后分离项目,然后剩下的就不具体细说了。按照惯例先把联系方式填写下qq704273004

2、讲述一下获取微信用户信息的原理或者说途径

在此讲一些原理和接口也是参考别人然后加上自己理解,使用的时候最好也自己理解一下。获取用户信息根据微信公众号的文档的流程来的,具体公众号的地址就不贴出来了 读者自行寻找就好了,大概获得用户信息的方法有三种,如有理解不对或是错误的地方之后再更改。

获取用户信息主要分为2中方式,一种是静默授权一种是用户感知授权,这其中主要区别是调用接口和是否弹出授权页面,以及获取用户信息使用的接口不一样。
个人理解静默授权需要关注公众号,用户感知授权可以不需要用户关注公号就取到用户信息。
同时是用户感知授权对应的授权是方式网页授权,静默授权的授权方式是全局授权,最大的区别是两个access_token的区别。

关于Access_token的异同:

有效期:两者有效时间都是7200s。
使用范围:通过网页授权获得的access_token,只能获取到对应的微信用户信息,与微信用户是一对一关系;
					而普通的access_token在有效期内可以使用,可以获取所有用户信息。
次数限制:普通access_token每天获取最多次数为2000次,而网页授权的access_token获取次数没有限制。

(引用博客连接)详细区别请点击

3、具体的使用方法及调用接口

方法一:使用全局的Access Token获取用户基本信息**
  • 用户关注及恢复小时的时候,获取用户的OpenId
  • 使用全局获取access_token接口获取
    https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=APPID&secret=APPSECRET
  • 在使用全局access_token获取OpenId的详细信息
    https://api.weixin.qq.com/cgi-bin/user/info?access_token=ACCESS_TOKEN&openid=OPENID&lang=zh_CN
方法二:通过网页授权弹出授权页面获取用户基本信息**
  • 微信公众号配置回调域名

  • 构造url请求将回调指向自己服务器的接口或者页面,例如:

    https://open.weixin.qq.com/connect/oauth2/authorize?appid=APPID&redirect_uri=REDIRECT_URL&response_type=code&scope=snsapi_userinfo&state=1#wechat_redirect 
    

    参数解释:APPID微信公众号的APPID;REDIRECT_URL重定向页面这个URL一般指向自己的服务器;scope=snsapi_userinfo该参数表示授权的作用域是请求用户信息
    注意:scope的参数有两种一种是snsapi_base该参数只能获取OpenId并且不会弹出授权页面;snsapi_userinfo参数会弹出授权页面,并且可以用于取用户信息

  • REDIRECT_URL回调页面
    当请求成功的时候会重定向到回调页面(接口),此时的URL将会增加俩参数一个是code一个是state。例如:
    http://xxxx.xxxx.cn/om/wx/getUserInfo?code=CODE&state=1

  • 使用code调用网页授权的获取access_token接口取得access_token
    https://api.weixin.qq.com/sns/oauth2/access_token?appid=APPID&secret=APPSECRET&code=CODE&grant_type=authorization_code
    调用之后回返回OpenId以及网页授权的access_token

  • 使用access_token和openId获取用户信息
    https://api.weixin.qq.com/sns/userinfo?access_token=ACCESS_TOKEN&openid=OPENID&lang=zh_CN

    这样便去的了用户信息,我采用的方案便是这种,改方案的好处使用网页授权无需关心用户是否关注。

    注意:到此大家肯定发现了方法一和方法二获取用户信息的接口不一样;
    对于这两个接口要给大家说明一下:方法一中的获取用户信息接口有先决条件首先用户需要关注公众号,然后接口中的ACCESS_TOKEN必须是全局的!!!
    方法二当中的:不需要用户关注,只需要授权即可,ACCESS_TOKEN是网页授权的次数没有限制

方法三:通过网页授权不弹出授权页面获得用户基本信息

个人感觉该方法就是上面两种方法的综合体,该方法有一定局限性,需要用户关注!!!

  • 微信公众号配置回调域名
  • 网页授权请求构造(静默)
    https://open.weixin.qq.com/connect/oauth2/authorize?appid=APPID&redirect_uri=REDIRECT_URL&response_type=code&scope=snsapi_base&state=1#wechat_redirect
    和方法二的区别就是scope=snsapi_base作用域变成不弹出授权页面只进行跳转,只获取用户的OpenId
  • REDIRECT_URL回调页面
    当请求成功的时候会重定向到回调页面(接口),此时的URL将会增加俩参数一个是code一个是state。例如:
    http://xxxx.xxxx.cn/om/wx/getUserInfo?code=CODE&state=1
    取到code
  • 获取全局的Access Token(下面的步骤就和方法一种的一样了)
    https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=APPID&secret=APPSECRET
  • 在使用全局access_token获取OpenId的详细信息
    https://api.weixin.qq.com/cgi-bin/user/info?access_token=ACCESS_TOKEN&openid=OPENID&lang=zh_CN
注意点:

对添加的回调页面域名:首先,不能加入http:// ,只需要填写域名;另外,只需要写总的域名地址,不需要精确到最内层。
https的回调好像有问题,尽量使用http吧,这个我没有深入。

4、代码的实现(暂时只对方法2进行了实现)

上面的原理都已经讲过了在这里可能只贴代码不进行具体的讲解了

Controller层代码:
package com.jerei.ebase.modules.ommt.controller;

import com.baomidou.mybatisplus.mapper.EntityWrapper;
import com.jerei.ebase.common.result.Result;
import com.jerei.ebase.common.utils.RedisUtils;
import com.jerei.ebase.modules.ommt.entity.WeiXinUser;
import com.jerei.ebase.modules.ommt.entity.WxUserSignEntity;
import com.jerei.ebase.modules.ommt.service.WeiXinUserInfoService;
import com.jerei.ebase.modules.ommt.service.WxUserSignService;
import com.jerei.ebase.modules.ommt.util.ProjectConst;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.view.RedirectView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import java.util.HashMap;
import java.util.Map;

/**
 * @author 柚子
 * @Description: om-meeting-pro
 * @Title: 重定向之后调取的url
 * @Created by zhongshuiayou on 2019/4/25 16:36
 */
@RestController
@RequestMapping("/wx")
public class WeiXinUserInfoController {
	private static Logger logger = LoggerFactory.getLogger(WeiXinUserInfoController.class);
	@Autowired
	private WeiXinUserInfoService userService;

	@Autowired
	private WxUserSignService wxUserSignService;
	/**
	 * 进行网页授权,便于获取到用户的绑定的内容
	 * @param request
	 * @param session
	 * @param map
	 * @return
	 */
	@RequestMapping("/getUserInfo")
	public ModelAndView check(HttpServletRequest request , HttpSession session, Map<String, Object> map) {
		//首先判断一下session中,是否有保存着的当前用户的信息,有的话,就不需要进行重复请求信息
		//这个地方作用主要是一次授权可以维持一定时间不需要再次授权
		WeiXinUser  weiXinUser = null ;
		if(session.getAttribute("currentUser") != null){
			weiXinUser = (WeiXinUser) session.getAttribute("currentUser");
		}else {
			/**
			 * 进行获取openId,必须的一个参数,这个是当进行了授权页面的时候,再重定向了我们自己的一个页面的时候
			 */
			String code = request.getParameter("code");
			try {
				//得到当前用户的信息(具体信息就看weixinUser这个javabean)
				weiXinUser = getTheCode(session, code);
				logger.info(weiXinUser.toString());
				WxUserSignEntity wxUserSignEntity = wxUserSignService.selectOne(new EntityWrapper<WxUserSignEntity>().eq("open_id",weiXinUser.getOpenId()));
				if (wxUserSignEntity==null|| wxUserSignEntity.getOpenId()==null) {
					//保存用户信息的业务代码
					wxUserSignEntity = new WxUserSignEntity();
					wxUserSignEntity.setOpenId(weiXinUser.getOpenId());
					wxUserSignEntity.setNickname(weiXinUser.getNickname());
					wxUserSignEntity.setHeadImgUrl(weiXinUser.getHeadImgUrl());
					wxUserSignEntity.setCity(weiXinUser.getCity());
					wxUserSignEntity.setLanguage(weiXinUser.getLanguage());
					wxUserSignEntity.setSex(weiXinUser.getSex());
					wxUserSignEntity.setProvince(weiXinUser.getProvince());
					wxUserSignEntity.setCountry(weiXinUser.getCountry());
					wxUserSignService.insert(wxUserSignEntity);
				}else{
					//返回参与活动成功之后的页面
					return new ModelAndView(new RedirectView(ProjectConst.Get_WEIXINPAGE_Code));
				}
				//将获取到的用户信息,放入到session中
				System.out.println(weiXinUser.toString());
				session.setAttribute("currentUser", weiXinUser);
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
		map.put("weiXinUser", weiXinUser);
		return new ModelAndView(new RedirectView(ProjectConst.Get_WEIXINPAGE_Code));
	}
	/**
	 * 获取用户的openId
	 * @param session
	 * @param code
	 * @return 返回封装的微信用户的对象
	 */
	private WeiXinUser getTheCode(HttpSession session, String code) {
		Map<String , String> authInfo = new HashMap<>();
		String openId = "";
		if (code != null)
		{
			// 调用根据用户的code得AccessToken 已经openId
			authInfo= userService.getAuthInfo(code);
			//获取到openId
			openId = authInfo.get("Openid");
		}
		//获取到微信用户的信息
		WeiXinUser userinfo = userService.getUserInfo(authInfo.get("AccessToken") ,openId);

		return userinfo;
	}
}

Service层接口:
package com.jerei.ebase.modules.ommt.service;

import com.jerei.ebase.modules.ommt.entity.WeiXinUser;

import java.util.Map;

/**
 * @author 柚子
 * @Description: om-meeting-pro
 * @Title: 获取微信用户信息service接口
 * @Created by zhongshuiayou on 2019/4/25 13:59
 */
public interface WeiXinUserInfoService {
	/**
	 * 获取到微信个人用户的信息
	 * @param accessToken
	 * @param openId
	 * @return
	 */
	WeiXinUser getUserInfo(String accessToken, String openId);

	/**
	 *用于获取网页授权后的信息字段,其中主要是获取openId
	 * @param code  授权码
	 * @return
	 */
	Map<String , String > getAuthInfo(String code);

	/**
	 * 进行网页授权的认证
	 * @param code 授权码
	 * @return
	 */
	Map<String,String> oauth2GetOpenid(String code);
}

Service实现层:
package com.jerei.ebase.modules.ommt.service.impl;

import com.alibaba.fastjson.JSONObject;
import com.jerei.ebase.modules.ommt.entity.WeiXinUser;
import com.jerei.ebase.modules.ommt.service.WeiXinUserInfoService;
import com.jerei.ebase.modules.ommt.util.ProjectConst;
import com.jerei.ebase.modules.ommt.util.WxUtils;
import org.springframework.stereotype.Service;

import java.util.HashMap;
import java.util.Map;

/**
 * @author 柚子
 * @Description: om-meeting-pro
 * @Title: 获取用户信息实现层
 * @Created by zhongshuiayou on 2019/4/25 14:01
 */

@Service
public class WeiXinUserInfoImlp implements WeiXinUserInfoService {
	@Override
	public WeiXinUser getUserInfo(String accessToken, String openId) {
		WeiXinUser weixinUserInfo = null;
		// 拼接获取用户信息接口的请求地址access_token=ACCESS_TOKEN&openid=OPENID&lang=zh_CN
		String requestUrl = ProjectConst.GET_PAGEUSERS_URL+"access_token="+accessToken+"&openid="+openId+"&lang=zh_CN";
		// 获取用户信息(返回的是Json格式内容)
		JSONObject jsonObject = WxUtils.doGetStr(requestUrl);

		if (null != jsonObject) {
			try {
				//封装获取到的用户信息
				weixinUserInfo = new WeiXinUser();
				// 用户的标识
				weixinUserInfo.setOpenId(jsonObject.getString("openid"));
				// 昵称
				weixinUserInfo.setNickname(WxUtils.filterEmoji(jsonObject.getString("nickname")));
				// 用户的性别(1是男性,2是女性,0是未知)
				weixinUserInfo.setSex(jsonObject.getInteger("sex"));
				// 用户所在国家
				weixinUserInfo.setCountry(jsonObject.getString("country"));
				// 用户所在省份
				weixinUserInfo.setProvince(jsonObject.getString("province"));
				// 用户所在城市
				weixinUserInfo.setCity(jsonObject.getString("city"));
				// 用户头像
				weixinUserInfo.setHeadImgUrl(jsonObject.getString("headimgurl"));
			} catch (Exception e) {
				//这个地方可以去掉不会出现这种问题(原文博主是用地三种方式取的,我已经将其修改成为第二种)
				//可以只留着else里面的错误信息
				//if (0 == weixinUserInfo.getSubscribe()) {
				//	System.out.println("用户并没有关注本公众号");
				//} else {
					int errorCode = jsonObject.getInteger("errcode");
					String errorMsg = jsonObject.getString("errmsg");
					System.out.println("由于"+errorCode +"错误码;错误信息为:"+errorMsg+";导致获取用户信息失败");
				//}
			}
		}
		return weixinUserInfo;
	}

	@Override
	public Map<String, String> getAuthInfo(String code) {
		//进行授权验证,获取到OpenID字段等信息包括AccessToken
		Map<String, String> result = oauth2GetOpenid(code);
		// 从这里可以得到用户openid
		String openId = result.get("Openid");

		return result;

	}

	@Override
	public Map<String, String> oauth2GetOpenid(String code) {
		//自己的配置appid(公众号进行查阅)
		String appid = ProjectConst.PROJECT_APPID;
		//自己的配置APPSECRET;(公众号进行查阅)
		String appsecret = ProjectConst.PROJECT_APPSECRET;
		//拼接用户授权接口信息
		String requestUrl = ProjectConst.GET_WEBAUTH_URL+"appid="+appid+"&secret="+appsecret+"&code="+code+"&grant_type=authorization_code";
		//存储获取到的授权字段信息
		Map<String, String> result = new HashMap<String, String>();
		try {
			//这个地方用户自行判断是否要保存该信息(此处主要实现的功能是通过网页授权获取openId,access_token)
			JSONObject OpenidJSONO = WxUtils.doGetStr(requestUrl);
			//OpenidJSONO可以得到的内容:access_token expires_in  refresh_token openid scope
			String Openid = String.valueOf(OpenidJSONO.get("openid"));
			String AccessToken = String.valueOf(OpenidJSONO.get("access_token"));
			//用户保存的作用域
			String Scope = String.valueOf(OpenidJSONO.get("scope"));
			String refresh_token = String.valueOf(OpenidJSONO.get("refresh_token"));
			result.put("Openid", Openid);
			result.put("AccessToken", AccessToken);
			result.put("scope", Scope);
			result.put("refresh_token", refresh_token);
		} catch (Exception e) {
			e.printStackTrace();
		}
		return result;
	}
}

实体类接口:

由于采用的是方法2的方式所以有一些字段是没有数据的,原文博客的博主是采用方法3取得因此在此我没有进行修改,基础字段是一样,我采用lombok因此没有get和set方法,需要的同学自行加上就好。

package com.jerei.ebase.modules.ommt.entity;

import lombok.Data;

/**
 * @author 柚子
 * @Description: om-meeting-pro
 * @Title: 获取微信用户信息实体
 * @Created by zhongshuiayou on 2019/4/25 8:26
 */
@Data
public class WeiXinUser {
	/**
	 * 用户的标识
	 */
	private String openId;
	/**
	 * 关注状态(1是关注,0是未关注),未关注时获取不到其余信息
	 */
	private int subscribe;
	/**
	 * 用户关注时间,为时间戳。如果用户曾多次关注,则取最后关注时间
	 */
	private String subscribeTime;
	/**
	 * 昵称
	 */
	private String nickname;
	/**
	 * 用户的性别(1是男性,2是女性,0是未知)
	 */
	private int sex;
	/**
	 * 用户所在国家
	 */
	private String country;
	/**
	 * 用户所在省份
	 */
	private String province;
	/**
	 * 用户所在城市
	 */
	private String city;
	/**
	 * 用户的语言,简体中文为zh_CN
	 */
	private String language;
	/**
	 * 用户头像
	 */
	private String headImgUrl;

	@Override
	public String toString() {
		return "WeiXinUser{" +
				"openId='" + openId + '\'' +
				", subscribe=" + subscribe +
				", subscribeTime='" + subscribeTime + '\'' +
				", nickname='" + nickname + '\'' +
				", sex=" + sex +
				", country='" + country + '\'' +
				", province='" + province + '\'' +
				", city='" + city + '\'' +
				", language='" + language + '\'' +
				", headImgUrl='" + headImgUrl + '\'' +
				'}';
	}
}
用到的工具类以及静态常量:
package com.jerei.ebase.modules.ommt.util;


import com.alibaba.fastjson.JSONObject;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.util.EntityUtils;

import java.io.IOException;

/**
 * @author 柚子
 * @Description: om-meeting-pro
 * @Title: 获取微信用户信息工具类
 * @Created by zhongshuiayou on 2019/4/25 13:49
 */
public class WxUtils {
	/**
	 * Get请求,方便到一个url接口来获取结果
	 * @param url
	 * @return
	 */
	public static JSONObject doGetStr(String url) {
		DefaultHttpClient defaultHttpClient = new DefaultHttpClient();
		HttpGet httpGet = new HttpGet(url);
		JSONObject jsonObject = null;
		try {
			HttpResponse response = defaultHttpClient.execute(httpGet);
			HttpEntity entity = response.getEntity();
			if (entity != null) {
				String result = EntityUtils.toString(entity, "UTF-8");
				jsonObject = JSONObject.parseObject(result);
				System.out.println(jsonObject.toJSONString());
			}
		} catch (ClientProtocolException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
		return jsonObject;
	}

	/**
	 * 检测是否有emoji字符
	 * @param source 需要判断的字符串
	 * @return 一旦含有就抛出
	 */
	public static boolean containsEmoji(String source) {
		int len = source.length();
		for (int i = 0; i < len; i++) {
			char codePoint = source.charAt(i);
			if (!notisEmojiCharacter(codePoint)) {
				//判断确认有表情字符
				return true;
			}
		}
		return false;
	}


	/**
	 * 非emoji表情字符判断
	 * @param codePoint
	 * @return
	 */
	private static boolean notisEmojiCharacter(char codePoint) {
		return (codePoint == 0x0) ||
				(codePoint == 0x9) ||
				(codePoint == 0xA) ||
				(codePoint == 0xD) ||
				((codePoint >= 0x20) && (codePoint <= 0xD7FF)) ||
				((codePoint >= 0xE000) && (codePoint <= 0xFFFD)) ||
				((codePoint >= 0x10000) && (codePoint <= 0x10FFFF));
	}

	/**
	 * 过滤emoji 或者 其他非文字类型的字符
	 * @param source  需要过滤的字符串
	 * @return
	 */
	public static String filterEmoji(String source) {
		if (!containsEmoji(source)) {
			//如果不包含,直接返回
			return source;
		}
		//该buf保存非emoji的字符
		StringBuilder buf = null;
		int len = source.length();
		for (int i = 0; i < len; i++) {
			char codePoint = source.charAt(i);
			if (notisEmojiCharacter(codePoint)) {
				if (buf == null) {
					buf = new StringBuilder(source.length());
				}
				buf.append(codePoint);
			}
		}

		if (buf == null) {
			//如果没有找到非emoji的字符,则返回无内容的字符串
			return "";
		} else {
			if (buf.length() == len) {
				buf = null;
				return source;
			} else {
				return buf.toString();
			}
		}
	}

}

/**
 * @author zhongshuiayou
 * @Description: om-meeting-pro
 * @Title: 静态常量(该静态常量当中基本列出了所有的常用获取微信用户信息接口,使用者按需取就可以了)
 * @Created by zhongshuiayou on 2019/4/25 13:25
 */
public class ProjectConst {
	//测试号 APPID 和APPSECRET
	public static final String PROJECT_APPID ="";
	public static final String PROJECT_APPSECRET ="";

	/**
	 * 用于获取当前与微信公众号交互的用户信息的接口(第一个接口需要用户关注第二个不需要)
	 */
	public static final String GET_WEIXIN_USER_URL = "https://api.weixin.qq.com/cgi-bin/user/info?access_token=ACCESS_TOKEN&openid=OPENID";
	public final static String GET_PAGEUSERS_URL = "https://api.weixin.qq.com/sns/userinfo?";

	/**
	 * 用于进行网页授权验证的接口URL,通过这个才可以得到opendID等字段信息(用户网页授权access_token 获取接口地址)
	 */
	public final static String GET_WEBAUTH_URL = "https://api.weixin.qq.com/sns/oauth2/access_token?";

	/**
	 * 这个主要是为了获取用户信息成功之后的跳转页面
	 * 注意:参数:REDIRECT_URL 表示的是当授权成功后,跳转到的自己设定的页面,所以这个要根据自己的需要进行修改
	 * 使用位置:是在控制器当中的modelAndView的返回中使用的
	 */
	public final static String Get_WEIXINPAGE_Code = "https://open.weixin.qq.com/connect/oauth2/authorize?appid=APPID&redirect_uri=REDIRECT_URL&response_type=code&scope=snsapi_userinfo&state=1#wechat_redirect";
	/**
	 * 获取access_token的URL(基础接口的token也叫全局access_token获取接口)
	 */
	public static final String ACCESS_TOKEN_URL = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=APPID&secret=APPSECRET";
}

文章在再这里基本上就结束了,这样就能获取到用户信息,至于实际当中用什么方法取要看业务需求,如果大家还有疑问可以通过qq联系我,谢谢。

5、上面实现代码参考

我也是参考了之前大神的文章写的,原博文当中的疑问我也已经搞明白,有不明白的可以咨询我。
原文地址:方法参考原文地址

  • 0
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值