网上搜资料时,网友都说官方文档太垃圾了不易看懂,如何如何的。现在个人整理了一个通俗易懂易上手的,希望可以帮助到刚接触微信接口的你。 请看流程图!看懂图,就懂了一半了。 其实整体流程大体只需三步:用户点击登录按钮(其实就是个链接) ---》 用户点击授权登录 ----》 实现获取用户信息代码。 然后获取用户信息代码只需三步:获取code ----》 通过code获取access_token和openId ---》 通过access_token和openId获取用户信息。
网上搜资料时,网友都说官方文档太垃圾了不易看懂,如何如何的。现在个人整理了一个通俗易懂易上手的,希望可以帮助到刚接触微信接口的你。
请看流程图!看懂图,就懂了一半了:
其实整体流程大体只需三步:用户点击登录按钮(其实就相当于一个链接) ---》 用户点击授权登录 ----》 实现获取用户信息代码。
然后获取用户信息代码只需三步:获取code ----》 通过code获取access_token和openId ---》 通过access_token和openId获取用户信息(包含union)。
以上便是整体套路,当然官网上也有,但具体如何实现呢?
不着急,咱们一步一步来!
第一步:微信登录按钮
它其实就是一个连接,不过想得到这个链接,有一点点麻烦。
1、设置。 微信公众平台---》接口权限---》网页授权---》修改 ---》设置网页授权域名(域名,不含http://),其实就是微信调你的java方法的项目路径或项目域名,如:www.zzff.net/pp ---》点击设置后弹出页面(大致意思,将MP_verify_31qRIDcjN8ZD1lVJ.txt放在你项目路径下面,如:www.ffzz.net/pp/MP_verify_31qRIDcjN8ZD1lVJ.txt 能访问到) ---》点击确认,授权回调页面域名设置成功!
2、拼链接。 https://open.weixin.qq.com/connect/oauth2/authorize?appid=xxx公众号IDxxxxx & redirect_uri = 授权回调页面域名/你的action(即微信授权后跳向的地址)
& response_type=code(固定的) & scope = snsapi_userinfo(或者snsapi_base默认授权) & state=STATE#wechat_redirect
如:https://open.weixin.qq.com/connect/oauth2/authorize?appid=wxf0e81c3bee622d60&redirect_uri=http%3A%2F%2Fnba.bluewebgame.com%2Foauth_response.php&response_type=code&scope=snsapi_userinfo&state=STATE#wechat_redirect
这个链接中参数的具体含义,官方解释如下:
参数 | 是否必须 | 说明 |
appid | 是 | 公众号的唯一标识 |
redirect_uri | 是 | 授权后重定向的回调链接地址,请使用urlencode对链接进行处理 |
response_type | 是 | 返回类型,请填写code |
scope | 是 | 应用授权作用域,snsapi_base (不弹出授权页面,直接跳转,只能获取用户openid),snsapi_userinfo (弹出授权页面,可通过openid拿到昵称、性别、所在地。并且,即使在未关注的情况下,只要用户授权,也能获取其信息) |
state | 否 | 重定向后会带上state参数,开发者可以填写a-zA-Z0-9的参数值,最多128字节 |
#wechat_redirect | 是 | 无论直接打开还是做页面302重定向时候,必须带此参数 |
第二步:授权确认登录
这一步最简单,第一步登录链接拼好后,在手机微信中打开,微信便会跳转到确认授权页面,点击确认授权即可。(这一步,不用开发者做处理!)
第三步:获取用户信息 (重点)
这一步便是真正的代码实现的地方。开篇便讲了,它只需三步:获取code ----》 通过code获取access_token和openId ---》 通过access_token和openId获取用户信息。
First: 获取code
Second: 获取网页授权access_token和openId
Third:通过access_token和openId获取用户信息。
关于union机制的细节:如果开发者需要公众号微信登录和APP微信登录共用一个微信ID,那个就需要union机制了。其实很简单,只需在微信开放平台(open.weixin.qq.com)绑定公众号,获取用户信息时就会返回union字段。
具体代码实现为:实体 ---- 方法 --- 工具
实体Oauth2Token:
1 package com.wfcm.wxUitls;
2
3 /**
4 * 类名: WeixinOauth2Token </br>
5 * 描述: 网页授权信息 </br>
6 * 创建时间: 2015-11-27 </br>
7 * 发布版本:V1.0 </br>
8 */
9 public class Oauth2Token {
10 // 网页授权接口调用凭证
11 private String accessToken;
12 // 凭证有效时长
13 private int expiresIn;
14 // 用于刷新凭证
15 private String refreshToken;
16 // 用户标识
17 private String openId;
18 // 用户授权作用域
19 private String scope;
20
21 public String getAccessToken() {
22 return accessToken;
23 }
24
25 public void setAccessToken(String accessToken) {
26 this.accessToken = accessToken;
27 }
28
29 public int getExpiresIn() {
30 return expiresIn;
31 }
32
33 public void setExpiresIn(int expiresIn) {
34 this.expiresIn = expiresIn;
35 }
36
37 public String getRefreshToken() {
38 return refreshToken;
39 }
40
41 public void setRefreshToken(String refreshToken) {
42 this.refreshToken = refreshToken;
43 }
44
45 public String getOpenId() {
46 return openId;
47 }
48
49 public void setOpenId(String openId) {
50 this.openId = openId;
51 }
52
53 public String getScope() {
54 return scope;
55 }
56
57 public void setScope(String scope) {
58 this.scope = scope;
59 }
60 }
View Code
实体SNSUserInfo:
1 package com.wfcm.wxUitls;
2
3 import java.util.List;
4
5 /**
6 * 类名: SNSUserInfo </br>
7 * 描述: 通过网页授权获取的用户信息 </br>
8 * 开发人员: wzf </br>
9 * 创建时间: 2015-11-27 </br>
10 * 发布版本:V1.0 </br>
11 */
12 public class SNSUserInfo {
13 // 用户标识
14 private String openId;
15 // 用户昵称
16 private String nickname;
17 // 性别(1是男性,2是女性,0是未知)
18 private int sex;
19 // 国家
20 private String country;
21 // 省份
22 private String province;
23 // 城市
24 private String city;
25 // 用户头像链接
26 private String headImgUrl;
27 // 用户特权信息
28 private List<String> privilegeList;
29
30 private String unionid;
31
32 public String getUnionid() {
33 return unionid;
34 }
35
36 public void setUnionid(String unionid) {
37 this.unionid = unionid;
38 }
39
40 public String getOpenId() {
41 return openId;
42 }
43
44 public void setOpenId(String openId) {
45 this.openId = openId;
46 }
47
48 public String getNickname() {
49 return nickname;
50 }
51
52 public void setNickname(String nickname) {
53 this.nickname = nickname;
54 }
55
56 public int getSex() {
57 return sex;
58 }
59
60 public void setSex(int sex) {
61 this.sex = sex;
62 }
63
64 public String getCountry() {
65 return country;
66 }
67
68 public void setCountry(String country) {
69 this.country = country;
70 }
71
72 public String getProvince() {
73 return province;
74 }
75
76 public void setProvince(String province) {
77 this.province = province;
78 }
79
80 public String getCity() {
81 return city;
82 }
83
84 public void setCity(String city) {
85 this.city = city;
86 }
87
88 public String getHeadImgUrl() {
89 return headImgUrl;
90 }
91
92 public void setHeadImgUrl(String headImgUrl) {
93 this.headImgUrl = headImgUrl;
94 }
95
96 public List<String> getPrivilegeList() {
97 return privilegeList;
98 }
99
100 public void setPrivilegeList(List<String> privilegeList) {
101 this.privilegeList = privilegeList;
102 }
103 }
View Code
方法WxController(其中authorize() 方法就是请求第一步获取的链接):
1 package com.wfcm.controller;
2
3 import java.io.BufferedReader;
4 import java.io.IOException;
5 import java.io.InputStreamReader;
6 import java.io.UnsupportedEncodingException;
7 import java.net.URL;
8 import java.net.URLConnection;
9 import java.net.URLEncoder;
10 import java.security.MessageDigest;
11 import java.security.NoSuchAlgorithmException;
12 import java.util.ArrayList;
13 import java.util.Formatter;
14 import java.util.HashMap;
15 import java.util.List;
16 import java.util.Map;
17 import java.util.UUID;
18
19 import javax.servlet.http.HttpServletRequest;
20 import javax.servlet.http.HttpServletResponse;
21
22 import org.apache.http.client.utils.URLEncodedUtils;
23 import org.json.JSONObject;
24 import org.slf4j.Logger;
25 import org.slf4j.LoggerFactory;
26 import org.springframework.beans.factory.annotation.Autowired;
27 import org.springframework.stereotype.Controller;
28 import org.springframework.web.bind.annotation.RequestMapping;
29 import org.springframework.web.bind.annotation.ResponseBody;
30
31 import com.alibaba.fastjson.JSON;
32 import com.alibaba.fastjson.JSONArray;
33 import com.alibaba.fastjson.util.IOUtils;
34 import com.wfcm.annotation.IgnoreSign;
35 import com.wfcm.annotation.IgnoreToken;
36 import com.wfcm.entity.WfMemberEntity;
37 import com.wfcm.service.WfMemberService;
38 import com.wfcm.service.WfMemberSessionService;
39 import com.wfcm.utils.FastJSONUtils;
40 import com.wfcm.utils.NetUtil;
41 import com.wfcm.utils.R;
42 import com.wfcm.wxUitls.AccessToken;
43 import com.wfcm.wxUitls.JsapiTicket;
44 import com.wfcm.wxUitls.Oauth2Token;
45 import com.wfcm.wxUitls.SNSUserInfo;
46
47 @Controller
48 @RequestMapping("/wx")
49 @ResponseBody
50 public class WxController {
51
52 private static Logger log = LoggerFactory.getLogger(WxController.class);
53
54
55 /**
56 * 向指定URL发送GET方法的请求
57 *
58 * @param url
59 * 发送请求的URL
60 * @param param
61 * 请求参数,请求参数应该是 name1=value1&name2=value2 的形式。
62 * @return URL 所代表远程资源的响应结果
63 *
64 * 用户同意授权,获取code
65 */
66 @RequestMapping("/authorize")
67 @ResponseBody
68 @IgnoreToken
69 public static R authorize() {
70 String appid = "wxbb000000000e";
71 //String uri ="wftest.zzff.net/wx/weixinLogin";
72 String uri = urlEncodeUTF8("wftest.zzff.net/api/wx/weixinLogin");
73 String result = "";
74 BufferedReader in = null;
75 try {
76 String urlNameString = "https://open.weixin.qq.com/connect/oauth2/authorize?appid="+appid+"&redirect_uri="+uri+"&response_type=code&scope=snsapi_userinfo&state=STATE#wechat_redirect";
77
78 URL realUrl = new URL(urlNameString);
79 // 打开和URL之间的连接
80 URLConnection connection = realUrl.openConnection();
81 // 设置通用的请求属性
82 connection.setRequestProperty("accept", "*/*");
83 connection.setRequestProperty("connection", "Keep-Alive");
84 connection.setRequestProperty("user-agent",
85 "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)");
86 // 建立实际的连接
87 connection.connect();
88 // 获取所有响应头字段
89 Map<String, List<String>> map = connection.getHeaderFields();
90 // 遍历所有的响应头字段
91 for (String key : map.keySet()) {
92 System.out.println(key + "--->" + map.get(key));
93 }
94 // 定义 BufferedReader输入流来读取URL的响应
95 in = new BufferedReader(new InputStreamReader(
96 connection.getInputStream()));
97 String line =null;
98 while ((line = in.readLine()) != null) {
99 result += line;
100 }
101 /* com.alibaba.fastjson.JSONObject jsonObj= FastJSONUtils.getJSONObject(result);
102 String access_token = jsonObj.getString("access_token");
103 long expires_in = Long.valueOf(jsonObj.getString("expires_in"));
104 */
105 } catch (Exception e) {
106 System.out.println("发送GET请求出现异常!" + e);
107 e.printStackTrace();
108 }
109 // 使用finally块来关闭输入流
110 finally {
111 try {
112 if (in != null) {
113 in.close();
114 }
115 } catch (Exception e2) {
116 e2.printStackTrace();
117 }
118 }
119 return R.ok(result);
120 }
121
122 @RequestMapping("/weixinLogin")
123 @ResponseBody
124 @IgnoreToken
125 @IgnoreSign
126 public void weixinLogin(HttpServletRequest request,HttpServletResponse response) throws Exception {
127 // 用户同意授权后,能获取到code
128 Map<String, String[]> params = request.getParameterMap();//针对get获取get参数
129 String[] codes = params.get("code");//拿到code的值
130 String code = codes[0];//code
131 //String[] states = params.get("state");
132 //String state = states[0];//state
133
134 System.out.println("****************code:"+code);
135 // 用户同意授权
136 if (!"authdeny".equals(code)) {
137 // 获取网页授权access_token
138 Oauth2Token oauth2Token = getOauth2AccessToken("wxb0000000000e", "4c22222233333335555a9", code);
139 System.out.println("***********************************oauth2Token信息:"+oauth2Token.toString());
140 // 网页授权接口访问凭证
141 String accessToken = oauth2Token.getAccessToken();
142 // 用户标识
143 String openId = oauth2Token.getOpenId();
144 // 获取用户信息
145 SNSUserInfo snsUserInfo = getSNSUserInfo(accessToken, openId);
146 System.out.println("***********************************用户信息unionId:"+snsUserInfo.getUnionid()+"***:"+snsUserInfo.getNickname());
147 // 设置要传递的参数
148
149 //具体业务start
150
151 //具体业务end
152
153 String url = "http://wftest.zzff.net/#/biddd?from=login&tokenId="+snsUserInfo.getUnionid();
154
155 response.sendRedirect(url);
156 return ;
157 }
158 }
159
160
161 /**
162 * 获取网页授权凭证
163 *
164 * @param appId 公众账号的唯一标识
165 * @param appSecret 公众账号的密钥
166 * @param code
167 * @return WeixinAouth2Token
168 */
169 public static Oauth2Token getOauth2AccessToken(String appId, String appSecret, String code) {
170 Oauth2Token wat = null;
171 // 拼接请求地址
172 String requestUrl = "https://api.weixin.qq.com/sns/oauth2/access_token?appid=APPID&secret=SECRET&code=CODE&grant_type=authorization_code";
173 requestUrl = requestUrl.replace("APPID", appId);
174 requestUrl = requestUrl.replace("SECRET", appSecret);
175 requestUrl = requestUrl.replace("CODE", code);
176 // 获取网页授权凭证
177 com.alibaba.fastjson.JSONObject jsonObject = JSON.parseObject(NetUtil.get(requestUrl));
178 if (null != jsonObject) {
179 try {
180 wat = new Oauth2Token();
181 wat.setAccessToken(jsonObject.getString("access_token"));
182 wat.setExpiresIn(jsonObject.getInteger("expires_in"));
183 wat.setRefreshToken(jsonObject.getString("refresh_token"));
184 wat.setOpenId(jsonObject.getString("openid"));
185 wat.setScope(jsonObject.getString("scope"));
186 } catch (Exception e) {
187 wat = null;
188 int errorCode = jsonObject.getInteger("errcode");
189 String errorMsg = jsonObject.getString("errmsg");
190 log.error("获取网页授权凭证失败 errcode:{} errmsg:{}", errorCode, errorMsg);
191 }
192 }
193 return wat;
194 }
195
196 /**
197 * 通过网页授权获取用户信息
198 *
199 * @param accessToken 网页授权接口调用凭证
200 * @param openId 用户标识
201 * @return SNSUserInfo
202 */
203 public static SNSUserInfo getSNSUserInfo(String accessToken, String openId) {
204 SNSUserInfo snsUserInfo = null;
205 // 拼接请求地址
206 String requestUrl = "https://api.weixin.qq.com/sns/userinfo?access_token=ACCESS_TOKEN&openid=OPENID";
207 requestUrl = requestUrl.replace("ACCESS_TOKEN", accessToken).replace("OPENID", openId);
208 // 通过网页授权获取用户信息
209 com.alibaba.fastjson.JSONObject jsonObject = JSON.parseObject(NetUtil.get(requestUrl));
210
211 if (null != jsonObject) {
212 try {
213 snsUserInfo = new SNSUserInfo();
214 // 用户的标识
215 snsUserInfo.setOpenId(jsonObject.getString("openid"));
216 // 昵称
217 snsUserInfo.setNickname(jsonObject.getString("nickname"));
218 // 性别(1是男性,2是女性,0是未知)
219 snsUserInfo.setSex(jsonObject.getInteger("sex"));
220 // 用户所在国家
221 snsUserInfo.setCountry(jsonObject.getString("country"));
222 // 用户所在省份
223 snsUserInfo.setProvince(jsonObject.getString("province"));
224 // 用户所在城市
225 snsUserInfo.setCity(jsonObject.getString("city"));
226 // 用户头像
227 snsUserInfo.setHeadImgUrl(jsonObject.getString("headimgurl"));
228 // 用户特权信息
229 List<String> list = JSON.parseArray(jsonObject.getString("privilege"),String.class);
230 snsUserInfo.setPrivilegeList(list);
231 //与开放平台共用的唯一标识,只有在用户将公众号绑定到微信开放平台帐号后,才会出现该字段。
232 snsUserInfo.setUnionid(jsonObject.getString("unionid"));
233 } catch (Exception e) {
234 snsUserInfo = null;
235 int errorCode = jsonObject.getInteger("errcode");
236 String errorMsg = jsonObject.getString("errmsg");
237 log.error("获取用户信息失败 errcode:{} errmsg:{}", errorCode, errorMsg);
238 }
239 }
240 return snsUserInfo;
241 }
242
243 /**
244 * URL编码(utf-8)
245 *
246 * @param source
247 * @return
248 */
249 public static String urlEncodeUTF8(String source) {
250 String result = source;
251 try {
252 result = java.net.URLEncoder.encode(source, "utf-8");
253 } catch (UnsupportedEncodingException e) {
254 e.printStackTrace();
255 }
256 return result;
257 }
258
259 private static String byteToHex(final byte[] hash) {
260 Formatter formatter = new Formatter();
261 for (byte b : hash)
262 {
263 formatter.format("%02x", b);
264 }
265 String result = formatter.toString();
266 formatter.close();
267 return result;
268 }
269
270 private static String create_nonce_str() {
271 return UUID.randomUUID().toString();
272 }
273
274 private static String create_timestamp() {
275 return Long.toString(System.currentTimeMillis() / 1000);
276 }
277 }
View Code
工具NetUtil:
1 package com.wfcm.utils;
2
3 import java.io.BufferedInputStream;
4 import java.io.IOException;
5 import java.io.InputStreamReader;
6 import java.io.UnsupportedEncodingException;
7 import java.util.ArrayList;
8 import java.util.Collection;
9 import java.util.List;
10 import java.util.Map;
11
12 import org.apache.commons.httpclient.NameValuePair;
13 import org.apache.http.HttpEntity;
14 import org.apache.http.HttpResponse;
15 import org.apache.http.client.entity.UrlEncodedFormEntity;
16 import org.apache.http.client.methods.HttpGet;
17 import org.apache.http.client.methods.HttpPost;
18 import org.apache.http.impl.client.CloseableHttpClient;
19 import org.apache.http.impl.client.HttpClientBuilder;
20 import org.apache.http.message.BasicNameValuePair;
21
22 /**
23 * Created by Song on 2016/11/28.
24 * 基于HttpClient提供网络访问工具
25 */
26 public final class NetUtil {
27 public static CloseableHttpClient httpClient = HttpClientBuilder.create().build();
28
29 /**
30 * get请求获取String类型数据
31 * @param url 请求链接
32 * @return
33 */
34 public static String get(String url){
35 StringBuffer sb = new StringBuffer();
36 HttpGet httpGet = new HttpGet(url);
37 try {
38 HttpResponse response = httpClient.execute(httpGet); //1
39
40 HttpEntity entity = response.getEntity();
41 InputStreamReader reader = new InputStreamReader(entity.getContent(),"utf-8");
42 char [] charbufer;
43 while (0<reader.read(charbufer=new char[10])){
44 sb.append(charbufer);
45 }
46 }catch (IOException e){//1
47 e.printStackTrace();
48 }finally {
49 httpGet.releaseConnection();
50 }
51 return sb.toString();
52 }
53
54 /**
55 * post方式请求数据
56 * @param url 请求链接
57 * @param data post数据体
58 * @return
59 */
60 @SuppressWarnings("unchecked")
61 public static String post(String url, Map<String,String> data){
62 StringBuffer sb = new StringBuffer();
63 HttpPost httpPost = new HttpPost(url);
64 List<NameValuePair> valuePairs = new ArrayList<NameValuePair>();
65 if(null != data) {
66 for (String key : data.keySet()) {
67 valuePairs.addAll((Collection<? extends NameValuePair>) new BasicNameValuePair(key, data.get(key)));
68 }
69 }
70 try {
71 httpPost.setEntity(new UrlEncodedFormEntity((List<? extends org.apache.http.NameValuePair>) valuePairs));
72 HttpResponse response = httpClient.execute(httpPost);
73 HttpEntity httpEntity = response.getEntity();
74 BufferedInputStream bis = new BufferedInputStream(httpEntity.getContent());
75 byte [] buffer;
76 while (0<bis.read(buffer=new byte[128])){
77 sb.append(new String(buffer,"utf-8"));
78 }
79 }catch (UnsupportedEncodingException e){//数据格式有误
80 e.printStackTrace();
81 }catch (IOException e){//请求出错
82 e.printStackTrace();
83 }finally {
84 httpPost.releaseConnection();
85 }
86 return sb.toString();
87 }
88
89
90 }
View Code
R类:
package com.wfcm.utils;
import java.util.HashMap;
import java.util.Map;
/**
* 返回数据
*
* @author xlf
* @email xlfbe696@gmail.com
* @date 2017年4月19日 上午11:58:56
*/
public class R extends HashMap<String, Object> {
private static final long serialVersionUID = 1L;
public static final String SUCCESS = "success";
public static final String EXCEPTION = "exception";
public static final Integer SUCCESSCODE = 0;
public static final Integer EXCEPTIONCODE = 500;
public R() {
put("errCode", 0);
put("msg", SUCCESS);
}
public R(int code, String msg){
put("errCode", code);
put("msg", msg);
}
public static R error() {
return error(500, "未知异常,请联系管理员");
}
public static R error(String msg) {
return error(500, msg);
}
public static R error(int code, String msg) {
R r = new R();
r.put("errCode", code);
r.put("msg", msg);
return r;
}
public static R ok(String msg) {
R r = new R();
r.put("msg", msg);
return r;
}
public static R ok(Map<String, Object> map) {
R r = new R();
r.putAll(map);
return r;
}
public static R ok() {
return new R();
}
public R put(String key, Object value) {
super.put(key, value);
return this;
}
}
View Code
OK,大功告成!整体流程已经搭建起来,读懂了这些代码差不多就明白了整个流程了,然后再看官方文档,你会觉得读起来很顺畅,而不是刚开始那种味同嚼蜡的感觉。你只需再根据官方文档仔细检查检查流程,有没有需要完善的地方,就可以了。
还等什么呢,赶快敲实现功能吧!!!