官方参考: https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421140842&token=&lang=zh_CN
在关注者与公众号产生消息交互后,公众号可获得关注者的OpenID(加密后的微信号,每个用户对每个公众号的OpenID是唯一的。对于不同公众号,同一用户的openid不同)。公众号可通过本接口来根据OpenID获取用户基本信息,包括昵称、头像、性别、所在城市、语言和关注时间。
请注意,如果开发者有在多个公众号,或在公众号、移动应用之间统一用户帐号的需求,需要前往微信开放平台(open.weixin.qq.com)绑定公众号后,才可利用UnionID机制来满足上述需求。
UnionID机制说明:
开发者可通过OpenID来获取用户基本信息。特别需要注意的是,如果开发者拥有多个移动应用、网站应用和公众帐号,可通过获取用户基本信息中的unionid来区分用户的唯一性,因为只要是同一个微信开放平台帐号下的移动应用、网站应用和公众帐号,用户的unionid是唯一的。换句话说,同一用户,对同一个微信开放平台下的不同应用,unionid是相同的。
在微信里面, 点击链接, 然后进入微信授权, 授权后回调自己的代码
A: 先在html里面定义跳转链接, 这个跳转是带自己的appid进入微信
1 2 3 4 |
<a href="https://open.weixin.qq.com/connect/oauth2/authorize?appid=你的appid&redirect_uri=回调的地址&response_type=code&scope=snsapi_userinfo&state=STATE#wechat_redirect" style="font-size: 20px;"> 获取用户信息 </a> |
B: 进入这个页面之后, 显示微信授权, 如果授权成功, 传回code和state, 否则只传回state, 然后处理代码
utils
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 |
package com.pandy.framework.base.utils; import com.google.common.collect.Lists; import org.apache.commons.lang3.StringUtils; import org.apache.http.*; import org.apache.http.client.ClientProtocolException; import org.apache.http.client.ResponseHandler; import org.apache.http.client.entity.UrlEncodedFormEntity; import org.apache.http.client.methods.HttpGet; import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClients; import org.apache.http.message.BasicNameValuePair; import org.apache.http.protocol.BasicHttpContext; import org.apache.http.protocol.HttpContext; import org.apache.http.util.EntityUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.IOException; import java.util.HashMap; import java.util.List; import java.util.Map; /** * 项目名称: wp_idea_linux * 功能说明: * 创建者: Pandy, * 邮箱: panyongzheng@163.com, 1453261799@qq.com * 版权: * 官网: * 创建日期: 15-9-17. * 创建时间: 下午3:33. * 修改历史: * ----------------------------------------------- */ public class HttpClientUtils { private static final Logger logger = LoggerFactory.getLogger(HttpClientUtils.class); public static ResponseHandler<String> getStringResponseHandler(final String fromCharSet, final String toCharSet) { ResponseHandler<String> responseHandler = new ResponseHandler<String>() { @Override public String handleResponse(final HttpResponse response) throws ClientProtocolException, IOException {// int status = response.getStatusLine().getStatusCode(); if (status >= 200 && status < 300) { HttpEntity entity = response.getEntity(); if (StringUtils.isBlank(fromCharSet) && StringUtils.isBlank(fromCharSet)) { logger.debug("不使用任何编码"); return entity != null ? EntityUtils.toString(entity) : null; } else { logger.debug("使用编码: fromCharSet={}, toCharSet={}"); return new String(EntityUtils.toString(entity).getBytes(fromCharSet), toCharSet); } } else { throw new ClientProtocolException("返回异常, 状态: " + status); } } }; return responseHandler; } public static ResponseHandler<String> getStringResponseHandler() { return getStringResponseHandler("ISO_8859_1", "UTF-8"); } /** * TODO: 暂时无法解决乱码问题 * * @param url * @param domain * @param responseHandler * @param <T> * @return */ public static <T> T httpGet(String url, Object domain, ResponseHandler<T> responseHandler) { CloseableHttpClient httpclient = HttpClients.createDefault(); try { String str = null; if (domain != null) { String json = JSONUtil.toJSONString(domain); Map<String, Object> map = JSONUtil.parseObject(json, HashMap.class); if (map != null && map.size() > 0) { List<NameValuePair> params = Lists.newArrayList(); for (Map.Entry<String, Object> entry : map.entrySet()) { String key = entry.getKey(); Object value = entry.getValue(); params.add(new BasicNameValuePair(key, value.toString())); } str = EntityUtils.toString(new UrlEncodedFormEntity(params, Consts.UTF_8)); System.out.println(str); } } HttpGet httpGet = null; if (str != null) { httpGet = new HttpGet(url + "?" + str); } else { httpGet = new HttpGet(url); } HttpContext localContext = new BasicHttpContext(); T responseBody = httpclient.execute(httpGet, responseHandler, localContext); return responseBody; } catch (ClientProtocolException e) { e.printStackTrace(); } catch (ParseException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } finally { try { httpclient.close(); } catch (IOException e) { e.printStackTrace(); } } return null; } } |
service
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 |
/** * 获得OpenId * * @param code * @return */ @Transactional(rollbackFor = RuntimeException.class) public String getOpenIdByCode(String code) { Map<String, Object> jsonMap = getAccessTokenByCode(code); String openid = jsonMap.get("openid").toString(); return openid; } /** * 获得UnionId信息 * * @param accessToken * @param openid * @param lang * @return */ @Transactional(rollbackFor = RuntimeException.class) public Map<String, Object> getUnionIdInfoByCode(String accessToken, String openid, String lang) { String url = "https://api.weixin.qq.com/cgi-bin/user/info?access_token=" + accessToken + "&openid=" + openid + "&lang=" + lang; System.out.println("========================================================="); System.out.println(url); System.out.println("========================================================="); String json = HttpClientUtils.httpGet(url, null, HttpClientUtils.getStringResponseHandler()); System.out.println("========================================================="); System.out.println("获取用户UnionId信息:\n" + json); System.out.println("========================================================="); return JSONUtil.parseObject(json, HashMap.class); } @Transactional(rollbackFor = RuntimeException.class) public Map<String, Object> getUnionIdInfoCNByCode(String accessToken, String openid) { return getUnionIdInfoByCode(accessToken, openid, "zh_CN"); } @Transactional(rollbackFor = RuntimeException.class) public Map<String, Object> getUnionIdInfoENByCode(String accessToken, String openid) { return getUnionIdInfoByCode(accessToken, openid, "en"); } @Transactional(rollbackFor = RuntimeException.class) public boolean validateAccessToken(String accessToken, String openid) { String url = "https://api.weixin.qq.com/sns/auth?access_token=" + accessToken + "&openid=" + openid; System.out.println("========================================================="); System.out.println(url); System.out.println("========================================================="); String json = HttpClientUtils.httpGet(url, null, HttpClientUtils.getStringResponseHandler()); System.out.println("========================================================="); System.out.println("验证AccessToken信息:\n" + json); System.out.println("========================================================="); Map<String, Object> map = JSONUtil.parseObject(json, HashMap.class); String errcode = map.get("errcode") == null ? null : map.get("errcode").toString(); if ("0".equalsIgnoreCase(errcode)) { logger.debug("accessToken有效"); return true; } else { logger.debug("accessToken无效"); return false; } } /** * 第一步:用户同意授权,获取code * 在确保微信公众账号拥有授权作用域(scope参数)的权限的前提下(服务号获得高级接口后,默认拥有scope参数中的snsapi_base和snsapi_userinfo), * 引导关注者打开如下页面: * https://open.weixin.qq.com/connect/oauth2/authorize?appid=APPID&redirect_uri=REDIRECT_URI&response_type=code&scope=SCOPE&state=STATE#wechat_redirect * <p> * 通过code换取网页授权access_token * <p> * <p> * 返回的OpenID说明: * 公众号可获得关注者的OpenID(加密后的微信号,每个用户对每个公众号的OpenID是唯一的。 * 对于不同公众号,同一用户的openid不同)。 * 公众号可通过本接口来根据OpenID获取用户基本信息,包括昵称、头像、性别、所在城市、语言和关注时间。 * { "openid": "xxxxxxxxxxxxx", "access_token": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", "expires_in": 7200, "refresh_token": "xxxxxxxxxxxxxxxxxxxxx", "scope": "snsapi_userinfo," } * @param code * @return */ @Transactional(rollbackFor = RuntimeException.class) public Map<String, Object> getAccessTokenByCode(String code) { String url = "https://api.weixin.qq.com/sns/oauth2/access_token?appid=" + appid + "&secret=" + secret + "&code=" + code + "&grant_type=authorization_code"; System.out.println("========================================================="); System.out.println(url); System.out.println("========================================================="); String json = HttpClientUtils.httpGet(url, null, HttpClientUtils.getStringResponseHandler()); System.out.println("========================================================="); System.out.println("获取openid:\n" + json); System.out.println("========================================================="); return JSONUtil.parseObject(json, HashMap.class); } /** * 刷新access_token(如果需要) { "openid": "xxxxxxxxxxxxxxxxxxxxxxx", "access_token": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", "expires_in": 7200, "refresh_token": "y-eDL0-GGhB-8gU-xxxxxxxxxxxxxxxxxxxxxxxxxx", "scope": "snsapi_base,snsapi_userinfo," } * @param refreshToken * @return */ @Transactional(rollbackFor = RuntimeException.class) public Map<String, Object> resetAccessToken(String refreshToken) { String url = "https://api.weixin.qq.com/sns/oauth2/refresh_token?appid=" + appid + "&grant_type=refresh_token&refresh_token=" + refreshToken; System.out.println("========================================================="); System.out.println(url); System.out.println("========================================================="); String json = HttpClientUtils.httpGet(url, null, HttpClientUtils.getStringResponseHandler()); System.out.println("========================================================="); System.out.println("刷新Token:\n" + json); System.out.println("========================================================="); return JSONUtil.parseObject(json, HashMap.class); } @Transactional(rollbackFor = RuntimeException.class) public Map<String, Object> getUserInfo(String accessToken, String openid, String lang) { String url = "https://api.weixin.qq.com/sns/userinfo?access_token=" + accessToken + "&openid=" + openid + "&lang=" + lang; System.out.println("========================================================="); System.out.println(url); System.out.println("========================================================="); String json = HttpClientUtils.httpGet(url, null, HttpClientUtils.getStringResponseHandler()); System.out.println("========================================================="); System.out.println("获取用户信息:\n" + json); System.out.println("========================================================="); return JSONUtil.parseObject(json, HashMap.class); } /** * 拉取用户信息(需scope为 snsapi_userinfo) * * @param accessToken * @param openid * @return */ @Transactional(rollbackFor = RuntimeException.class) public Map<String, Object> getUserInfoEN(String accessToken, String openid) { return getUserInfo(accessToken, openid, "en"); } /** * 拉取用户信息(需scope为 snsapi_userinfo) * * @param accessToken * @param openid * @return */ @Transactional(rollbackFor = RuntimeException.class) public Map<String, Object> getUserInfoCN(String accessToken, String openid) { String url = "https://api.weixin.qq.com/sns/userinfo?access_token=" + accessToken + "&openid=" + openid + "&lang=zh_CN"; return getUserInfo(accessToken, openid, "zh_CN"); } |
controller: 业务处理, 这个可以放到service, 或者其他地方, 我只是测试, 所以在controller里面
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 |
@RequestMapping(value = "/authorizeSuccess.do") public ModelAndView authorizeSuccess(@RequestParam("code") String code, @RequestParam("state") String state, HttpServletRequest request, HttpServletResponse response) { ModelAndView view = new ModelAndView("h5/examples/authorizeSuccess"); view.addObject("code", code); view.addObject("state", state); Map<String, Object> jsonMap = null; String accessToken = null; String openid = null; String refreshToken = null; //这些信息, 可以保存在自己的数据库, 每次连接, 先验证accessToken的有效性, 再判断是否去获取, 免得每次都去微信服务器验证麻烦 accessToken = "Z8oQjp6NfnduR7zHJe5NYhJxHYDq08kmgPo45RsisWHSqwp_MDvOg2pl5Pqn5oTZLy9zJIDowIs_Tcek6FdAoIz74KjMGrrxWpMmaRqjUWk"; openid = "owLhfuJIEm3z8RUiNtfXtOrE_SnE"; refreshToken = "y-eDL0-GGhB-8gU-1JMOLDvzD2a9Js4dfGtXnBKbwUfur9lScMqtPBYjijy5LAnNwS53TPgpSyTtLSgogSb_bAInPpL1Ab_7ydY70sypeTI"; if (!weiXinService.validateAccessToken(accessToken, openid)) { System.out.println("获取新的AccessToken"); jsonMap = weiXinService.getAccessTokenByCode(code); accessToken = jsonMap.get("access_token").toString(); openid = jsonMap.get("openid").toString(); refreshToken = jsonMap.get("refresh_token").toString(); } else { System.out.println("有效, 同时刷新AccessToken的有效期"); jsonMap = weiXinService.resetAccessToken(refreshToken); } //获得unionId的中文信息 jsonMap = weiXinService.getUnionIdInfoCNByCode(accessToken, openid); //获得unionId的英文信息 jsonMap = weiXinService.getUnionIdInfoENByCode(accessToken, openid); //获得userInfo的中文信息 jsonMap = weiXinService.getUserInfoEN(accessToken, openid); //获得userInfo的英文信息 jsonMap = weiXinService.getUserInfoCN(accessToken, openid); view.addAllObjects(jsonMap); return view; } |