关注公众号后,公众号可获得关注者的OpenID(加密后的微信号,每个用户对每个公众号的OpenID是唯一的。对于不同公众号,同一用户的openid不同)。公众号可通过OpenID获取用户基本信息,包括昵称、头像、性别、所在城市、语言和关注时间。
如果开发者有在多个公众号,或在公众号、移动应用之间统一用户帐号的需求,需要前往微信开放平台(open.weixin.qq.com)绑定公众号后,才可利用UnionID机制来满足上述需求。
一个微信用户对一个公众号OpenID 是唯一的。一个微信用户对同一个微信开放平台UnionID是唯一的。
获取OpenID前,先要获取code
在微信里面引导关注者打开如下页面(测试方便起见可以直接把url当成普通消息通过文件传输助手发到微信上,然后直接点这个链接,效果一样):
如果用户同意授权,页面将跳转至 redirect_uri?code=CODE&state=STATE 就这样code自动生成了。会带进重定向的url的后面参数中。
redirect_uri 是我们java项目提供的一个URL,接收两个参数:code 和 state。注意redirect_uri 需要URL编码。
例如,我现在提供的接口是 https://60cc9d13.ngrok.io/wx/api/userInfo
url 编码以后是 https%3a%2f%2f60cc9d13.ngrok.io%2fwx%2fapi%2fuserInfo
把这个url发到微信,点击,就可以直接访问了。下面有代码和tomcat日志。
根据code获取OpenId
访问
https://api.weixin.qq.com/sns/oauth2/access_token?appid=${APPID}&secret=${APPSECRET}&code=${CODE}&grant_type=authorization_code
返回包含openId的json对象。
获取access_token
访问
https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=${APPID}&secret=${APPSECRET}
返回包含accessToken的json对象
获取用户信息
访问
https://api.weixin.qq.com/cgi-bin/user/info?access_token=${ACCESS_TOKEN}&openid=${OPENID}&lang=zh_CN
返回用户信息,注意如果使用了开放平台,才有unionid。否则unionid为null.
代码demo,简单起见所有东西都放一个类里了.
不要乱用我的appId和SECRET
package com.tsing.wechat.controller;
import com.alibaba.fastjson.JSON;
import com.tsing.wechat.model.AccessToken;
import com.tsing.wechat.model.WeChatUserInfo;
import com.tsing.wechat.model.WebToken;
import com.tsing.wechat.utils.HttpUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.text.StrSubstitutor;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import java.util.HashMap;
import java.util.Map;
@Slf4j
@RestController
@RequestMapping("/api")
public class WxUserInfoController {
public static final String APP_ID = "APPID";
public static final String APP_ID_VALUE = "wx5087e91fe1430b1d";
public static final String APP_SECRET = "APPSECRET";
public static final String APP_SECRET_VALUE = "*****";
public static final String CODE = "CODE";
public static final String ACCESS_TOKEN = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=${APPID}&secret=${APPSECRET}";
private static final String ACCESS_TOKEN_CODE = "https://api.weixin.qq.com/sns/oauth2/access_token?appid=${APPID}&secret=${APPSECRET}&code=${CODE}&grant_type=authorization_code";
private static final String USER_INFO = "https://api.weixin.qq.com/cgi-bin/user/info?access_token=${ACCESS_TOKEN}&openid=${OPENID}&lang=zh_CN";
/**
* 根据code获取网页授权用户的openid
*/
@RequestMapping(value = "/userInfo")
public WeChatUserInfo getUserInfo(@RequestParam String code, @RequestParam String state) {
String openId = getWebAccessToken(APP_ID_VALUE, APP_SECRET_VALUE, code);
log.debug("根据code=" + code + "获取openId值为:" + openId);
String accessToken = getAccessToken(APP_ID_VALUE, APP_SECRET_VALUE);
WeChatUserInfo userInfo = getWeChatUserInfo(accessToken, openId);
log.info("用户信息:{}", userInfo);
return userInfo;
}
public WeChatUserInfo getWeChatUserInfo(String accessToken, String openId) {
Map<String, String> map = new HashMap<>();
map.put("ACCESS_TOKEN", accessToken);
map.put("OPENID", openId);
String usrInfoUrl = urlReplace(USER_INFO, map);
log.info("获取到的用户信息url:{}", usrInfoUrl);
String useStr = HttpUtils.doGet(usrInfoUrl);
log.info("获取到的用户信息为:{}", useStr);
return JSON.parseObject(useStr, WeChatUserInfo.class);
}
public String getWebAccessToken(String appId, String secret, String code) {
Map<String, String> map = new HashMap<>();
map.put(APP_ID, appId);
map.put(APP_SECRET, secret);
map.put(CODE, code);
String url = urlReplace(ACCESS_TOKEN_CODE, map);
log.info("获取网页token的URL:{}", url);
String res = HttpUtils.doGet(url);
log.info("获取网页token的返回:{}", res);
WebToken token = JSON.parseObject(res, WebToken.class);
return token.getOpenId();
}
public String getAccessToken(String appId, String secret) {
Map<String, String> map = new HashMap<>();
map.put(APP_ID, appId);
map.put(APP_SECRET, secret);
String url = urlReplace(ACCESS_TOKEN, map);
String res = HttpUtils.doGet(url);
log.info("获取token返回:{}", res);
AccessToken token = JSON.parseObject(res, AccessToken.class);
return token.getAccessToken();
}
/**
* 将URL中的模板参数用实际值替换
*
* @param url url
* @param parms 参数
* @return 替换参数后的url
*/
public static String urlReplace(String url, Map<String, String> parms) {
if (StringUtils.isNotBlank(url) && null != parms) {
StrSubstitutor strsub = new StrSubstitutor(parms);
url = strsub.replace(url);
}
return url;
}
}
日志,因为只是一个测试公众号,没有绑定微信开放平台,所以最后一行日志大的unionid=null
2018-10-16 10:23:04.467 INFO [http-nio-8080-exec-2]com.tsing.wechat.controller.WxUserInfoController.getWebAccessToken:71 -获取网页token的URL:https://api.weixin.qq.com/sns/oauth2/access_token?appid=wx5087e91fe1430b1d&secret=b6428efc6c87d5b8cd29bc757407d8e6&code=07113uKn1wh6si0TCtKn1YpsKn113uK7&grant_type=authorization_code
2018-10-16 10:23:04.669 INFO [http-nio-8080-exec-2]com.tsing.wechat.controller.WxUserInfoController.getWebAccessToken:73 -获取网页token的返回:{"access_token":"14_wDfP5QFeN-R8fhq8WgZRuapJGnQSz5lQrI4cOqc6CDAuqJvaeO-FBKJV6f2b6EEUVy-a3TNTBgBDky00M6okPA","expires_in":7200,"refresh_token":"14_mQTvjt1tglGSN82bqxa2XKqRmqdI0slBKGzLkOUlpzR-rPOIwrzMT3HWbgFp3US1zoafY5dF_I4O-yG2spvGgQ","openid":"o-7aj0hBDfVEzsK2P_F9U-IwosZ4","scope":"snsapi_userinfo"}
2018-10-16 10:23:04.670 DEBUG [http-nio-8080-exec-2]com.tsing.wechat.controller.WxUserInfoController.getUserInfo:47 -根据code=07113uKn1wh6si0TCtKn1YpsKn113uK7获取openId值为:o-7aj0hBDfVEzsK2P_F9U-IwosZ4
2018-10-16 10:23:04.854 INFO [http-nio-8080-exec-2]com.tsing.wechat.controller.WxUserInfoController.getAccessToken:84 -获取token返回:{"access_token":"14_xEbh6Hq5U0MXjAmlEUY88dcm5BrRB7lPnLgcgiIzZE9zkOsUsry1Ms57hKJGIqmXKwakgn10V4MqtGx_zCVrHW3HRpjCQiY6zji2itWpgvN3kNBS1NAJAUK5jNjpHTrLuOisDIIH24L5yZbHNITdAGAXSD","expires_in":7200}
2018-10-16 10:23:04.854 INFO [http-nio-8080-exec-2]com.tsing.wechat.controller.WxUserInfoController.getWeChatUserInfo:59 -获取到的用户信息url:https://api.weixin.qq.com/cgi-bin/user/info?access_token=14_xEbh6Hq5U0MXjAmlEUY88dcm5BrRB7lPnLgcgiIzZE9zkOsUsry1Ms57hKJGIqmXKwakgn10V4MqtGx_zCVrHW3HRpjCQiY6zji2itWpgvN3kNBS1NAJAUK5jNjpHTrLuOisDIIH24L5yZbHNITdAGAXSD&openid=o-7aj0hBDfVEzsK2P_F9U-IwosZ4&lang=zh_CN
2018-10-16 10:23:05.017 INFO [http-nio-8080-exec-2]com.tsing.wechat.controller.WxUserInfoController.getWeChatUserInfo:61 -获取到的用户信息为:{"subscribe":1,"openid":"o-7aj0hBDfVEzsK2P_F9U-IwosZ4","nickname":"_captain","sex":1,"language":"zh_CN","city":"杭州","province":"浙江","country":"中国","headimgurl":"http:\/\/thirdwx.qlogo.cn\/mmopen\/9RibwCfvUfQNnmNiarD2S8A8YNHC4IGOLgzSBRMaeyDSG0JBpmHXZLNOxqJypjnib8vFkDUCbn5IicLvL4ibDps5LvzAEZs5FjQiaib\/132","subscribe_time":1539600947,"remark":"","groupid":0,"tagid_list":[],"subscribe_scene":"ADD_SCENE_QR_CODE","qr_scene":0,"qr_scene_str":""}
2018-10-16 10:23:05.038 INFO [http-nio-8080-exec-2]com.tsing.wechat.controller.WxUserInfoController.getUserInfo:50 -用户信息:WeChatUserInfo(openid=o-7aj0hBDfVEzsK2P_F9U-IwosZ4, subscribe=1, subscribeTime=null, nickName=_captain, sex=1, country=中国, province=浙江, city=杭州, language=zh_CN, headImgUrl=http://thirdwx.qlogo.cn/mmopen/9RibwCfvUfQNnmNiarD2S8A8YNHC4IGOLgzSBRMaeyDSG0JBpmHXZLNOxqJypjnib8vFkDUCbn5IicLvL4ibDps5LvzAEZs5FjQiaib/132, unionid=null)
不要乱用我的appId和SECRET