最近,对微信公众号有点兴趣,就自己研究了研究里面的一些内容,发现还挺有意思的,而且通过微信公众号可以调用一些比较有意思的接口,就比如百度开发服务平台点击进入里面的很有接口,就比较常见的翻译,语音识别,地理位置等等,都挺好的。好了,不多说,进入正题好了。
我想,做微信公众号开发的,对于想获取关注了公众号的用户信息,或者说是当前与后台服务器进行交互的当前用户信息,这个功能是很重要的吧。因为,通过这个,可以直接获取到当前的用户信息,而不需要,每次都是进行自己输入信息,这个只会让用户感觉到很不适。。。所以,为了解决这个需求,那咱们来研究研究,如何获取微信的个人信息~!
(一)思路
我们来研究一下,要想获取到个人信息,是如何一个流程呢?
路线图:
上面,画了一个简单的一个流程图,我想,看着这个应该觉得不难吧。是的,确实思路很简单,但是,,里面的坑也不少。接下来,我对两种情况都进行讲解。
(二)情况一:通过用户与服务器进行消息交互,来获取用户信息
思路:针对这种情况的话,比较简单,因为,我们在做用户与服务器进行消息交互的时候,我们可以知道,用户发送的内容是以XML的形式进行发送的,然后服务器,首先接受到XML,然后再把XML转为Map对象,再从Map对象中获取对应的内容即可。那么,发送的XML的字段是个怎么样呢?
字段信息:
ToUserName:发送给谁的ID
FromUserName:发消息方的ID(其实也就是用户的OpenId)
CreateTime:消息发送时间,时间戳
MsgType:消息类似,有文本,图片,音频,视频,事件推送等
Content:发送的内容
通过这个,我想大家,再结合上面给的流程图,是不是发现了什么呢?是的,这个OpenId,我们已经获取了呀,那是不是可以直接获取到用户信息了呢?。。。。emmmmmm,这样说吧。差不多是可以的,那具体是怎么做呢?紧接着往下看,仔细看我其中的注释
步骤:
1:解析发送过来的XML信息格式,将其转为Map格式
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
|
/**
* XML格式转为map格式
* @param request
* @return
*/
public
static
Map<string string=
""
> xmlToMap(HttpServletRequest request){
Map<string string=
""
> map =
new
HashMap<string string=
""
>();
try
{
InputStream inputStream =
null
;
inputStream = request.getInputStream();
SAXReader reader =
new
SAXReader();
Document doc = reader.read(inputStream);
Element rootElement = doc.getRootElement();
List<element> elements = rootElement.elements();
for
(Element el:elements) {
map.put(el.getName() , el.getText());
}
inputStream.close();
return
map ;
}
catch
(Exception e) {
e.printStackTrace();
return
null
;
}
}</element></string></string></string>
|
备注:记得导入相应的包哦。。比如dom4j还有xsreader。。
2:获取用户的个人信息
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
|
package
com.hnu.scw.utils;
import
com.hnu.scw.model.AccessToken;
import
net.sf.json.JSONObject;
/**
* @author scw
* @create 2018-01-18 16:42
* @desc 用于获取微信用户的信息
**/
public
class
WeiXinUserInfoUtils {
private
static
final
String GET_USERINFO_URL =
"https://api.weixin.qq.com/cgi-bin/user/info?access_token=ACCESS_TOKEN&openid=OPENID&lang=zh_CN"
;
/**
* 获取微信用户账号的相关信息
* @param opendID 用户的openId,这个通过当用户进行了消息交互的时候,才有
* @return
*/
public
static
String getUserInfo(String opendID){
AccessToken accessToken = WeiXinUtils.getAccessToken();
//获取access_token
String token = accessToken.getToken();
String url = GET_USERINFO_URL.replace(
"ACCESS_TOKEN"
, token);
url = url.replace(
"OPENID"
,opendID);
JSONObject jsonObject = WeiXinUtils.doGetStr(url);
return
jsonObject.toString();
}
}
|
备注:传入的参数就是咱们之前说过的,FromUserName的值,这个应该不用多解释吧。因为,对于传入的XML,我们已经存入了Map中,那么直接从Map取出对应的字段信息就可以了,
1
|
String fromUserName = map.get(
"FromUserName"
);
|
3:通过上面,我们就得到了具体的用户信息的Json格式了,当然,我上面的方法将Json内容转为了String,我这里只是用于查看是否获取到信息了而已,所以,你们就根据各自的需求进行处理就可以了,该解析的就解析即可。。
总结:
上面的这种方法是不是很简单,这个没什么难的,而且这个根据微信公众号的开发手册也可以分析出来。
缺点:我们发现了,这种方法,只有当用户进行了消息交互,才会有FromUserName(这时候可以等价看成是OpenId),那么我们在实际开发中,肯定遇到了一种情况,就是用户没有进行消息交互,直接点击一个菜单按钮,然后就把用户信息自动显示到了一个页面中,那这样是怎么做,方法是一样吗?不多说,继续看下面~~~~~~~~~
(三)情况二:通过点击按钮,来直接获取到用户信息
思路:首先,点击菜单按钮,要先到网页授权的接口去请求(主要是获取Code,这是必须要的一个参数),然后再重定向到我们自己菜单按钮实际想去的URL,然后再获取OpenId,再通过OpenId,获取用户信息。。哇塞,咦,思路挺简单的嘛。。那么,我们开始工作。。
步骤:
1:创建菜单
对于这个自定义菜单的话,不是主要介绍的了,这个如果做过微信公众号开发的,应该都明白吧。所以,我这就贴一点关键代码:
1
2
3
4
5
|
ViewButton viewButton =
new
ViewButton();
viewButton.setName(
"view菜单"
);
viewButton.setType(
"view"
);
//viewButton.setUrl("http://myjava.ngrok.xiaomiqiu.cn/tomainpage");
|
分析:
知识点1:
1
|
https:
//open.weixin.qq.com/connect/oauth2/authorize?appid=XXXX
|
知识点2:
1
|
redirect_uri=http:
//myjava.ngrok.xiaomiqiu.cn/tologin/userinfo&response_type=code&scope=snsapi_userinfo&state=1#wechat_redirect");
|
2:配置微信公众号的网页授权域名
备注:这个是在自己微信公众号的开发管理模块里面的,而且,我这里用的是测试号进行的(当然,如果有企业号这更加好呀。界面也是一样)
注意点:对添加的回调页面域名:首先,不能加入http://这个,对于平常的链接,我想都有这个,这个是请求协议,但这里千万不能加入;另外,只需要写总的域名地址,不需要精确到最内层。打个比方:
一般的:
http://myjava.ngrok.xiaomiqiu.cn/tologin/userinfo
这里就配置就只需要:(其实就是配置我们的服务器域名即可)
myjava.ngrok.xiaomiqiu.cn
3:编写,相应的处理内容(关键代码,请仔细看)
备注:我用的是SSH(SpringMVC+Spring+Hibernate)框架来进行开发的,当然,用Servlet也可以,用SSM(SpringMVC+Spring+Mybatis)或者用SSH(Struts+Spring+Hibernate)都可以,这个根据自己的需求即可相应的改变呢!!(另外,这个几个框架,我其他的文章都有很详细的介绍了,所以自己看看相应配置即可)
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
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
|
package
com.hnu.scw.controller;
import
com.hnu.scw.bean.WeiXinUser;
import
com.hnu.scw.service.WeiXinUserInfoService;
import
com.hnu.scw.utils.WeiXinUtils;
import
org.springframework.beans.factory.annotation.Autowired;
import
org.springframework.stereotype.Controller;
import
org.springframework.web.bind.annotation.RequestMapping;
import
javax.servlet.http.HttpServletRequest;
import
javax.servlet.http.HttpSession;
import
java.util.HashMap;
import
java.util.Map;
/**
* @author scw
* @create 2018-01-18 17:47
* @desc 获取微信用户的所有信息,这个主要是为了不要用户自己填写个人信息
**/
@Controller
public
class
WeiXinUserInfoController {
@Autowired
private
WeiXinUserInfoService userService;
/**
* 进行网页授权,便于获取到用户的绑定的内容
* @param request
* @param session
* @param map
* @return
*/
@RequestMapping
(
"/tologin/userinfo"
)
public
String 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,必须的一个参数,这个是当进行了授权页面的时候,再重定向了我们自己的一个页面的时候,
* 会在request页面中,新增这个字段信息,要结合这个ProjectConst.Get_WEIXINPAGE_Code这个常量思考
*/
String code = request.getParameter(
"code"
);
try
{
//得到当前用户的信息(具体信息就看weixinUser这个javabean)
weiXinUser = getTheCode(session, code);
//将获取到的用户信息,放入到session中
session.setAttribute(
"currentUser"
, weiXinUser);
}
catch
(Exception e) {
e.printStackTrace();
}
}
map.put(
"weiXinUser"
, weiXinUser);
return
"hello"
;
}
/**
* 获取用户的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得到需要的授权信息
authInfo= userService.getAuthInfo(code);
//获取到openId
openId = authInfo.get(
"Openid"
);
}
// 获取基础刷新的接口访问凭证(目前还没明白为什么用authInfo.get("AccessToken");这里面的access_token就不行)
String accessToken = WeiXinUtils.getAccessToken().getToken();
//获取到微信用户的信息
WeiXinUser userinfo = userService.getUserInfo(accessToken ,openId);
return
userinfo;
}
}</string></string,>
|
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
|
package
com.hnu.scw.service;
import
com.hnu.scw.bean.WeiXinUser;
import
java.util.Map;
/**
* 用于进行微信用户个人信息的操作接口
*/
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);
}
</string,string></string>
|
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
|
package
com.hnu.scw.service.imp;
import
com.hnu.scw.bean.WeiXinUser;
import
com.hnu.scw.projectconst.ProjectConst;
import
com.hnu.scw.service.WeiXinUserInfoService;
import
com.hnu.scw.utils.WeiXinUtils;
import
net.sf.json.JSONObject;
import
org.springframework.stereotype.Service;
import
java.util.HashMap;
import
java.util.Map;
/**
* @author scw
* @create 2018-01-18 17:51
* @desc 用于获取微信用户的信息
**/
@Service
public
class
WeiXinUserInfoImlp
implements
WeiXinUserInfoService {
/**
* 获取微信用户的信息
* @param accessToken
* @param openId
* @return
*/
@Override
public
WeiXinUser getUserInfo(String accessToken, String openId) {
WeiXinUser weixinUserInfo =
null
;
// 拼接获取用户信息接口的请求地址
String requestUrl = ProjectConst.GET_WEIXIN_USER_URL.replace(
"ACCESS_TOKEN"
, accessToken).replace(
"OPENID"
, openId);
// 获取用户信息(返回的是Json格式内容)
JSONObject jsonObject = WeiXinUtils.doGetStr(requestUrl);
if
(
null
!= jsonObject) {
try
{
//封装获取到的用户信息
weixinUserInfo =
new
WeiXinUser();
// 用户的标识
weixinUserInfo.setOpenId(jsonObject.getString(
"openid"
));
// 昵称
weixinUserInfo.setNickname(jsonObject.getString(
"nickname"
));
// 用户的性别(1是男性,2是女性,0是未知)
weixinUserInfo.setSex(jsonObject.getInt(
"sex"
));
// 用户所在国家
weixinUserInfo.setCountry(jsonObject.getString(
"country"
));
// 用户所在省份
weixinUserInfo.setProvince(jsonObject.getString(
"province"
));
// 用户所在城市
weixinUserInfo.setCity(jsonObject.getString(
"city"
));
// 用户头像
weixinUserInfo.setHeadImgUrl(jsonObject.getString(
"headimgurl"
));
}
catch
(Exception e) {
if
(
0
== weixinUserInfo.getSubscribe()) {
System.out.println(
"用户并没有关注本公众号"
);
}
else
{
int
errorCode = jsonObject.getInt(
"errcode"
);
String errorMsg = jsonObject.getString(
"errmsg"
);
System.out.println(
"由于"
+errorCode +
"错误码;错误信息为:"
+errorMsg+
";导致获取用户信息失败"
);
}
}
}
return
weixinUserInfo;
}
/**
* 进行用户授权,获取到需要的授权字段,比如openId
* @param code 识别得到用户id必须的一个值
* 得到网页授权凭证和用户id
* @return
*/
@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.replace(
"APPID"
, appid).replace(
"SECRET"
, appsecret).replace(
"CODE"
, code);
//存储获取到的授权字段信息
Map<string, string=
""
> result =
new
HashMap<string, string=
""
>();
try
{
JSONObject OpenidJSONO = WeiXinUtils.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;
}
/**
* 获取到微信用户的唯一的OpendID
* @param code 这是要获取OpendId的必须的一个参数
* @return
*/
@Override
public
Map<string string=
""
> getAuthInfo(String code) {
//进行授权验证,获取到OpenID字段等信息
Map<string, string=
""
> result = oauth2GetOpenid(code);
// 从这里可以得到用户openid
String openId = result.get(
"Openid"
);
return
result;
}
}</string,></string></string,></string,></string,>
|
4:GET请求接口的代码:
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
|
package
com.hnu.scw.utils;
import
com.hnu.scw.menu.BaseButton;
import
com.hnu.scw.menu.ClickButton;
import
com.hnu.scw.menu.CustomeMenu;
import
com.hnu.scw.menu.ViewButton;
import
com.hnu.scw.model.AccessToken;
import
net.sf.json.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.client.methods.HttpPost;
import
org.apache.http.entity.StringEntity;
import
org.apache.http.impl.client.DefaultHttpClient;
import
org.apache.http.util.EntityUtils;
import
java.io.IOException;
import
java.io.UnsupportedEncodingException;
/**
* @author scw
* @create 2018-01-17 14:13
* @desc 用户获取access_token,众号调用各接口时都需使用access_token
**/
public
class
WeiXinUtils {
/**
* 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.fromObject(result);
}
}
catch
(ClientProtocolException e) {
e.printStackTrace();
}
catch
(IOException e) {
e.printStackTrace();
}
return
jsonObject;
}
|
5:获取Access_Token代码:(这个就是在4中的类中的方法)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
/**
* 获取access_token
* @return
*/
public
static
AccessToken getAccessToken(){
AccessToken accessToken =
new
AccessToken();
String url = ACCESS_TOKEN_URL.replace(
"APPID"
,APPID).replace(
"APPSECRET"
,APPSECRET);
JSONObject jsonObject = doGetStr(url);
if
(jsonObject !=
null
){
accessToken.setToken(jsonObject.getString(
"access_token"
));
accessToken.setExpireIn(jsonObject.getInt(
"expires_in"
));
}
return
accessToken;
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
package
com.hnu.scw.model;
/**
* @author scw
* @create 2018-01-17 14:35
* @desc 封装AccessToken的实体
**/
public
class
AccessToken {
private
String token;
private
int
expireIn;
public
String getToken() {
return
token;
}
public
void
setToken(String token) {
this
.token = token;
}
public
int
getExpireIn() {
return
expireIn;
}
public
void
setExpireIn(
int
expireIn) {
this
.expireIn = expireIn;
}
}
|
7:用户信息的实体类
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
|
package
com.hnu.scw.bean;
/**
* @author scw
* @create 2018-01-18 17:11
* @desc 对于微信用户本身存在的信息的一个javabean,不需要在数据库中进行处理
**/
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;
public
String getOpenId() {
return
openId;
}
public
void
setOpenId(String openId) {
this
.openId = openId;
}
public
int
getSubscribe() {
return
subscribe;
}
public
void
setSubscribe(
int
subscribe) {
this
.subscribe = subscribe;
}
public
String getSubscribeTime() {
return
subscribeTime;
}
public
void
setSubscribeTime(String subscribeTime) {
this
.subscribeTime = subscribeTime;
}
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 getCountry() {
return
country;
}
public
void
setCountry(String country) {
this
.country = country;
}
public
String getProvince() {
return
province;
}
public
void
setProvince(String province) {
this
.province = province;
}
public
String getCity() {
return
city;
}
public
void
setCity(String city) {
this
.city = city;
}
public
String getLanguage() {
return
language;
}
public
void
setLanguage(String language) {
this
.language = language;
}
public
String getHeadImgUrl() {
return
headImgUrl;
}
public
void
setHeadImgUrl(String headImgUrl) {
this
.headImgUrl = headImgUrl;
}
}
|
8:一些静态常量的接口地址:
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
|
package
com.hnu.scw.projectconst;
/**
* @author scw
* @create 2018-01-18 15:31
* @desc 项目相关的静态量
**/
public
class
ProjectConst {
/**
* 用于获取当前与微信公众号交互的用户信息的接口(一般是用第一个接口地址)
*/
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 GetPageUsersUrl =
"https://api.weixin.qq.com/sns/userinfo?access_token=ACCESS_TOKEN&openid=OPENID&lang=zh_CN"
;
/**
* 用于进行网页授权验证的接口URL,通过这个才可以得到opendID等字段信息
*/
public
final
static
String GET_WEBAUTH_URL =
"https://api.weixin.qq.com/sns/oauth2/access_token?appid=APPID&secret=SECRET&code=CODE&grant_type=authorization_code"
;
/**
* 用于进行当点击按钮的时候,能够在网页授权之后获取到code,再跳转到自己设定的一个URL路径上的接口,这个主要是为了获取之后于
* 获取openId的接口相结合
* 注意:参数:toselfURL 表示的是当授权成功后,跳转到的自己设定的页面,所以这个要根据自己的需要进行修改
*/
public
final
static
String Get_WEIXINPAGE_Code =
"https://open.weixin.qq.com/connect/oauth2/authorize?appid=APPID&redirect_uri=toselfURL&response_type=code&scope=snsapi_userinfo&state=1#wechat_redirect"
;
/**
* 获取access_token的URL
*/
private
static
final
String ACCESS_TOKEN_URL =
"https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=APPID&secret=APPSECRET"
;
}
|
9:大功告成。。。。哇塞,完美解决。。
总结:
这种方式,相对于上面的话,好处就在于,不需要进行消息交互就可以获取到用户的信息,这样其实更符合我们的业务需求,相对于更加好。但是,第一种情况也是有实用价值的,所以,我们要客观的进行评价和使用~~~~当然,这里面还有很多的可以优化的地方,就是,比如获取Access_Token,我们一般都是弄到本地,因为微信公众号一天请求的次数有限制(2000),所以这可以进行优化哦。。。。其他的根据需求来就可以啦!!!!!!!