1. 程式人生 > 實用技巧 >OAuth2實現微信登入

OAuth2實現微信登入

一新增配置

1 # 微信開放平臺 appid
2 wx.open.app_id=wxed9954c01bb89b47
3 # 微信開放平臺 appsecret
4 wx.open.app_secret=a7482517235173ddb4083788de60b90e
5 # 微信開放平臺 重定向url
6 wx.open.redirect_url=http://guli.shop/api/ucenter/wx/callback

二建立util包,建立ConstantPropertiesUtil.java常量類,讀取配置資訊

 1 package com.atguigu.eduservice.uitls;
 2
3 import org.springframework.beans.factory.InitializingBean; 4 import org.springframework.beans.factory.annotation.Value; 5 import org.springframework.stereotype.Component; 6 7 /** 8 * Author: noob 9 * DATE: 2020/7/16 0016 10 * PROJECT: guli_parent 11 * DESCRIPTION: 讀取配置檔案的值 12 **/ 13 @Component
14 public class ConstanWxUtils implements InitializingBean{ 15 @Value("${wx.open.app_id}") 16 private String appId; 17 @Value("${wx.open.app_secret}") 18 private String appSecret; 19 @Value("${wx.open.redirect_url}") 20 21 22 private String redirectUrl; 23 public static
String WX_OPEN_APP_ID; 24 public static String WX_OPEN_APP_SECRET; 25 public static String WX_OPEN_REDIRECT_URL; 26 @Override 27 public void afterPropertiesSet() throws Exception { 28 WX_OPEN_APP_ID = appId; 29 WX_OPEN_APP_SECRET = appSecret; 30 WX_OPEN_REDIRECT_URL = redirectUrl; 31 32 } 33 }

三生成二維碼圖片,當我們掃描後會獲得一個code

 1 @GetMapping("login")
 2     public String getWxCode() {
 3         //固定地址,後面拼接引數
 4 //        String url = "https://open.weixin.qq.com/" +
 5 //                "connect/qrconnect?appid="+ ConstantWxUtils.WX_OPEN_APP_ID+"&response_type=code";
 6 
 7         // 微信開放平臺授權baseUrl  %s相當於?代表佔位符
 8         String baseUrl = "https://open.weixin.qq.com/connect/qrconnect" +
 9                 "?appid=%s" +
10                 "&redirect_uri=%s" +
11                 "&response_type=code" +
12                 "&scope=snsapi_login" +
13                 "&state=%s" +
14                 "#wechat_redirect";
15 
16         //對redirect_url進行URLEncoder編碼
17         String redirectUrl = ConstanWxUtils.WX_OPEN_REDIRECT_URL;
18         try {
19             redirectUrl = URLEncoder.encode(redirectUrl, "utf-8");
20         }catch(Exception e) {
21         }
22 
23         //設定%s裡面值
24         String url = String.format(
25                     baseUrl,
26                      ConstanWxUtils.WX_OPEN_APP_ID,
27                     redirectUrl,
28                     "atguigu"
29                  );
30 
31         //重定向到請求微信地址裡面
32         return "redirect:"+url;
33     }

四當登入成功,會獲得code和state,回撥到http://8150/api/ucenter/wx/callback?code=xx&state=xx

