一、准备工作

https://open.weixin.qq.com

  • 1、注册
  • 2、邮箱激活

这是微信开放平台 不能和微信公众平台用一个邮箱号

  • 3、完善开发者资料
  • 4、开发者资质认证

准备营业执照,1-2个工作日审批、300元

  • 5、创建网站应用

(这个我是没办法认证,所以也没截图了,下面放的是博客 blog.java1234.com中的图片哦,我是后面用的别的方法跳转到本地的….)

img

img

提交审核,7个工作日审批(其实和QQ认证差不多的,看这个之前还是先看看我手摸手带兄弟们写的QQ第三方的登录)

  • 6、熟悉微信登录流程

参考文档:https://open.weixin.qq.com/cgi-bin/showdocument?action=dir_list&t=resource/res_list&verify=1&id=open1419316505&token=e547653f995d8f402704d5cb2945177dc8aa4e7e&lang=zh_CN

二、后端开发(开发授权Url)

1、添加配置

application.properties添加相关配置信息

1
2
3
4
5
6
7
8
9
10
11
12
# 这里用的  尚硅谷 项目实战中的appid和app_secret 
# 不得不为尚硅谷打波广告了 有了尚硅谷 学习不在苦.....
# 这个redirect_url会直接重定向到本地localhost:8150/api/ucenter/wx/callback
# 所以后期项目必须端口号为8150 扫码登录重定向地址为/api/ucenter/wx/callback处理回调逻辑
# 譬如:保存微信返回的各种信息 以及页面跳转路地址(一般里面重定向到首页)

# 微信开放平台 appid
wx.open.app_id=wxed9954c01bb89b47
# 微信开放平台 appsecret
wx.open.app_secret=a7482517235173ddb4083788de60b90e
# 微信开放平台 重定向url
wx.open.redirect_url=http://guli.shop/api/ucenter/wx/callback

2、创建常量类

创建util包,创建ConstantPropertiesUtil.java常量类

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
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

@Component
public class ConstantPropertiesUtil implements InitializingBean {

@Value("${wx.open.app_id}")
private String appId;

@Value("${wx.open.app_secret}")
private String appSecret;

@Value("${wx.open.redirect_url}")
private String redirectUrl;

public static String WX_OPEN_APP_ID;
public static String WX_OPEN_APP_SECRET;
public static String WX_OPEN_REDIRECT_URL;

@Override
public void afterPropertiesSet() throws Exception {
WX_OPEN_APP_ID = appId;
WX_OPEN_APP_SECRET = appSecret;
WX_OPEN_REDIRECT_URL = redirectUrl;
}
}

3、创建controller

guli-microservice-ucenter微服务中创建api包

api包中创建WxApiController

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
@CrossOrigin
@Controller //只是请求地址,不需要返回数据
@RequestMapping("/api/ucenter/wx")
public class WxApiController {

//1 生成微信扫描二维码
@GetMapping("login")
public String getWxCode() {
//固定地址,后面拼接参数
// String url = "https://open.weixin.qq.com/" +
// "connect/qrconnect?appid="+ ConstantWxUtils.WX_OPEN_APP_ID+"&response_type=code";

// 微信开放平台授权baseUrl %s相当于?代表占位符
String baseUrl = "https://open.weixin.qq.com/connect/qrconnect" +
"?appid=%s" +
"&redirect_uri=%s" +
"&response_type=code" +
"&scope=snsapi_login" +
"&state=%s" +
"#wechat_redirect";

//对redirect_url进行URLEncoder编码
String redirectUrl = ConstantPropertiesUtil.WX_OPEN_REDIRECT_URL;
try {
redirectUrl = URLEncoder.encode(redirectUrl, "utf-8");
}catch(Exception e) {
}

//设置%s里面值
String url = String.format(
baseUrl,
ConstantPropertiesUtil.WX_OPEN_APP_ID,
redirectUrl,
"atguigu"
);

//重定向到请求微信地址里面
return "redirect:"+url;
}
}

此时只是能跳转到二维码页面!!!然后扫码之后回调还没写

先测试一波

授权url参数说明

参数 是否必须 说明
appid 应用唯一标识
redirect_uri 请使用urlEncode对链接进行处理
response_type 填code
scope 应用授权作用域,拥有多个作用域用逗号(,)分隔,网页应用目前仅填写snsapi_login即
state 用于保持请求和回调的状态,授权请求后原样带回给第三方。该参数可用于防止csrf攻击(跨站请求伪造攻击),建议第三方带上该参数,可设置为简单的随机数加session进行校验

