前言
微信公众号分为订阅号和服务号两种,两种公众号对权限,发送频率,显示的级别都不相同,用户可以根据需求选择适合的公众号种类,两者的区别这里不作叙述。
在整理文章的时候先查了一遍官网的资料,发现现在订阅号已经不能直接升级为服务号了(之前是有一次升级的机会的),所以在选择时请慎重!
正文
今天主要讲的是订阅号通过openid获取用户基本信息,通过微信开放平台的官方文档可以知道,服务号是可以直接通过oauth2.0进行网页授权获取用户基本信息,而认证的订阅号也有获取用户基本信息的权限,但是不同的是,订阅号并不是用通过服务号相同的通过网页授权的方式,订阅号需要用户主动触发才能获得,需要进行在订阅号中点击菜单或者发送信息等交互操作才可以获得。
好了,大致的流程图已经看完了,现在来写个例子,这里我直接用springboot来写:
首先,写一个CheckUtil,与微信服务端进行交互验证并获得授权,这里的token与微信公众平台的后台服务器配置一致即可:
import java.security.MessageDigest;
import java.util.Arrays;
public class CheckUtil {
private static final String token = "checkit";
public static boolean CheckSignature(String signature, String timestamp, String nonce) {
String[] arr = new String[] { token, timestamp, nonce };
// 排序
Arrays.sort(arr);
//
StringBuffer content = new StringBuffer();
for (int i = 0; i < arr.length; i++) {
content.append(arr[i]);
}
//
String temp = getSha1(content.toString());
return temp.equals(signature);
}
public static String getSha1(String str) {
if (null == str || 0 == str.length()) {
return null;
}
char[] hexDigits = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' };
try {
MessageDigest mdTemp = MessageDigest.getInstance("SHA1");
mdTemp.update(str.getBytes("UTF-8"));
byte[] md = mdTemp.digest();
int j = md.length;
char[] buf = new char[j * 2];
int k = 0;
for (int i = 0; i < j; i++) {
byte byte0 = md[i];
buf[k++] = hexDigits[byte0 >>> 4 & 0xf];
buf[k++] = hexDigits[byte0 & 0xf];
}
return new String(buf);
} catch (Exception e) {
return null;
}
}
}
定义用户基本信息的bean类:
public class UserDetail {
// {"subscribe":1,"openid":"o3UkH0vwlJlLhzQGxhP9Xh-1_NSQ","nickname":"MMY","sex":2,"language":"zh_CN","city":"深圳","province":"广东","country":"中国","headimgurl":"http:\/\/thirdwx.qlogo.cn\/mmopen\/lbkVE3rsT19JuuAg0lLp44rRJEU5m2KANfQxfx7KFlsibl9triazkqAIMz8AiaAp0lNyWjhXMXBBvbMXQiagP2ED9FWK30EuErDS\/132","subscribe_time":1536321291,"remark":"","groupid":0,"tagid_list":[],"subscribe_scene":"ADD_SCENE_QR_CODE","qr_scene":0,"qr_scene_str":""}
private String openid;
private String nickname;
private int sex;
private String city;
private String province;
private String country;
private String headimgurl;
private int subscribe;
private String subscribe_time;
private String subscribe_scene;
private String groupid;
private String tagid_list;
private String remark;
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 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 int getSubscribe() {
return subscribe;
}
public void setSubscribe(int subscribe) {
this.subscribe = subscribe;
}
public String getSubscribe_time() {
return subscribe_time;
}
public void setSubscribe_time(String subscribe_time) {
this.subscribe_time = subscribe_time;
}
public String getSubscribe_scene() {
return subscribe_scene;
}
public void setSubscribe_scene(String subscribe_scene) {
this.subscribe_scene = subscribe_scene;
}
public String getGroupid() {
return groupid;
}
public void setGroupid(String groupid) {
this.groupid = groupid;
}
public String getTagid_list() {
return tagid_list;
}
public void setTagid_list(String tagid_list) {
this.tagid_list = tagid_list;
}
public String getRemark() {
return remark;
}
public void setRemark(String remark) {
this.remark = remark;
}
@Override
public String toString() {
return "UserDetail [openid=" + openid + ", nickname=" + nickname + ", sex=" + sex + ", city=" + city
+ ", province=" + province + ", country=" + country + ", headimgurl=" + headimgurl + ", subscribe="
+ subscribe + ", subscribe_time=" + subscribe_time + ", subscribe_scene=" + subscribe_scene
+ ", groupid=" + groupid + ", tagid_list=" + tagid_list + ", remark=" + remark + "]";
}
}
定义一个WeixinUtil工具类:
import com.alibaba.fastjson.JSON;
import com.checkit.bean.*;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.HttpStatus;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.util.EntityUtils;
import org.dom4j.Document;
import org.dom4j.DocumentHelper;
import org.dom4j.Element;
import java.io.*;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
public class WeixinUtil {
private static final String APPID = "替换成自己的APPID";
private static final String APPSECRET = "替换成自己的SECRET";
//获取access_token的地址
private static final String ACCESS_TOKEN_URL = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=APPID&secret=APPSECRET";
//发送模板信息的地址
private static final String SEND_TEMPLATE_URL = "https://api.weixin.qq.com/cgi-bin/message/template/send?access_token=ACCESS_TOKEN";
//获取用户信息的地址
private static final String GET_USER_INFO = "https://api.weixin.qq.com/cgi-bin/user/info?access_token=ACCESS_TOKEN&openid=OPENID&lang=zh_CN";
/**
* 获取用户信息
*
* @param openId
* @return
*/
public static String getUserInfo(String openId) {
return httpGet(GET_USER_INFO.replace("ACCESS_TOKEN", getAcessToken()).replaceAll("OPENID", openId));
}
/**
* get
*
* @param url
* @return
*/
public static String doGetStr(String url) {
DefaultHttpClient httpClient = new DefaultHttpClient();
HttpGet httpGet = new HttpGet(url);
// JSONObject jsonObject = null;
String result = "";
try {
HttpResponse response = httpClient.execute(httpGet);
HttpEntity entity = response.getEntity();
if (entity != null) {
result = EntityUtils.toString(entity, "UTF-8");
// jsonObject = JSONObject.fromObject(result);
return result;
}
} catch (ClientProtocolException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return result;
}
public static String httpGet(String url) {
// get请求返回结果
String strResult = null;
try {
DefaultHttpClient client = new DefaultHttpClient();
// 发送get请求
HttpGet request = new HttpGet(url);
HttpResponse response = client.execute(request);
/** 请求发送成功,并得到响应 **/
if (response.getStatusLine().getStatusCode() == HttpStatus.SC_OK) {
/** 读取服务器返回过来的json字符串数据 **/
strResult = EntityUtils.toString(response.getEntity(), "UTF-8");
}
} catch (IOException e) {
e.printStackTrace();
}
return strResult;
}
/**
* post
*
* @param url
* @param outStr
* @return
*/
public static String doPostStr(String url, String outStr) {
DefaultHttpClient httpClient = new DefaultHttpClient();
HttpPost httpPost = new HttpPost(url);
// JSONObject jsonObject = null;
String result = "";
try {
httpPost.setEntity(new StringEntity(outStr, "UTF-8"));
HttpResponse response = httpClient.execute(httpPost);
result = EntityUtils.toString(response.getEntity(), "UTF-8");
// jsonObject = JSONObject.fromObject(result);
return result;
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return result;
}
/**
* access_token
*
* @return
*/
public static String getAcessToken() {
AcessToken token = new AcessToken();
FileOutputStream fos = null;
OutputStreamWriter osw = null;
BufferedWriter bw = null;
String tokenFtxt = "";
Calendar c;
String dir = System.getProperty("user.dir");
dir = dir + File.separator + "token.txt";
File file = new File(dir);
if (!file.exists()) {
try {
// System.out.println("【ACCESS_TOKEN】-->acess_token文件不存在");
String url = ACCESS_TOKEN_URL.replace("APPID", APPID).replace("APPSECRET", APPSECRET);
String jsonStr = doGetStr(url);
if (jsonStr != null) {
TokenResponse response = JSON.parseObject(jsonStr, TokenResponse.class);
token.setToken(response.getAccess_token());
token.setExpiresIn(response.getExpires_in());
}
fos = new FileOutputStream(dir);
osw = new OutputStreamWriter(fos, "UTF-8");
bw = new BufferedWriter(osw);
c = Calendar.getInstance();
// int date = c.get(Calendar.DATE);
// int hour = c.get(Calendar.HOUR_OF_DAY);
bw.write(token.getToken() + "\r\n");
bw.write(System.currentTimeMillis() + "\r\n");
// bw.write(date + "\r\n");
// bw.write(hour + "\r\n");
return token.getToken();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} finally {
try {
if (bw != null)
bw.close();
if (osw != null)
osw.close();
if (fos != null)
fos.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
} else {
BufferedReader bf = null;
try {
bf = new BufferedReader(new FileReader(file));
tokenFtxt = bf.readLine();
String last = bf.readLine();
// String date = bf.readLine();
// String hour = bf.readLine();
if (tokenFtxt.isEmpty()) {
// System.out.println("【ACCESS_TOKEN】-->acess_token文件为空");
file.delete();
String url = ACCESS_TOKEN_URL.replace("APPID", APPID).replace("APPSECRET", APPSECRET);
String jsonStr = doGetStr(url);
if (jsonStr != null) {
TokenResponse response = JSON.parseObject(jsonStr, TokenResponse.class);
token.setToken(response.getAccess_token());
token.setExpiresIn(response.getExpires_in());
}
fos = new FileOutputStream(dir);
osw = new OutputStreamWriter(fos, "UTF-8");
bw = new BufferedWriter(osw);
c = Calendar.getInstance();
// int date2 = c.get(Calendar.DATE);
// int hour2 = c.get(Calendar.HOUR_OF_DAY);
bw.write(token.getToken() + "\r\n");
bw.write(System.currentTimeMillis() + "\r\n");
// bw.write(date2 + "\r\n");
// bw.write(hour2 + "\r\n");
return token.getToken();
} else if (checkTimeOUt(last)) {
// System.out.println("【ACCESS_TOKEN】-->acess_token超时");
file.delete();
String url = ACCESS_TOKEN_URL.replace("APPID", APPID).replace("APPSECRET", APPSECRET);
String jsonStr = doGetStr(url);
if (jsonStr != null) {
TokenResponse response = JSON.parseObject(jsonStr, TokenResponse.class);
token.setToken(response.getAccess_token());
token.setExpiresIn(response.getExpires_in());
}
fos = new FileOutputStream(dir);
osw = new OutputStreamWriter(fos, "UTF-8");
bw = new BufferedWriter(osw);
c = Calendar.getInstance();
int date2 = c.get(Calendar.DATE);
int hour2 = c.get(Calendar.HOUR_OF_DAY);
bw.write(token.getToken() + "\r\n");
bw.write(System.currentTimeMillis() + "\r\n");
return token.getToken();
} else {
return tokenFtxt;
}
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
} finally {
try {
if (bf != null)
bf.close();
if (bw != null)
bw.close();
if (osw != null)
osw.close();
if (fos != null)
fos.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
return null;
}
private static boolean checkTimeOUt(String lastTime) {
long last = Long.parseLong(lastTime);
long current = System.currentTimeMillis();
SimpleDateFormat formatter = new SimpleDateFormat("yyyy年-MM月dd日-HH时mm分ss秒");
// System.out.println("【ACCESS_TOKEN】-->上次获取token时间"
// + formatter.format(new Date(last)));
// System.out.println("【ACCESS_TOKEN】-->当前获取token时间"
// + formatter.format(new Date(current)));
float during = (current - last) / 1000;
if (during > 1 * 60 * 60) {
// System.out.println("【ACCESS_TOKEN】-->during=" + during
// + "token已经超时");
return true;
}
// System.out.println("【ACCESS_TOKEN】-->during=" + during + "token未超时");
return false;
}
public static Map<String, String> xmlToMap(String str) throws Exception {
Map<String, String> map = new HashMap<String, String>();
if (str == null || str.isEmpty() || str.length() <= 0)
return null;
Document document = DocumentHelper.parseText(str);
Element root = document.getRootElement();
Iterator<?> list = root.elementIterator();
while (list.hasNext()) {
Element e = (Element) list.next();
map.put(e.getName(), e.getText());
}
return map;
}
}
接下来是controller层,定义访问接口:
import com.alibaba.fastjson.JSON;
import com.checkit.Service.WechatService;
import com.checkit.Utils.CheckUtil;
import com.checkit.Utils.WeixinUtil;
import com.checkit.bean.UserDetail;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpServletRequest;
@RestController
public class WechatController {
@Autowired
WechatService wechatService;
@GetMapping(value = "wechat")
public String getWechatGet(@RequestParam String signature, @RequestParam String timestamp,
@RequestParam String nonce, @RequestParam String echostr) {
System.out.println("接受到token验证");
return CheckUtil.CheckSignature(signature, timestamp, nonce) ? echostr : null;
}
@GetMapping(value = "/wechat/getUserInfo")
public String getUserInfo(@RequestParam String openId) {
String userInfo = WeixinUtil.getUserInfo(openId);
UserDetail userDetail = JSON.parseObject(userInfo, UserDetail.class);
System.out.println("userInfo imgUrl : " + userDetail.getHeadimgurl());
return userInfo;
}
}
打包完部署到接收微信server推送的服务器上,运行,然后直接在浏览器输入http://接收微信server推送的服务器IP/wechat/getUserInfo?openId=用户的openId,即可获得用户信息。
总结下来主要是几步:
1.建立自己服务器跟微信服务器的连接;
2.通过微信公众平台后台提供的APPID和SECRET获取access_token,注意access_token的过期时间;
3.用户通过与微信公众号交互将openid发送至自己的服务器,此处获取用户的openid;
4.通过将openid和access_token作为参数访问微信官方接口获取用户基本信息,根据自己的需求进行处理;
微信订阅号获取用户基本信息的流程今天就分享到这里,如有纰漏,欢迎指正,感谢~
By the way
有问题?可以给我留言或私聊
有收获?那就顺手点个赞呗~
当然,也可以到我的公众号下「6曦轩」,
回复“学习”,即可领取一份
【Java工程师进阶架构师的视频教程】~
回复“面试”,可以获得:
【本人呕心沥血整理的 Java 面试题】
回复“MySQL脑图”,可以获得
【MySQL 知识点梳理高清脑图】
由于我咧,科班出身的程序员,php,Android以及硬件方面都做过,不过最后还是选择专注于做 Java,所以有啥问题可以到公众号提问讨论(技术情感倾诉都可以哈哈哈),看到的话会尽快回复,希望可以跟大家共同学习进步,关于服务端架构,Java 核心知识解析,职业生涯,面试总结等文章会不定期坚持推送输出,欢迎大家关注~~~