五匯入HttpClientUtils這個工具類

  1 package com.atguigu.eduservice.uitls;
  2 
  3 import org.apache.commons.io.IOUtils;
  4 import org.apache.commons.lang.StringUtils;
  5 import org.apache.http.Consts;
  6 import org.apache.http.HttpEntity;
  7 import org.apache.http.HttpResponse;
  8 import org.apache.http.NameValuePair;
  9 import org.apache.http.client.HttpClient;
 10 import org.apache.http.client.config.RequestConfig;
 11 import org.apache.http.client.config.RequestConfig.Builder;
 12 import org.apache.http.client.entity.UrlEncodedFormEntity;
 13 import org.apache.http.client.methods.HttpGet;
 14 import org.apache.http.client.methods.HttpPost;
 15 import org.apache.http.conn.ConnectTimeoutException;
 16 import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
 17 import org.apache.http.conn.ssl.SSLContextBuilder;
 18 import org.apache.http.conn.ssl.TrustStrategy;
 19 import org.apache.http.conn.ssl.X509HostnameVerifier;
 20 import org.apache.http.entity.ContentType;
 21 import org.apache.http.entity.StringEntity;
 22 import org.apache.http.impl.client.CloseableHttpClient;
 23 import org.apache.http.impl.client.HttpClients;
 24 import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
 25 import org.apache.http.message.BasicNameValuePair;
 26 
 27 import javax.net.ssl.SSLContext;
 28 import javax.net.ssl.SSLException;
 29 import javax.net.ssl.SSLSession;
 30 import javax.net.ssl.SSLSocket;
 31 import java.io.IOException;
 32 import java.net.SocketTimeoutException;
 33 import java.security.GeneralSecurityException;
 34 import java.security.cert.CertificateException;
 35 import java.security.cert.X509Certificate;
 36 import java.util.ArrayList;
 37 import java.util.List;
 38 import java.util.Map;
 39 import java.util.Map.Entry;
 40 import java.util.Set;
 41 
 42 /**
 43  *  依賴的jar包有:commons-lang-2.6.jar、httpclient-4.3.2.jar、httpcore-4.3.1.jar、commons-io-2.4.jar
 44  * @author zhaoyb
 45  *
 46  */
 47 public class HttpClientUtils {
 48 
 49     public static final int connTimeout=10000;
 50     public static final int readTimeout=10000;
 51     public static final String charset="UTF-8";
 52     private static HttpClient client = null;
 53 
 54     static {
 55         PoolingHttpClientConnectionManager cm = new PoolingHttpClientConnectionManager();
 56         cm.setMaxTotal(128);
 57         cm.setDefaultMaxPerRoute(128);
 58         client = HttpClients.custom().setConnectionManager(cm).build();
 59     }
 60 
 61     public static String postParameters(String url, String parameterStr) throws ConnectTimeoutException, SocketTimeoutException, Exception{
 62         return post(url,parameterStr,"application/x-www-form-urlencoded",charset,connTimeout,readTimeout);
 63     }
 64 
 65     public static String postParameters(String url, String parameterStr,String charset, Integer connTimeout, Integer readTimeout) throws ConnectTimeoutException, SocketTimeoutException, Exception{
 66         return post(url,parameterStr,"application/x-www-form-urlencoded",charset,connTimeout,readTimeout);
 67     }
 68 
 69     public static String postParameters(String url, Map<String, String> params) throws ConnectTimeoutException,
 70             SocketTimeoutException, Exception {
 71         return postForm(url, params, null, connTimeout, readTimeout);
 72     }
 73 
 74     public static String postParameters(String url, Map<String, String> params, Integer connTimeout,Integer readTimeout) throws ConnectTimeoutException,
 75             SocketTimeoutException, Exception {
 76         return postForm(url, params, null, connTimeout, readTimeout);
 77     }
 78 
 79     public static String get(String url) throws Exception {
 80         return get(url, charset, null, null);
 81     }
 82 
 83     public static String get(String url, String charset) throws Exception {
 84         return get(url, charset, connTimeout, readTimeout);
 85     }
 86 
 87     /**
 88      * 傳送一個 Post 請求, 使用指定的字符集編碼.
 89      *
 90      * @param url
 91      * @param body RequestBody
 92      * @param mimeType 例如 application/xml "application/x-www-form-urlencoded" a=1&b=2&c=3
 93      * @param charset 編碼
 94      * @param connTimeout 建立連結超時時間,毫秒.
 95      * @param readTimeout 響應超時時間,毫秒.
 96      * @return ResponseBody, 使用指定的字符集編碼.
 97      * @throws ConnectTimeoutException 建立連結超時異常
 98      * @throws SocketTimeoutException  響應超時
 99      * @throws Exception
100      */
101     public static String post(String url, String body, String mimeType,String charset, Integer connTimeout, Integer readTimeout)
102             throws ConnectTimeoutException, SocketTimeoutException, Exception {
103         HttpClient client = null;
104         HttpPost post = new HttpPost(url);
105         String result = "";
106         try {
107             if (StringUtils.isNotBlank(body)) {
108                 HttpEntity entity = new StringEntity(body, ContentType.create(mimeType, charset));
109                 post.setEntity(entity);
110             }
111             // 設定引數
112             Builder customReqConf = RequestConfig.custom();
113             if (connTimeout != null) {
114                 customReqConf.setConnectTimeout(connTimeout);
115             }
116             if (readTimeout != null) {
117                 customReqConf.setSocketTimeout(readTimeout);
118             }
119             post.setConfig(customReqConf.build());
120 
121             HttpResponse res;
122             if (url.startsWith("https")) {
123                 // 執行 Https 請求.
124                 client = createSSLInsecureClient();
125                 res = client.execute(post);
126             } else {
127                 // 執行 Http 請求.
128                 client = HttpClientUtils.client;
129                 res = client.execute(post);
130             }
131             result = IOUtils.toString(res.getEntity().getContent(), charset);
132         } finally {
133             post.releaseConnection();
134             if (url.startsWith("https") && client != null&& client instanceof CloseableHttpClient) {
135                 ((CloseableHttpClient) client).close();
136             }
137         }
138         return result;
139     }
140 
141 
142     /**
143      * 提交form表單
144      *
145      * @param url
146      * @param params
147      * @param connTimeout
148      * @param readTimeout
149      * @return
150      * @throws ConnectTimeoutException
151      * @throws SocketTimeoutException
152      * @throws Exception
153      */
154     public static String postForm(String url, Map<String, String> params, Map<String, String> headers, Integer connTimeout,Integer readTimeout) throws ConnectTimeoutException,
155             SocketTimeoutException, Exception {
156 
157         HttpClient client = null;
158         HttpPost post = new HttpPost(url);
159         try {
160             if (params != null && !params.isEmpty()) {
161                 List<NameValuePair> formParams = new ArrayList<NameValuePair>();
162                 Set<Entry<String, String>> entrySet = params.entrySet();
163                 for (Entry<String, String> entry : entrySet) {
164                     formParams.add(new BasicNameValuePair(entry.getKey(), entry.getValue()));
165                 }
166                 UrlEncodedFormEntity entity = new UrlEncodedFormEntity(formParams, Consts.UTF_8);
167                 post.setEntity(entity);
168             }
169 
170             if (headers != null && !headers.isEmpty()) {
171                 for (Entry<String, String> entry : headers.entrySet()) {
172                     post.addHeader(entry.getKey(), entry.getValue());
173                 }
174             }
175             // 設定引數
176             Builder customReqConf = RequestConfig.custom();
177             if (connTimeout != null) {
178                 customReqConf.setConnectTimeout(connTimeout);
179             }
180             if (readTimeout != null) {
181                 customReqConf.setSocketTimeout(readTimeout);
182             }
183             post.setConfig(customReqConf.build());
184             HttpResponse res = null;
185             if (url.startsWith("https")) {
186                 // 執行 Https 請求.
187                 client = createSSLInsecureClient();
188                 res = client.execute(post);
189             } else {
190                 // 執行 Http 請求.
191                 client = HttpClientUtils.client;
192                 res = client.execute(post);
193             }
194             return IOUtils.toString(res.getEntity().getContent(), "UTF-8");
195         } finally {
196             post.releaseConnection();
197             if (url.startsWith("https") && client != null
198                     && client instanceof CloseableHttpClient) {
199                 ((CloseableHttpClient) client).close();
200             }
201         }
202     }
203 
204 
205 
206 
207     /**
208      * 傳送一個 GET 請求
209      *
210      * @param url
211      * @param charset
212      * @param connTimeout  建立連結超時時間,毫秒.
213      * @param readTimeout  響應超時時間,毫秒.
214      * @return
215      * @throws ConnectTimeoutException   建立連結超時
216      * @throws SocketTimeoutException   響應超時
217      * @throws Exception
218      */
219     public static String get(String url, String charset, Integer connTimeout,Integer readTimeout)
220             throws ConnectTimeoutException,SocketTimeoutException, Exception {
221 
222         HttpClient client = null;
223         HttpGet get = new HttpGet(url);
224         String result = "";
225         try {
226             // 設定引數
227             Builder customReqConf = RequestConfig.custom();
228             if (connTimeout != null) {
229                 customReqConf.setConnectTimeout(connTimeout);
230             }
231             if (readTimeout != null) {
232                 customReqConf.setSocketTimeout(readTimeout);
233             }
234             get.setConfig(customReqConf.build());
235 
236             HttpResponse res = null;
237 
238             if (url.startsWith("https")) {
239                 // 執行 Https 請求.
240                 client = createSSLInsecureClient();
241                 res = client.execute(get);
242             } else {
243                 // 執行 Http 請求.
244                 client = HttpClientUtils.client;
245                 res = client.execute(get);
246             }
247 
248             result = IOUtils.toString(res.getEntity().getContent(), charset);
249         } finally {
250             get.releaseConnection();
251             if (url.startsWith("https") && client != null && client instanceof CloseableHttpClient) {
252                 ((CloseableHttpClient) client).close();
253             }
254         }
255         return result;
256     }
257 
258 
259     /**
260      * 從 response 裡獲取 charset
261      *
262      * @param ressponse
263      * @return
264      */
265     @SuppressWarnings("unused")
266     private static String getCharsetFromResponse(HttpResponse ressponse) {
267         // Content-Type:text/html; charset=GBK
268         if (ressponse.getEntity() != null  && ressponse.getEntity().getContentType() != null && ressponse.getEntity().getContentType().getValue() != null) {
269             String contentType = ressponse.getEntity().getContentType().getValue();
270             if (contentType.contains("charset=")) {
271                 return contentType.substring(contentType.indexOf("charset=") + 8);
272             }
273         }
274         return null;
275     }
276 
277 
278 
279     /**
280      * 建立 SSL連線
281      * @return
282      * @throws GeneralSecurityException
283      */
284     private static CloseableHttpClient createSSLInsecureClient() throws GeneralSecurityException {
285         try {
286             SSLContext sslContext = new SSLContextBuilder().loadTrustMaterial(null, new TrustStrategy() {
287                 public boolean isTrusted(X509Certificate[] chain,String authType) throws CertificateException {
288                     return true;
289                 }
290             }).build();
291 
292             SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(sslContext, new X509HostnameVerifier() {
293 
294                 @Override
295                 public boolean verify(String arg0, SSLSession arg1) {
296                     return true;
297                 }
298 
299                 @Override
300                 public void verify(String host, SSLSocket ssl)
301                         throws IOException {
302                 }
303 
304                 @Override
305                 public void verify(String host, X509Certificate cert)
306                         throws SSLException {
307                 }
308 
309                 @Override
310                 public void verify(String host, String[] cns,
311                                    String[] subjectAlts) throws SSLException {
312                 }
313 
314             });
315 
316             return HttpClients.custom().setSSLSocketFactory(sslsf).build();
317 
318         } catch (GeneralSecurityException e) {
319             throw e;
320         }
321     }
322 
323     public static void main(String[] args) {
324         try {
325             String str= post("https://localhost:443/ssl/test.shtml","name=12&page=34","application/x-www-form-urlencoded", "UTF-8", 10000, 10000);
326             //String str= get("https://localhost:443/ssl/test.shtml?name=12&page=34","GBK");
327             /*Map<String,String> map = new HashMap<String,String>();
328             map.put("name", "111");
329             map.put("page", "222");
330             String str= postForm("https://localhost:443/ssl/test.shtml",map,null, 10000, 10000);*/
331             System.out.println(str);
332         } catch (ConnectTimeoutException e) {
333             // TODO Auto-generated catch block
334             e.printStackTrace();
335         } catch (SocketTimeoutException e) {
336             // TODO Auto-generated catch block
337             e.printStackTrace();
338         } catch (Exception e) {
339             // TODO Auto-generated catch block
340             e.printStackTrace();
341         }
342     }
343 
344 }