4、测试

访问:

访问授权url后会得到一个微信登录二维码

img

用户扫描二维码会看到确认登录的页面

img

用户点击“确认登录”后,微信服务器会向谷粒学院的业务服务器发起回调,因此接下来我们需要开发回调controller

注意:如果没有正确的配置业务服务器的回调url,则会看到以下错误提示

img

三、开发回调Url

1、导入Pom.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<!--httpclient-->
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>4.5.1</version>
</dependency>
<!--commons-io-->
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.6</version>
</dependency>
<!--gson-->
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>2.8.2</version>
</dependency>

2、创建httpclient工具类

放入util包 HttpClientUtils.java

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
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
package com.atguigu.educenter.utils;

import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.http.Consts;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.NameValuePair;
import org.apache.http.client.HttpClient;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.config.RequestConfig.Builder;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.conn.ConnectTimeoutException;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.conn.ssl.SSLContextBuilder;
import org.apache.http.conn.ssl.TrustStrategy;
import org.apache.http.conn.ssl.X509HostnameVerifier;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.http.message.BasicNameValuePair;

import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLException;
import javax.net.ssl.SSLSession;
import javax.net.ssl.SSLSocket;
import java.io.IOException;
import java.net.SocketTimeoutException;
import java.security.GeneralSecurityException;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;

