日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

微信公众号扫码登录(一)—— 获取微信公众号二维码

發(fā)布時間:2023/12/9 编程问答 32 豆豆
生活随笔 收集整理的這篇文章主要介紹了 微信公众号扫码登录(一)—— 获取微信公众号二维码 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

引言

這幾天在研究微信登錄,今天解決了獲取微信二維碼問題;在這里總結(jié)一下

關(guān)于微信登錄想說的話

第一次接觸微信登錄,開始就弄混了登錄方式;簡單來說,微信掃碼登錄分為兩種,一種是微信公眾平臺,一種是微信開放平臺,兩者的開發(fā)文檔也不一樣,開始就是一直用的微信公眾號的參數(shù)卻使用的是微信開放平臺提供的接口,找了半天問題;


總結(jié)一下:

微信公眾號(公眾平臺) 和 微信開放平臺 是兩碼事。

  • 微信公眾平臺是掃碼通過微信公眾號授權(quán)登錄的,個人用戶可以申請訂閱號,但是沒有一些接口調(diào)用權(quán)限,企業(yè)用戶可以申請服務(wù)號,有許多接口權(quán)限;但是個人用戶可以通過測試號獲取一些權(quán)限進行學習測試;
  • 微信開放平臺是微信為了接入更多第三方應(yīng)用而開放的接口,對于web應(yīng)用,可以申請web應(yīng)用,從而獲取權(quán)限,但是只能是企業(yè)用戶才能申請;(個人學習很不方便)

兩者開發(fā)文檔是不同的,所以看網(wǎng)上教程一定要看清楚是公眾平臺還是開放平臺,不要跟錯教程了;最好就是看官方文檔,這樣就能避免踩坑;(我開始就是看不下去文檔,因為微信是php示例代碼,所以一直看網(wǎng)上教程,就被各種各樣的教程弄暈了;最后還是乖乖看文檔去了)


下面就進入正題

準備環(huán)境

首先申請了一個公眾號,然后從開發(fā)者工具中進入測試號:

然后就是這個界面:

appID和appsecret都是操作所需參數(shù);

接口配置下面介紹;

接口配置

接口配置后面會用到,所以需要先配置一下,其實看官方文檔就能看懂,但是示例代碼是php,所以這里我來演示一下我的操作;

官方文檔:傳送門

官方文檔意思就是你得有一個域名,代碼還得跑在該域名下的服務(wù)器上,然后你自己需要再代碼中實現(xiàn)一個接口獲取微信發(fā)來的信息進行處理;

如果你和我一樣是個學生,開發(fā)都在本地127.0.0.1,或者沒有域名,那該怎么辦?因為微信不能直接調(diào)用本地ip,這就需要用到內(nèi)網(wǎng)穿透;

簡單來說就是微信想要向你填入的url發(fā)送請求數(shù)據(jù),但是它不能直接向本地127.0.0.1發(fā)送,我們可以通過內(nèi)網(wǎng)穿透獲取一個域名,讓該域名映射到本地127.0.0.1,然后微信向該域名發(fā)送數(shù)據(jù),這樣就把數(shù)據(jù)發(fā)送到了本地;(個人理解)


內(nèi)網(wǎng)穿透

我通過ngrok進行的內(nèi)網(wǎng)穿透,就花2塊錢實名了一下,然后有一個免費的隧道可以使用:

然后進行配置,它就會給你分一個域名:

接下來下載ngrok客戶端,啟動隧道:

點擊.bat文件,按要求輸入隧道id:

鏈接成功,這時時就意味著訪問本地127.0.0.1和訪問生成的域名的效果是一樣的;


訪問流程就是:

文檔:

微信向自己填的url發(fā)送請求;

實現(xiàn)接入代碼

然后就是代碼配置,就是官網(wǎng)文檔的第二步,官方檢驗是一個php代碼,下面是java代碼:

controller接口:

