1.准备工作
1.1 获取微信登录凭证
前往官网微信开放平台 (qq.com),完成以下步骤:
1.注册
2.邮箱激活
3.完善开发者资料
4.开发者资质认证
5.创建网站应用
1.2 配置文件
在配置文件application.properties添加相关配置信息:
1
2
3
4
5
6# 微信开放平台 appid wx.open.app_id=你的appid # 微信开放平台 appsecret wx.open.app_secret=你的appsecret # 微信开放平台重定向url wx.open.redirect_url=http://81/api/ucenter/wx/callback
1.3 添加依赖
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15<!--httpclient--> <dependency> <groupId>org.apache.httpcomponents</groupId> <artifactId>httpclient</artifactId> </dependency> <!--commons-io--> <dependency> <groupId>commons-io</groupId> <artifactId>commons-io</artifactId> </dependency> <!--gson--> <dependency> <groupId>com.google.code.gson</groupId> <artifactId>gson</artifactId> </dependency>
1.4 创建读取公共常量的工具类
创建读取公共常量的工具类ConstantWxUtils:
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/** * @author xppll * @date 2021/12/11 14:39 */ @Component public class ConstantWxUtils 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; } }
1.5 HttpClient工具类
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/** * 依赖的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(); } } }
2.实现微信登录
可以参考官方文档:[网站应用微信登录开发指南](准备工作 | 微信开放文档 (qq.com))
2.1 具体流程
- 第三方发起微信授权登录请求,微信用户允许授权第三方应用后,微信会拉起应用或重定向到第三方网站,并且带上授权临时票据code参数
- 通过code参数加上AppID和AppSecret等,通过API换取access_token
- 通过access_token进行接口调用,获取用户基本数据资源或帮助用户实现基本操作
获取access_token时序图:
2.2 生成微信扫描的二维码(请求CODE)
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/** * @author xppll * @date 2021/12/11 14:48 */ @CrossOrigin @Controller @RequestMapping("/api/ucenter/wx") public class WxApiController { @Autowired private UcenterMemberService memberService; /** * 生成微信扫描二维码 * @return 定向到请求微信地址 */ @GetMapping("login") public String getWxCode() { //微信开放平台授权baseUrl 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 = ConstantWxUtils.WX_OPEN_REDIRECT_URL; try { redirectUrl = URLEncoder.encode(redirectUrl, "UTF-8"); } catch (UnsupportedEncodingException e) { throw new GuliException(20001, e.getMessage()); } //设置%s的值 String url = String.format( baseUrl, ConstantWxUtils.WX_OPEN_APP_ID, redirectUrl, "atguigu" ); //重定向到请求微信地址 return "redirect:" + url; } }
访问:http://localhost:8160/api/ucenter/wx/login
访问授权url后会得到一个微信登录二维码:
用户扫描二维码会看到确认登录的页面:
用户点击“确认登录”后,微信服务器会向谷粒学院的业务服务器发起回调,因此接下来我们需要开发回调controller
2.3 回调
具体分几步:
1.通过code获取access_token
1https://api.weixin.qq.com/sns/oauth2/access_token?appid=APPID&secret=SECRET&code=CODE&grant_type=authorization_code
参数 | 是否必须 | 说明 |
---|---|---|
appid | 是 | 应用唯一标识,在微信开放平台提交应用审核通过后获得 |
secret | 是 | 应用密钥AppSecret,在微信开放平台提交应用审核通过后获得 |
code | 是 | 填写第一步获取的code参数 |
grant_type | 是 | 填authorization_code |
2.从返回结果获取两个值access_token、openid
3.通过openid查询数据库判断该用户是不是第一次登录
4.如果是第一次登录,根据access_token和openid再去访问微信的资源服务器,获取用户信息,存入数据库
5.使用jwt根据member对象生成token字符串,最后返回首页面,通过路径传递token字符串
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/** * 获取扫描人信息,添加数据 * @param code 类似于手机验证码,随机唯一的值 * @param state 用于保持请求和回调的状态,授权请求后原样带回给第三方 * @return */ @GetMapping("callback") public String callback(String code, String state) { try { //获取code值,临时票据类似于验证码 //拿着code请求微信固定的地址,得到两个值 //1.向认证服务器发送请求换取access_token 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, ConstantWxUtils.WX_OPEN_APP_ID, ConstantWxUtils.WX_OPEN_APP_SECRET, code ); //2.请求拼接好的地址,得到返回的两个值access_token和openid //使用httpclient发送请求,得到返回结果(json形式的字符串) String accessTokenInfo = HttpClientUtils.get(accessTokenUrl); //从accessTokenInfo字符串获取两个值access_token、openid //把accessTokenInfo字符串转换为map集合,根据map里面的key获取值 //这里使用json转换工具Gson Gson gson = new Gson(); HashMap accessTokenMap = gson.fromJson(accessTokenInfo, HashMap.class); String access_token = (String) accessTokenMap.get("access_token"); String openid = (String) accessTokenMap.get("openid"); //3.判断该用户是不是第一次扫码登录 //通过openid判断 UcenterMember member = memberService.getOpenIdMember(openid); //4.只有第一次登录才获取信息 if (member == null) { //根据access_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); System.out.println(userInfo); //将用户信息存入数据库 //把json转换为map HashMap userInfoMap = gson.fromJson(userInfo, HashMap.class); //得到nickname String nickname = (String) userInfoMap.get("nickname"); //得到微信头像avatar String headimgurl = (String) userInfoMap.get("headimgurl"); member = new UcenterMember(); member.setOpenid(openid); member.setNickname(nickname); member.setAvatar(headimgurl); memberService.save(member); } //5.使用jwt根据member对象生成token字符串 String jwtToken = JwtUtils.getJwtToken(member.getId(), member.getNickname()); //最后返回首页面,通过路径传递token字符串 return "redirect:http://localhost:3000?token=" + jwtToken; } catch (Exception e) { throw new GuliException(20001, "微信登录失败"); } }
判断该用户是不是第一次扫码登录
以上就是详解SpringBoot如何实现整合微信登录的详细内容,更多关于SpringBoot整合微信登录的资料请关注靠谱客其它相关文章!
最后
以上就是敏感学姐最近收集整理的关于详解SpringBoot如何实现整合微信登录的全部内容,更多相关详解SpringBoot如何实现整合微信登录内容请搜索靠谱客的其他文章。
发表评论 取消回复