/**
* 依赖的jar包有:commons-lang-2.6.jar、httpclient-4.3.2.jar、httpcore-4.3.1.jar、commons-io-2.4.jar
* @author zhaoyb
*
*/
public class HttpClientUtils {

public static final int connTimeout=10000;
public static final int readTimeout=10000;
public static final String charset="UTF-8";
private static HttpClient client = null;

static {
PoolingHttpClientConnectionManager cm = new PoolingHttpClientConnectionManager();
cm.setMaxTotal(128);
cm.setDefaultMaxPerRoute(128);
client = HttpClients.custom().setConnectionManager(cm).build();
}

public static String postParameters(String url, String parameterStr) throws ConnectTimeoutException, SocketTimeoutException, Exception{
return post(url,parameterStr,"application/x-www-form-urlencoded",charset,connTimeout,readTimeout);
}

public static String postParameters(String url, String parameterStr,String charset, Integer connTimeout, Integer readTimeout) throws ConnectTimeoutException, SocketTimeoutException, Exception{
return post(url,parameterStr,"application/x-www-form-urlencoded",charset,connTimeout,readTimeout);
}

public static String postParameters(String url, Map<String, String> params) throws ConnectTimeoutException,
SocketTimeoutException, Exception {
return postForm(url, params, null, connTimeout, readTimeout);
}

public static String postParameters(String url, Map<String, String> params, Integer connTimeout,Integer readTimeout) throws ConnectTimeoutException,
SocketTimeoutException, Exception {
return postForm(url, params, null, connTimeout, readTimeout);
}

public static String get(String url) throws Exception {
return get(url, charset, null, null);
}

public static String get(String url, String charset) throws Exception {
return get(url, charset, connTimeout, readTimeout);
}

/**
* 发送一个 Post 请求, 使用指定的字符集编码.
*
* @param url
* @param body RequestBody
* @param mimeType 例如 application/xml "application/x-www-form-urlencoded" a=1&b=2&c=3
* @param charset 编码
* @param connTimeout 建立链接超时时间,毫秒.
* @param readTimeout 响应超时时间,毫秒.
* @return ResponseBody, 使用指定的字符集编码.
* @throws ConnectTimeoutException 建立链接超时异常
* @throws SocketTimeoutException 响应超时
* @throws Exception
*/
public static String post(String url, String body, String mimeType,String charset, Integer connTimeout, Integer readTimeout)
throws ConnectTimeoutException, SocketTimeoutException, Exception {
HttpClient client = null;
HttpPost post = new HttpPost(url);
String result = "";
try {
if (StringUtils.isNotBlank(body)) {
HttpEntity entity = new StringEntity(body, ContentType.create(mimeType, charset));
post.setEntity(entity);
}
// 设置参数
Builder customReqConf = RequestConfig.custom();
if (connTimeout != null) {
customReqConf.setConnectTimeout(connTimeout);
}
if (readTimeout != null) {
customReqConf.setSocketTimeout(readTimeout);
}
post.setConfig(customReqConf.build());

HttpResponse res;
if (url.startsWith("https")) {
// 执行 Https 请求.
client = createSSLInsecureClient();
res = client.execute(post);
} else {
// 执行 Http 请求.
client = HttpClientUtils.client;
res = client.execute(post);
}
result = IOUtils.toString(res.getEntity().getContent(), charset);
} finally {
post.releaseConnection();
if (url.startsWith("https") && client != null&& client instanceof CloseableHttpClient) {
((CloseableHttpClient) client).close();
}
}
return result;
}


/**
* 提交form表单
*
* @param url
* @param params
* @param connTimeout
* @param readTimeout
* @return
* @throws ConnectTimeoutException
* @throws SocketTimeoutException
* @throws Exception
*/
public static String postForm(String url, Map<String, String> params, Map<String, String> headers, Integer connTimeout,Integer readTimeout) throws ConnectTimeoutException,
SocketTimeoutException, Exception {

HttpClient client = null;
HttpPost post = new HttpPost(url);
try {
if (params != null && !params.isEmpty()) {
List<NameValuePair> formParams = new ArrayList<NameValuePair>();
Set<Entry<String, String>> entrySet = params.entrySet();
for (Entry<String, String> entry : entrySet) {
formParams.add(new BasicNameValuePair(entry.getKey(), entry.getValue()));
}
UrlEncodedFormEntity entity = new UrlEncodedFormEntity(formParams, Consts.UTF_8);
post.setEntity(entity);
}

if (headers != null && !headers.isEmpty()) {
for (Entry<String, String> entry : headers.entrySet()) {
post.addHeader(entry.getKey(), entry.getValue());
}
}
// 设置参数
Builder customReqConf = RequestConfig.custom();
if (connTimeout != null) {
customReqConf.setConnectTimeout(connTimeout);
}
if (readTimeout != null) {
customReqConf.setSocketTimeout(readTimeout);
}
post.setConfig(customReqConf.build());
HttpResponse res = null;
if (url.startsWith("https")) {
// 执行 Https 请求.
client = createSSLInsecureClient();
res = client.execute(post);
} else {
// 执行 Http 请求.
client = HttpClientUtils.client;
res = client.execute(post);
}
return IOUtils.toString(res.getEntity().getContent(), "UTF-8");
} finally {
post.releaseConnection();
if (url.startsWith("https") && client != null
&& client instanceof CloseableHttpClient) {
((CloseableHttpClient) client).close();
}
}
}




/**
* 发送一个 GET 请求
*
* @param url
* @param charset
* @param connTimeout 建立链接超时时间,毫秒.
* @param readTimeout 响应超时时间,毫秒.
* @return
* @throws ConnectTimeoutException 建立链接超时
* @throws SocketTimeoutException 响应超时
* @throws Exception
*/
public static String get(String url, String charset, Integer connTimeout,Integer readTimeout)
throws ConnectTimeoutException,SocketTimeoutException, Exception {

HttpClient client = null;
HttpGet get = new HttpGet(url);
String result = "";
try {
// 设置参数
Builder customReqConf = RequestConfig.custom();
if (connTimeout != null) {
customReqConf.setConnectTimeout(connTimeout);
}
if (readTimeout != null) {
customReqConf.setSocketTimeout(readTimeout);
}
get.setConfig(customReqConf.build());

HttpResponse res = null;

if (url.startsWith("https")) {
// 执行 Https 请求.
client = createSSLInsecureClient();
res = client.execute(get);
} else {
// 执行 Http 请求.
client = HttpClientUtils.client;
res = client.execute(get);
}

result = IOUtils.toString(res.getEntity().getContent(), charset);
} finally {
get.releaseConnection();
if (url.startsWith("https") && client != null && client instanceof CloseableHttpClient) {
((CloseableHttpClient) client).close();
}
}
return result;
}


/**
* 从 response 里获取 charset
*
* @param ressponse
* @return
*/
@SuppressWarnings("unused")
private static String getCharsetFromResponse(HttpResponse ressponse) {
// Content-Type:text/html; charset=GBK
if (ressponse.getEntity() != null && ressponse.getEntity().getContentType() != null && ressponse.getEntity().getContentType().getValue() != null) {
String contentType = ressponse.getEntity().getContentType().getValue();
if (contentType.contains("charset=")) {
return contentType.substring(contentType.indexOf("charset=") + 8);
}
}
return null;
}



/**
* 创建 SSL连接
* @return
* @throws GeneralSecurityException
*/
private static CloseableHttpClient createSSLInsecureClient() throws GeneralSecurityException {
try {
SSLContext sslContext = new SSLContextBuilder().loadTrustMaterial(null, new TrustStrategy() {
public boolean isTrusted(X509Certificate[] chain,String authType) throws CertificateException {
return true;
}
}).build();

SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(sslContext, new X509HostnameVerifier() {

@Override
public boolean verify(String arg0, SSLSession arg1) {
return true;
}

@Override
public void verify(String host, SSLSocket ssl)
throws IOException {
}

@Override
public void verify(String host, X509Certificate cert)
throws SSLException {
}

@Override
public void verify(String host, String[] cns,
String[] subjectAlts) throws SSLException {
}

});

return HttpClients.custom().setSSLSocketFactory(sslsf).build();

} catch (GeneralSecurityException e) {
throw e;
}
}

public static void main(String[] args) {
try {
String str= post("https://localhost:443/ssl/test.shtml","name=12&page=34","application/x-www-form-urlencoded", "UTF-8", 10000, 10000);
//String str= get("https://localhost:443/ssl/test.shtml?name=12&page=34","GBK");
/*Map<String,String> map = new HashMap<String,String>();
map.put("name", "111");
map.put("page", "222");
String str= postForm("https://localhost:443/ssl/test.shtml",map,null, 10000, 10000);*/
System.out.println(str);
} catch (ConnectTimeoutException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (SocketTimeoutException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}

}

3、创建回调controller方法

先写个实体类可以封装一波返回的数据(数据库自己创建)

放入entity包 Member.java

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
@Data
@EqualsAndHashCode(callSuper = false)
@Accessors(chain = true)
@TableName("ucenter_member")
@ApiModel(value="Member对象", description="会员表")
public class Member implements Serializable {

private static final long serialVersionUID = 1L;

@ApiModelProperty(value = "会员id")
@TableId(value = "id", type = IdType.ID_WORKER_STR)
private String id;

@ApiModelProperty(value = "微信openid")
private String openid;

@ApiModelProperty(value = "手机号")
private String mobile;

@ApiModelProperty(value = "密码")
private String password;

@ApiModelProperty(value = "昵称")
private String nickname;

@ApiModelProperty(value = "性别 1 女,2 男")
private Integer sex;

@ApiModelProperty(value = "年龄")
private Integer age;

@ApiModelProperty(value = "用户头像")
private String avatar;

@ApiModelProperty(value = "用户签名")
private String sign;

@ApiModelProperty(value = "是否禁用 1(true)已禁用, 0(false)未禁用")
private Boolean isDisabled;

@ApiModelProperty(value = "逻辑删除 1(true)已删除, 0(false)未删除")
private Boolean isDeleted;

@ApiModelProperty(value = "创建时间")
private Date gmtCreate;

@ApiModelProperty(value = "更新时间")
private Date gmtModified;


}

在WxApiController.java中添加如下方法

根据业务自己修改一下代码!!! 这里查询了数据库先判断是否存在

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
@CrossOrigin
@Controller //只是请求地址,不需要返回数据
@RequestMapping("/api/ucenter/wx")
public class WxApiController {


@Autowired
private UcenterMemberService memberService;

//2 获取扫描人信息,添加数据
@GetMapping("callback")
public String callback(String code, String state) {
try {
//1 获取code值,临时票据,类似于验证码
//2 拿着code请求 微信固定的地址,得到两个值 accsess_token 和 openid
String baseAccessTokenUrl = "https://api.weixin.qq.com/sns/oauth2/access_token" +
"?appid=%s" +
"&secret=%s" +
"&code=%s" +
"&grant_type=authorization_code";
//拼接三个参数 :id 秘钥 和 code值
String accessTokenUrl = String.format(
baseAccessTokenUrl,
ConstantPropertiesUtil.WX_OPEN_APP_ID,
ConstantPropertiesUtil.WX_OPEN_APP_SECRET,
code
);
//请求这个拼接好的地址,得到返回两个值 accsess_token 和 openid
//使用httpclient发送请求,得到返回结果
String accessTokenInfo = HttpClientUtils.get(accessTokenUrl);

//从accessTokenInfo字符串获取出来两个值 accsess_token 和 openid
//把accessTokenInfo字符串转换map集合,根据map里面key获取对应值
//使用json转换工具 Gson
Gson gson = new Gson();
HashMap mapAccessToken = gson.fromJson(accessTokenInfo, HashMap.class);
String access_token = (String)mapAccessToken.get("access_token");
String openid = (String)mapAccessToken.get("openid");

//把扫描人信息添加数据库里面
//判断数据表里面是否存在相同微信信息,根据openid判断
// UcenterMember member = memberService.getOpenIdMember(openid);
if(member == null) {//memeber是空,表没有相同微信数据,进行添加

//3 拿着得到accsess_token 和 openid,再去请求微信提供固定的地址,获取到扫描人信息
//访问微信的资源服务器,获取用户信息
String baseUserInfoUrl = "https://api.weixin.qq.com/sns/userinfo" +
"?access_token=%s" +
"&openid=%s";
//拼接两个参数
String userInfoUrl = String.format(
baseUserInfoUrl,
access_token,
openid
);
//发送请求
String userInfo = HttpClientUtils.get(userInfoUrl);
//获取返回userinfo字符串扫描人信息
HashMap userInfoMap = gson.fromJson(userInfo, HashMap.class);
String nickname = (String)userInfoMap.get("nickname");//昵称
String headimgurl = (String)userInfoMap.get("headimgurl");//头像

member = new UcenterMember();
member.setOpenid(openid);
member.setNickname(nickname);
member.setAvatar(headimgurl);
memberService.save(member);
}

//使用jwt根据member对象生成token字符串
//String jwtToken = JwtUtils.getJwtToken(member.getId(), member.getNickname());
//最后:返回首页面,通过路径传递token字符串
//return "redirect:http://localhost:3000?token="+jwtToken;

ModelAndView mav=new ModelAndView();
mav.setViewName("main");
mav.addObject("userInfo",userInfoMap);
return mav;
}catch(Exception e) {
throw new GuliException(20001,"登录失败");
}
}
}
#调用微信返回的Map集合数据

{country=CN, unionid=oWgGz1HEMl_CRcZEyAk3ya2t0UIY, province=Heilongjiang, city=Daqing, openid=o3_SC52OZg94V0voGcDDn0Xi1irI, sex=1.0, nickname=我叫阿鑫, headimgurl=http://thirdwx.qlogo.cn/mmopen/vi_32/22rFMVYtn31Znsf38d954iaboeSQaMGj9OuE6ffpc3u1RsrPKfzumrVaIE9GqApWW4iamMV1IZnicnBVwxNrWbEsw/132, language=zh_CN, privilege=[]}

4、整合前端测试一波

项目结构

img

index.html

1
2
3
4
5
6
7
8
9
10
11
12
13
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title th:text="${title}"></title>
<title>AAAAAAAAAA</title>
</head>
<body>
<div >
<font color="black"><strong>微信扫码登录测试</strong></font><br/><br/>
<a href="/api/ucenter/wx/login"><img src="/images/weixinlogin.png"/></a>
</div>
</body>

LoginController.java

1
2
3
4
5
6
7
8
9
10
11
@Controller
public class LoginController {

@RequestMapping("/")
public String root(Model model){
model.addAttribute("title","zzxxx");
model.addAttribute("zzz","dwadw1111111111111111ad");
return "index";
}

}

img

点击微信账号登录

img

main.html

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.w3.org/1999/xhtml">
<head>
<meta charset="UTF-8">
<title>主页</title>
</head>
<body>
<div>
<P>微信唯一OpenId:<font th:text="${userInfo.openid}"></font></P>
<P>昵称:<font th:text="${userInfo.nickname}"></font></P>
<P>性别:<font th:text="${userInfo.sex}"></font></P>
<P>省份:<font th:text="${userInfo.province}"></font></P>
<P>市区:<font th:text="${userInfo.city}"></font></P>
<P><img th:src="${userInfo.headimgurl}"></P>
</div>
</body>
</html>

img


感谢尚硅谷提供的授权认证接口

兄弟们可以B站关注一波尚硅谷