/*** 接入微信接口*/ @GetMapping("/callback") @ResponseBody public String checkSign (HttpServletRequest request) throws Exception {log.info("===========>checkSign");// 獲取微信請求參數(shù)String signature = request.getParameter ("signature");String timestamp = request.getParameter ("timestamp");String nonce = request.getParameter ("nonce");String echostr = request.getParameter ("echostr");log.info("開始校驗此次消息是否來自微信服務(wù)器,param->signature:{},\ntimestamp:{},\nnonce:{},\nechostr:{}",signature, timestamp, nonce, echostr);if (CheckWXTokenUtils.checkSignature(signature, timestamp, nonce)) {return echostr;}return ""; }

校驗工具類(直接cv)

import lombok.extern.log4j.Log4j2;import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.util.Arrays;/*** 和微信建立鏈接參數(shù)校驗*/ @Log4j2 public class CheckWXTokenUtils {private static final String TOKEN = "xxxxxx"; // 自定義的token/*** 校驗微信服務(wù)器Token簽名** @param signature 微信加密簽名* @param timestamp 時間戳* @param nonce 隨機數(shù)* @return boolean*/public static boolean checkSignature(String signature, String timestamp, String nonce) {String[] arr = {TOKEN, timestamp, nonce};Arrays.sort(arr);StringBuilder stringBuilder = new StringBuilder();for (String param : arr) {stringBuilder.append(param);}String hexString = SHA1(stringBuilder.toString());return signature.equals(hexString);}private static String SHA1(String str) {MessageDigest md;try {md = MessageDigest.getInstance("SHA-1");byte[] digest = md.digest(str.getBytes());return toHexString(digest);} catch (NoSuchAlgorithmException e) {log.info("校驗令牌Token出現(xiàn)錯誤:{}", e.getMessage());}return "";}/*** 字節(jié)數(shù)組轉(zhuǎn)化為十六進制** @param digest 字節(jié)數(shù)組* @return String*/private static String toHexString(byte[] digest) {StringBuilder hexString = new StringBuilder();for (byte b : digest) {String shaHex = Integer.toHexString(b & 0xff);if (shaHex.length() < 2) {hexString.append(0);}hexString.append(shaHex);}return hexString.toString();} }

幾個校驗參數(shù)官方文檔也說了,自己對比著看就行了;

再次放入官方文檔地址:接入概述


配置就是:

url是內(nèi)網(wǎng)穿透域名+自己實現(xiàn)的接口

token也是自己寫的;

然后在網(wǎng)頁服務(wù)中:

點擊修改:(還是內(nèi)網(wǎng)穿透域名)

然后就配置好了;

下面就是正式開始二維碼生成了;

生成二維碼

先放出文檔:

生成帶參數(shù)的二維碼

獲取Access token

獲取ticket

我們先看第一個文檔,大致意思就是先發(fā)送一個獲取二維碼ticket的post請求,獲取ticket:

整理一下:

url: post https://api.weixin.qq.com/cgi-bin/qrcode/create?access_token=TOKEN參數(shù): param參數(shù):access_token json參數(shù)(兩個必要的,其他可以自己看文檔加): {"expire_seconds": 604800, "action_name": "QR_SCENE"}結(jié)果: {"ticket":"gQH47joAAAAAAAAAASxodHRwOi8vd2VpeGluLnFxLmNvbS9xL2taZ2Z3TVRtNzJXV1Brb3ZhYmJJAAIEZ23sUwMEmm 3sUw==","expire_seconds":60,"url":"http://weixin.qq.com/q/kZgfwMTm72WWPkovabbI"}

那么就很簡單了,就是調(diào)用就接口獲取ticket,但是可以發(fā)現(xiàn)所需要的參數(shù)中的access_token我們并沒有,所以調(diào)用該接口前需要先獲取,同樣也有官方文檔,上面已經(jīng)放出來了;


獲取access_token

同樣整理一下:

url: get https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=APPID&secret=APPSECRET參數(shù): grant_type、appid、secret結(jié)果: {"access_token":"ACCESS_TOKEN","expires_in":7200}

其中g(shù)rant_type就寫client_credential就行了,固定的;

appid和secret就是測試號的:

所以很輕易就可以獲取到access_token;

獲取到access_token就可以獲取ticket,到這里這兩步就完成了;

接下來就是獲取二維碼了:

獲取二維碼

這就不過多介紹了,就加一個ticket參數(shù)即可;

測試流程

下面我使用postman測試一遍流程:

1,首先獲取access_token:

https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=&secret=

2,然后獲取ticket:

https://api.weixin.qq.com/cgi-bin/qrcode/create?access_token=ACCESS_TOKEN

3,最后獲取二維碼:

https://mp.weixin.qq.com/cgi-bin/showqrcode?ticket=TICKET

直接瀏覽器訪問該鏈接,即可看到該二維碼

整個流程就走通了;

下面就是代碼實現(xiàn)該流程;

代碼實現(xiàn)

這里思考一個問題,后端內(nèi)部如何自己發(fā)送請求獲取響應(yīng)?

平時都是前端向后端發(fā)送請求,后端響應(yīng);

因為我們需要先發(fā)送請求獲取access_token,再獲取ticket,最后響應(yīng)給前端的就是一個二維碼url,所以后端需要自己發(fā)請求并獲取響應(yīng)結(jié)果;

這里可以使用httpclient,具體細節(jié)可以查資料,這里不過多介紹;

下面是實現(xiàn)代碼:

httpclient依賴:

<dependency><groupId>org.apache.httpcomponents</groupId><artifactId>httpclient</artifactId><version>4.5.13</version> </dependency>

httpclient工具類(直接cv):

import org.apache.http.HttpEntity; import org.apache.http.NameValuePair; import org.apache.http.client.config.RequestConfig; import org.apache.http.client.entity.UrlEncodedFormEntity; import org.apache.http.client.methods.CloseableHttpResponse; import org.apache.http.client.methods.HttpGet; import org.apache.http.client.methods.HttpPost; import org.apache.http.client.methods.HttpRequestBase; import org.apache.http.client.utils.URIBuilder; import org.apache.http.client.utils.URLEncodedUtils; import org.apache.http.conn.ssl.SSLConnectionSocketFactory; import org.apache.http.entity.StringEntity; import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClientBuilder; import org.apache.http.impl.client.HttpClients; import org.apache.http.message.BasicNameValuePair; import org.apache.http.ssl.SSLContexts; import org.apache.http.util.EntityUtils;import javax.net.ssl.SSLContext; import java.io.File; import java.io.FileInputStream; import java.nio.charset.StandardCharsets; import java.security.KeyStore; import java.util.ArrayList; import java.util.List; import java.util.Map;/*** HttpClient工具類*/ public class HttpClientUtils {private static final CloseableHttpClient httpClient;// 采用靜態(tài)代碼塊,初始化超時時間配置,再根據(jù)配置生成默認httpClient對象static {RequestConfig config = RequestConfig.custom().setConnectTimeout(30000).setSocketTimeout(15000).build();httpClient = HttpClientBuilder.create().setDefaultRequestConfig(config).build();}/*** 發(fā)送 HTTP GET請求,不帶請求參數(shù)和請求頭* @param url 請求地址* @return* @throws Exception*/public static String doGet(String url) throws Exception {HttpGet httpGet = new HttpGet(url);return doHttp(httpGet);}/*** 發(fā)送 HTTP GET,請求帶參數(shù),不帶請求頭* @param url 請求地址* @param params 請求參數(shù)* @return* @throws Exception*/public static String doGet(String url, Map<String, Object> params) throws Exception {// 轉(zhuǎn)換請求參數(shù)List<NameValuePair> pairs = covertParamsToList(params);// 裝載請求地址和參數(shù)URIBuilder ub = new URIBuilder();ub.setPath(url);ub.setParameters(pairs);HttpGet httpGet = new HttpGet(ub.build());return doHttp(httpGet);}/*** 發(fā)送 HTTP GET請求,帶請求參數(shù)和請求頭* @param url 請求地址* @param headers 請求頭* @param params 請求參數(shù)* @return* @throws Exception*/public static String doGet(String url, Map<String, Object> headers, Map<String, Object> params) throws Exception {// 轉(zhuǎn)換請求參數(shù)List<NameValuePair> pairs = covertParamsToList(params);// 裝載請求地址和參數(shù)URIBuilder ub = new URIBuilder();ub.setPath(url);ub.setParameters(pairs);HttpGet httpGet = new HttpGet(ub.build());// 設(shè)置請求頭for (Map.Entry<String, Object> param : headers.entrySet()) {httpGet.addHeader(param.getKey(), String.valueOf(param.getValue()));}return doHttp(httpGet);}/*** 發(fā)送 HTTP POST請求,不帶請求參數(shù)和請求頭** @param url 請求地址* @return* @throws Exception*/public static String doPost(String url) throws Exception {HttpPost httpPost = new HttpPost(url);return doHttp(httpPost);}/*** 發(fā)送 HTTP POST請求,帶請求參數(shù),不帶請求頭** @param url 請求地址* @param params 請求參數(shù)* @return* @throws Exception*/public static String doPost(String url, Map<String, Object> params) throws Exception {// 轉(zhuǎn)換請求參數(shù)List<NameValuePair> pairs = covertParamsToList(params);HttpPost httpPost = new HttpPost(url);// 設(shè)置請求參數(shù)httpPost.setEntity(new UrlEncodedFormEntity(pairs, StandardCharsets.UTF_8.name()));return doHttp(httpPost);}/*** 發(fā)送 HTTP POST請求,帶請求參數(shù)和請求頭** @param url 地址* @param headers 請求頭* @param params 參數(shù)* @return* @throws Exception*/public static String doPost(String url, Map<String, Object> headers, Map<String, Object> params) throws Exception {// 轉(zhuǎn)換請求參數(shù)List<NameValuePair> pairs = covertParamsToList(params);HttpPost httpPost = new HttpPost(url);// 設(shè)置請求參數(shù)httpPost.setEntity(new UrlEncodedFormEntity(pairs, StandardCharsets.UTF_8.name()));// 設(shè)置請求頭for (Map.Entry<String, Object> param : headers.entrySet()) {httpPost.addHeader(param.getKey(), String.valueOf(param.getValue()));}return doHttp(httpPost);}/*** 發(fā)送 HTTP POST請求,請求參數(shù)是JSON格式,數(shù)據(jù)編碼是UTF-8** @param url 請求地址* @param param 請求參數(shù)* @return* @throws Exception*/public static String doPostJson(String url, String param) throws Exception {HttpPost httpPost = new HttpPost(url);// 設(shè)置請求頭httpPost.addHeader("Content-Type", "application/json; charset=UTF-8");// 設(shè)置請求參數(shù)httpPost.setEntity(new StringEntity(param, StandardCharsets.UTF_8.name()));return doHttp(httpPost);}/*** 發(fā)送 HTTP POST請求,請求參數(shù)是XML格式,數(shù)據(jù)編碼是UTF-8** @param url 請求地址* @param param 請求參數(shù)* @return* @throws Exception*/public static String doPostXml(String url, String param) throws Exception {HttpPost httpPost = new HttpPost(url);// 設(shè)置請求頭httpPost.addHeader("Content-Type", "application/xml; charset=UTF-8");// 設(shè)置請求參數(shù)httpPost.setEntity(new StringEntity(param, StandardCharsets.UTF_8.name()));return doHttp(httpPost);}/*** 發(fā)送 HTTPS POST請求,使用指定的證書文件及密碼,不帶請求頭信息<** @param url 請求地址* @param param 請求參數(shù)* @param path 證書全路徑* @param password 證書密碼* @return* @throws Exception* @throws Exception*/public static String doHttpsPost(String url, String param, String path, String password) throws Exception {HttpPost httpPost = new HttpPost(url);// 設(shè)置請求參數(shù)httpPost.setEntity(new StringEntity(param, StandardCharsets.UTF_8.name()));return doHttps(httpPost, path, password);}/*** 發(fā)送 HTTPS POST請求,使用指定的證書文件及密碼,請求頭為“application/xml;charset=UTF-8”** @param url 請求地址* @param param 請求參數(shù)* @param path 證書全路徑* @param password 證書密碼* @return* @throws Exception* @throws Exception*/public static String doHttpsPostXml(String url, String param, String path, String password) throws Exception {HttpPost httpPost = new HttpPost(url);// 設(shè)置請求頭httpPost.addHeader("Content-Type", "application/xml; charset=UTF-8");// 設(shè)置請求參數(shù)httpPost.setEntity(new StringEntity(param, StandardCharsets.UTF_8.name()));return doHttps(httpPost, path, password);}/*** 發(fā)送 HTTPS 請求,使用指定的證書文件及密碼** @param request* @param path 證書全路徑* @param password 證書密碼* @return* @throws Exception* @throws Exception*/private static String doHttps(HttpRequestBase request, String path, String password) throws Exception {// 獲取HTTPS SSL證書SSLConnectionSocketFactory csf = getHttpsFactory(path, password);// 通過連接池獲取連接對象CloseableHttpClient httpClient = HttpClients.custom().setSSLSocketFactory(csf).build();return doRequest(httpClient, request);}/*** 獲取HTTPS SSL連接工廠,使用指定的證書文件及密碼** @param path 證書全路徑* @param password 證書密碼* @return* @throws Exception* @throws Exception*/private static SSLConnectionSocketFactory getHttpsFactory(String path, String password) throws Exception {// 初始化證書,指定證書類型為“PKCS12”KeyStore keyStore = KeyStore.getInstance("PKCS12");// 讀取指定路徑的證書FileInputStream input = new FileInputStream(new File(path));try {// 裝載讀取到的證書,并指定證書密碼keyStore.load(input, password.toCharArray());} finally {input.close();}// 獲取HTTPS SSL證書連接上下文SSLContext sslContext = SSLContexts.custom().loadKeyMaterial(keyStore, password.toCharArray()).build();// 獲取HTTPS連接工廠,指定TSL版本SSLConnectionSocketFactory sslCsf = new SSLConnectionSocketFactory(sslContext, new String[]{"SSLv2Hello", "SSLv3", "TLSv1", "TLSv1.2"}, null, SSLConnectionSocketFactory.getDefaultHostnameVerifier());return sslCsf;}/*** 發(fā)送 HTTP 請求** @param request* @return* @throws Exception*/private static String doHttp(HttpRequestBase request) throws Exception {// 通過連接池獲取連接對象return doRequest(httpClient, request);}/*** 處理Http/Https請求,并返回請求結(jié)果,默認請求編碼方式:UTF-8* @param httpClient* @param request* @return*/private static String doRequest(CloseableHttpClient httpClient, HttpRequestBase request) throws Exception {String result = null;try (CloseableHttpResponse response = httpClient.execute(request)) {// 獲取請求結(jié)果int statusCode = response.getStatusLine().getStatusCode();if (statusCode != 200) {request.abort();throw new RuntimeException("HttpClient error status code: " + statusCode);}// 解析請求結(jié)果HttpEntity entity = response.getEntity();// 轉(zhuǎn)換結(jié)果result = EntityUtils.toString(entity, StandardCharsets.UTF_8.name());// 關(guān)閉IO流EntityUtils.consume(entity);}return result;}/*** 轉(zhuǎn)換請求參數(shù),將Map鍵值對拼接成QueryString字符串** @param params* @return*/public static String covertMapToQueryStr(Map<String, Object> params) {List<NameValuePair> pairs = covertParamsToList(params);return URLEncodedUtils.format(pairs, StandardCharsets.UTF_8.name());}/*** 轉(zhuǎn)換請求參數(shù)** @param params* @return*/public static List<NameValuePair> covertParamsToList(Map<String, Object> params) {List<NameValuePair> pairs = new ArrayList<>();for (Map.Entry<String, Object> param : params.entrySet()) {pairs.add(new BasicNameValuePair(param.getKey(), String.valueOf(param.getValue())));}return pairs;} }

然后controller實現(xiàn)一個接口,前端調(diào)用該接口獲取二維碼url:

// 獲取二維碼 @GetMapping("/qr/login/param") @ResponseBody public BaseResponse<String> getWxQRCodeParam() {String QRUrl = null;try {// 第一步:發(fā)送請求獲取access_tokenString getAccessTokenUrl = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential" +"&appid=" + WXConstant.APP_ID +"&secret=" + WXConstant.APP_SECRET;String accessTokenRes = HttpClientUtils.doGet(getAccessTokenUrl);log.info("accessTokenRes=>" + accessTokenRes);String accessToken = (String) JSON.parseObject(accessTokenRes).get("access_token"); // 獲取到access_token// 第二步:通過access_token和一些參數(shù)發(fā)送post請求獲取二維碼TicketString getTicketUrl = "https://api.weixin.qq.com/cgi-bin/qrcode/create?access_token=" + accessToken;// 封裝參數(shù)Map<String, Object> ticketInfo = new HashMap<>();ticketInfo.put("expire_seconds", 604800); // 二維碼超時時間ticketInfo.put("action_name", "QR_SCENE");String ticketJsonInfo = JSON.toJSON(ticketInfo).toString();String ticketRes = HttpClientUtils.doPostJson(getTicketUrl, ticketJsonInfo);log.info("ticketRes=>" + ticketRes);String ticket = (String) JSON.parseObject(ticketRes).get("ticket");// 第三步:通過ticket獲取二維碼urlString encodeTicket = URLEncoder.encode(ticket, "utf-8"); // 編碼ticketString getQRUrl = "https://mp.weixin.qq.com/cgi-bin/showqrcode?ticket=" + encodeTicket;QRUrl = getQRUrl; // 二維碼url} catch (Exception e) {e.printStackTrace();}return ResultUtils.success(QRUrl); }

注:該接口只注重了功能實現(xiàn),具體安全性并未考慮;

可以調(diào)用該接口測試一下:

那么前端只需要獲取到這個url并展示出來就行了:

可以測試一下:

可以看到每次都能獲取到不同二維碼;


到這里二維碼生成就完成了,可以掃碼測試一下:

掃碼候就是公眾號界面了,接下來就是一些授權(quán)操作了,下集在講;

總結(jié)

總的來說只要細心看文檔還不算是特別難理解的,所以一定要多看文檔!

總的來說流程就是那三步:

因為文章是本人理解記錄的,可能會有錯誤,有錯誤問題歡迎交流!

總結(jié)

以上是生活随笔為你收集整理的微信公众号扫码登录(一)—— 获取微信公众号二维码的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。