六根據code呼叫微信特定方法獲得access_token和openid

 1  try {
 2             //1 獲取code值,臨時票據,類似於驗證碼
 3             //2 拿著code請求 微信固定的地址,得到兩個值 accsess_token 和 openid
 4             String baseAccessTokenUrl = "https://api.weixin.qq.com/sns/oauth2/access_token" +
 5                     "?appid=%s" +
 6                     "&secret=%s" +
 7                     "&code=%s" +
 8                     "&grant_type=authorization_code";
 9             //拼接三個引數 :id  祕鑰 和 code值
10             String accessTokenUrl = String.format(
11                     baseAccessTokenUrl,
12                     ConstanWxUtils.WX_OPEN_APP_ID,
13                     ConstanWxUtils.WX_OPEN_APP_SECRET,
14                     code
15             );
16             //請求這個拼接好的地址,得到返回兩個值 accsess_token 和 openid
17             //使用httpclient傳送請求,得到返回結果
18             String accessTokenInfo = HttpClientUtils.get(accessTokenUrl);
19 
20             //從accessTokenInfo字串獲取出來兩個值 accsess_token 和 openid
21             //把accessTokenInfo字串轉換map集合,根據map裡面key獲取對應值
22             //使用json轉換工具 Gson
23             Gson gson = new Gson();
24             HashMap mapAccessToken = gson.fromJson(accessTokenInfo, HashMap.class);
25             String access_token = (String)mapAccessToken.get("access_token");
26             String openid = (String)mapAccessToken.get("openid");

七根據access_token和openid訪問微信特定函式,得到使用者資訊

 1 /把掃描人資訊新增資料庫裡面
 2             //判斷資料表裡面是否存在相同微信資訊,根據openid判斷
 3             UcenterMember member = memberService.getOpenIdMember(openid);
 4             if(member == null) {//memeber是空,表沒有相同微信資料,進行新增
 5 
 6                 //3 拿著得到accsess_token 和 openid,再去請求微信提供固定的地址,獲取到掃描人資訊
 7                 //訪問微信的資源伺服器,獲取使用者資訊
 8                 String baseUserInfoUrl = "https://api.weixin.qq.com/sns/userinfo" +
 9                         "?access_token=%s" +
10                         "&openid=%s";
11                 //拼接兩個引數
12                 String userInfoUrl = String.format(
13                         baseUserInfoUrl,
14                         access_token,
15                         openid
16                 );
17                 //傳送請求
18                 String userInfo = HttpClientUtils.get(userInfoUrl);
19                 //獲取返回userinfo字串掃描人資訊
20                 HashMap userInfoMap = gson.fromJson(userInfo, HashMap.class);
21                 String nickname = (String)userInfoMap.get("nickname");//暱稱
22                 String headimgurl = (String)userInfoMap.get("headimgurl");//頭像
23 
24                 member = new UcenterMember();
25                 member.setOpenid(openid);
26                 member.setNickname(nickname);
27                 member.setAvatar(headimgurl);
28                 memberService.save(member);
29             }
30 
31             //使用jwt根據member物件生成token字串
32             String jwtToken = JwtUtils.getJwtToken(member.getId(), member.getNickname());
33             //最後:返回首頁面,通過路徑傳遞token字串
34             return "redirect:http://localhost:3000?token="+jwtToken;
35         }catch(Exception e) {
36             throw new GuliException(20001,"登入失敗");
37         }