当我们进行微信平台相关内容开发时,如果想做一些针对微信用户进行一些信息获取及消息推送,那我们第一步先要拿到微信用户的OPENID(用户唯一标识,建立在一个公众号与一个微信用户之间,即针对一个公众号,所有的微信用户都享有一个独立不变的标识);
1 . 如果微信用户已经关注自己的公众号, 那可以通过微信接口里面的用户管理模板直接获取到对应公众号的所有已关注用户OPENID的列表;
1). 在调用所有的微信接口之前,我们先要请求授权,拿到access_token
@Getter
@Config("wechat.appid")
private static String WECHATAPPID;
@Getter
@Config("wechat.appsecret")
private static String WECHATAPPSECRET;
public static final String ACCESS_TOKEN = "access_token";
public final static String WECHATACCESSTOKEN = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=";
/**
*
* 成功: {"access_token":"ACCESS_TOKEN","expires_in":7200}
*
*
* 失败: {"errcode":40013,"errmsg":"invalid appid"}
*
* @return 如果没有获取到有效的凭据,就返回 null
*/
private static Map getWechatAPPAccessTokenFromWeChat() {
StringBuilder accessTokenUrl = new StringBuilder().append(WECHATACCESSTOKEN).append(WECHATAPPID)
.append("&secret=").append(WECHATAPPSECRET);
Map result = HttpClientUtil.readHttpContent(accessTokenUrl.toString());
if (result != null && result.get(ACCESS_TOKEN) != null)
return result;
return null;
}
//HttpClientUtil.class static method
/**
* get json content and transform to Map from the urlStr
* If occuer one error just return null.
* @param urlStr
* @return
*/
public static Map readHttpContent(String urlStr){
Map result = null;
HttpEntity httpEntity = null;
try {
HttpResponse response = Request.Get(urlStr).execute().returnResponse();
httpEntity = response.getEntity();
}catch (IOException e) {
logger.error("Get resouse from {} had occurred an error: {}", urlStr, e.toString());
return result;
}
try (
InputStream in = httpEntity.getContent();
){
ObjectMapper mapper = new ObjectMapper();
result = mapper.readValue(in, HashMap.class);
}catch (IOException e) {
logger.error("Get json content from {} had occurred an error: {}", urlStr, e.toString());
return result;
}
return result;
}
2). 使用前面得到的access_token获取公众号的微信关注用户列表
public static final String ERRMSG = "errmsg";
public final static String WECHATUSERLISTURL = "https://api.weixin.qq.com/cgi-bin/user/get?access_token=";
/**
*
* 获取成功:{
* "total":23000,
* "count":10000,
* "data":{"
* openid":[
* "OPENID1",
* "OPENID2",
* ...,
* "OPENID10000"
* ]
* },
* "next_openid":"OPENID10000"
* }
*
*
*
* 获取失败:{"errcode":40013,"errmsg":"invalid appid"}
*
*
*
* 获取微信公众帐号的关注者列表(wechat user openID),每次最多10000个
*
* @param accessToken
* @param next_openid: 没有此参数,则默认从关注列表的第一个开始获取
* @return
*/
public static Map getWechatUserOpenIDList(String accessToken, String next_openid) {
StringBuilder urlB = new StringBuilder().append(WECHATUSERLISTURL).append(accessToken);
if (!Strings.isBlank(next_openid))
urlB.append("&next_openid=").append(next_openid);
Map result = HttpClientUtil.readHttpContent(urlB.toString());
if (result != null && result.get(ERRMSG) == null)
return result;
return null;
}
采用方法一,到此便得到了用户openID列表,可以进行后面获取微信用户信息及向用户推送模板消息等;
2 . 无论微信用户是否关注自己的公众号, 当微信用户访问我们的web站点时(通过微信内置浏览器),我们可以通过页面授权的方式拿到微信用户的OpendID(页面授权需要跳转到微信服务器拿到oauth_code然后再换取access_token及OpendID);
1). 为我们的web站点服务添加一个filter,拦截来自微信浏览器的请求,进行302跳转到微信站点,拿到授权码再回调回来;
public class WechatFilter implements Filter {
private static final Logger logger = LoggerFactory.getLogger(WechatFilter.class);
@Override
public void init(FilterConfig filterConfig) throws ServletException {}
@Override
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
HttpServletRequest httpRequest = (HttpServletRequest) request;
if (response.isCommitted()) {
chain.doFilter(request, response);
return;
}
//判断请求是否来自微信浏览器
if (!WebUtil.isWechatRequest(httpRequest)) {
chain.doFilter(request, response);
return;
}
String requestURL = WebUtil.getRequsetUrl(httpRequest, true, true);
logger.debug("Request from wechat client:{}", requestURL);
HttpSession session = httpRequest.getSession();
String weChatOpenId = (String) session.getAttribute(WeChatManager.WECHATOPENID);
if (weChatOpenId == null) {
if (httpRequest.getParameter(WeChatManager.CODE) == null) {
StringBuilder url = new StringBuilder("http://").append(WebUtil.getRequsetUrl(httpRequest, true, true));
response.reset();
((HttpServletResponse) response).sendRedirect(url.toString());
return;
} else {
String oauthCode = httpRequest.getParameter(WeChatManager.CODE);
if (!Strings.isBlank(oauthCode)) {
Map oauth_data = WeChatPageInterface.getWechatPageAccessToken(oauthCode);
weChatOpenId = oauthCode != null && oauth_data.get(WeChatManager.OPENID) != null ? (String) oauth_data.get(WeChatManager.OPENID) : null;
session.setAttribute(WeChatManager.WECHATOPENID, weChatOpenId);
} else {
logger.warn("The oauth_code is empty!");
}
}
}
chain.doFilter(request, response);
}
@Override
public void destroy() {
}
}
//WeChatPageInterface.class static method
/**
*
* 微信获取授权成功:{ "access_token":"ACCESS_TOKEN", "expires_in":7200, "refresh_token":"REFRESH_TOKEN",
* "openid":"OPENID", "scope":"SCOPE", "unionid": "o6_bmasdasdsad6_2sgVt7hMZOPfL" }
*
*
* 微信获取授权失败:{"errcode":40029,"errmsg":"invalid code"}
*
*
* 请求微信公众号的网页授权,得到用户OpenID和access_token
* 微信网页授权是通过OAuth2.0机制实现的,在用户授权给公众号后,公众号可以获取到一个网页授权特有的接口调用凭证( 网页授权access_token),
* 通过网页授权access_token可以进行授权后接口调用,如获取用户基本信息;
*
* @return 如果没有获取到有效的凭据,就返回 null
*/
public static Map getWechatPageAccessToken(String oauthCode) {
StringBuilder accessTokenUrl = new StringBuilder().append(WeChatManager.weChatAuthAccessTokenPage).append(WeChatManager.getWECHATAPPID())
.append("&secret=").append(WeChatManager.getWECHATAPPSECRET()).append("&code=")
.append(oauthCode).append("&grant_type=").append("authorization_code");
Map accessToken = HttpClientUtil.readHttpContent(accessTokenUrl.toString());
if (accessToken != null && accessToken.get(WeChatManager.ACCESS_TOKEN) != null) {
return accessToken;
}
return null;
}
至此,我们通过两种方式都可以获取微信用户的OpenID了,至于采用哪一种可以根据实际业务环境;
PS: OpenID是介于微信用户与公众账号之间恒定不变的,所以可以进行三方持久化存储;