微信公众号扫码登录(一) 您所在的位置:网站首页 如何不登陆微信看公众号 微信公众号扫码登录(一)

微信公众号扫码登录(一)

2024-05-30 11:33| 来源: 网络整理| 查看: 265

引言

这几天在研究微信登录,今天解决了获取微信二维码问题;在这里总结一下

关于微信登录想说的话

第一次接触微信登录,开始就弄混了登录方式;简单来说,微信扫码登录分为两种,一种是微信公众平台,一种是微信开放平台,两者的开发文档也不一样,开始就是一直用的微信公众号的参数却使用的是微信开放平台提供的接口,找了半天问题;

总结一下:

微信公众号(公众平台) 和 微信开放平台 是两码事。

微信公众平台是扫码通过微信公众号授权登录的,个人用户可以申请订阅号,但是没有一些接口调用权限,企业用户可以申请服务号,有许多接口权限;但是个人用户可以通过测试号获取一些权限进行学习测试;微信开放平台是微信为了接入更多第三方应用而开放的接口,对于web应用,可以申请web应用,从而获取权限,但是只能是企业用户才能申请;(个人学习很不方便)

两者开发文档是不同的,所以看网上教程一定要看清楚是公众平台还是开放平台,不要跟错教程了;最好就是看官方文档,这样就能避免踩坑;(我开始就是看不下去文档,因为微信是php示例代码,所以一直看网上教程,就被各种各样的教程弄晕了;最后还是乖乖看文档去了)

下面就进入正题

准备环境

首先申请了一个公众号,然后从开发者工具中进入测试号:

image-20220922221842878

然后就是这个界面:

image-20220922221943712

appID和appsecret都是操作所需参数;

接口配置下面介绍;

接口配置

接口配置后面会用到,所以需要先配置一下,其实看官方文档就能看懂,但是示例代码是php,所以这里我来演示一下我的操作;

官方文档:传送门

官方文档意思就是你得有一个域名,代码还得跑在该域名下的服务器上,然后你自己需要再代码中实现一个接口获取微信发来的信息进行处理;

如果你和我一样是个学生,开发都在本地127.0.0.1,或者没有域名,那该怎么办?因为微信不能直接调用本地ip,这就需要用到内网穿透;

简单来说就是微信想要向你填入的url发送请求数据,但是它不能直接向本地127.0.0.1发送,我们可以通过内网穿透获取一个域名,让该域名映射到本地127.0.0.1,然后微信向该域名发送数据,这样就把数据发送到了本地;(个人理解)

内网穿透

我通过ngrok进行的内网穿透,就花2块钱实名了一下,然后有一个免费的隧道可以使用:

image-20220922222849476

然后进行配置,它就会給你分一个域名:

image-20220922223101957

接下来下载ngrok客户端,启动隧道:

image-20220922223534052

点击.bat文件,按要求输入隧道id:

image-20220922223631622

链接成功,这时时就意味着访问本地127.0.0.1和访问生成的域名的效果是一样的;

访问流程就是:

image-20220922223402119

文档:image-20220922223204499

微信向自己填的url发送请求;

实现接入代码

然后就是代码配置,就是官网文档的第二步,官方检验是一个php代码,下面是java代码:

controller接口:

/** * 接入微信接口 */ @GetMapping("/callback") @ResponseBody public String checkSign (HttpServletRequest request) throws Exception { log.info("===========>checkSign"); // 获取微信请求参数 String signature = request.getParameter ("signature"); String timestamp = request.getParameter ("timestamp"); String nonce = request.getParameter ("nonce"); String echostr = request.getParameter ("echostr"); log.info("开始校验此次消息是否来自微信服务器,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; /** * 和微信建立链接参数校验 */ @Log4j2 public class CheckWXTokenUtils { private static final String TOKEN = "xxxxxx"; // 自定义的token /** * 校验微信服务器Token签名 * * @param signature 微信加密签名 * @param timestamp 时间戳 * @param nonce 随机数 * @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出现错误:{}", e.getMessage()); } return ""; } /** * 字节数组转化为十六进制 * * @param digest 字节数组 * @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() private static final CloseableHttpClient httpClient; // 采用静态代码块,初始化超时时间配置,再根据配置生成默认httpClient对象 static { RequestConfig config = RequestConfig.custom().setConnectTimeout(30000).setSocketTimeout(15000).build(); httpClient = HttpClientBuilder.create().setDefaultRequestConfig(config).build(); } /** * 发送 HTTP GET请求,不带请求参数和请求头 * @param url 请求地址 * @return * @throws Exception */ public static String doGet(String url) throws Exception { HttpGet httpGet = new HttpGet(url); return doHttp(httpGet); } /** * 发送 HTTP GET,请求带参数,不带请求头 * @param url 请求地址 * @param params 请求参数 * @return * @throws Exception */ public static String doGet(String url, Map params) throws Exception { // 转换请求参数 List pairs = covertParamsToList(params); // 装载请求地址和参数 URIBuilder ub = new URIBuilder(); ub.setPath(url); ub.setParameters(pairs); HttpGet httpGet = new HttpGet(ub.build()); return doHttp(httpGet); } /** * 发送 HTTP GET请求,带请求参数和请求头 * @param url 请求地址 * @param headers 请求头 * @param params 请求参数 * @return * @throws Exception */ public static String doGet(String url, Map headers, Map params) throws Exception { // 转换请求参数 List pairs = covertParamsToList(params); // 装载请求地址和参数 URIBuilder ub = new URIBuilder(); ub.setPath(url); ub.setParameters(pairs); HttpGet httpGet = new HttpGet(ub.build()); // 设置请求头 for (Map.Entry param : headers.entrySet()) { httpGet.addHeader(param.getKey(), String.valueOf(param.getValue())); } return doHttp(httpGet); } /** * 发送 HTTP POST请求,不带请求参数和请求头 * * @param url 请求地址 * @return * @throws Exception */ public static String doPost(String url) throws Exception { HttpPost httpPost = new HttpPost(url); return doHttp(httpPost); } /** * 发送 HTTP POST请求,带请求参数,不带请求头 * * @param url 请求地址 * @param params 请求参数 * @return * @throws Exception */ public static String doPost(String url, Map params) throws Exception { // 转换请求参数 List pairs = covertParamsToList(params); HttpPost httpPost = new HttpPost(url); // 设置请求参数 httpPost.setEntity(new UrlEncodedFormEntity(pairs, StandardCharsets.UTF_8.name())); return doHttp(httpPost); } /** * 发送 HTTP POST请求,带请求参数和请求头 * * @param url 地址 * @param headers 请求头 * @param params 参数 * @return * @throws Exception */ public static String doPost(String url, Map headers, Map params) throws Exception { // 转换请求参数 List pairs = covertParamsToList(params); HttpPost httpPost = new HttpPost(url); // 设置请求参数 httpPost.setEntity(new UrlEncodedFormEntity(pairs, StandardCharsets.UTF_8.name())); // 设置请求头 for (Map.Entry param : headers.entrySet()) { httpPost.addHeader(param.getKey(), String.valueOf(param.getValue())); } return doHttp(httpPost); } /** * 发送 HTTP POST请求,请求参数是JSON格式,数据编码是UTF-8 * * @param url 请求地址 * @param param 请求参数 * @return * @throws Exception */ public static String doPostJson(String url, String param) throws Exception { HttpPost httpPost = new HttpPost(url); // 设置请求头 httpPost.addHeader("Content-Type", "application/json; charset=UTF-8"); // 设置请求参数 httpPost.setEntity(new StringEntity(param, StandardCharsets.UTF_8.name())); return doHttp(httpPost); } /** * 发送 HTTP POST请求,请求参数是XML格式,数据编码是UTF-8 * * @param url 请求地址 * @param param 请求参数 * @return * @throws Exception */ public static String doPostXml(String url, String param) throws Exception { HttpPost httpPost = new HttpPost(url); // 设置请求头 httpPost.addHeader("Content-Type", "application/xml; charset=UTF-8"); // 设置请求参数 httpPost.setEntity(new StringEntity(param, StandardCharsets.UTF_8.name())); return doHttp(httpPost); } /** * 发送 HTTPS POST请求,使用指定的证书文件及密码,不带请求头信息< * * @param url 请求地址 * @param param 请求参数 * @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); // 设置请求参数 httpPost.setEntity(new StringEntity(param, StandardCharsets.UTF_8.name())); return doHttps(httpPost, path, password); } /** * 发送 HTTPS POST请求,使用指定的证书文件及密码,请求头为“application/xml;charset=UTF-8” * * @param url 请求地址 * @param param 请求参数 * @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); // 设置请求头 httpPost.addHeader("Content-Type", "application/xml; charset=UTF-8"); // 设置请求参数 httpPost.setEntity(new StringEntity(param, StandardCharsets.UTF_8.name())); return doHttps(httpPost, path, password); } /** * 发送 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; } /** * 发送 HTTP 请求 * * @param request * @return * @throws Exception */ private static String doHttp(HttpRequestBase request) throws Exception { // 通过连接池获取连接对象 return doRequest(httpClient, request); } /** * 处理Http/Https请求,并返回请求结果,默认请求编码方式: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)) { // 获取请求结果 int statusCode = response.getStatusLine().getStatusCode(); if (statusCode != 200) { request.abort(); throw new RuntimeException("HttpClient error status code: " + statusCode); } // 解析请求结果 HttpEntity entity = response.getEntity(); // 转换结果 result = EntityUtils.toString(entity, StandardCharsets.UTF_8.name()); // 关闭IO流 EntityUtils.consume(entity); } return result; } /** * 转换请求参数,将Map键值对拼接成QueryString字符串 * * @param params * @return */ public static String covertMapToQueryStr(Map params) { List pairs = covertParamsToList(params); return URLEncodedUtils.format(pairs, StandardCharsets.UTF_8.name()); } /** * 转换请求参数 * * @param params * @return */ public static List covertParamsToList(Map params) { List pairs = new ArrayList(); for (Map.Entry param : params.entrySet()) { pairs.add(new BasicNameValuePair(param.getKey(), String.valueOf(param.getValue()))); } return pairs; } }

然后controller实现一个接口,前端调用该接口获取二维码url:

// 获取二维码 @GetMapping("/qr/login/param") @ResponseBody public BaseResponse getWxQRCodeParam() { String QRUrl = null; try { // 第一步:发送请求获取access_token String 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和一些参数发送post请求获取二维码Ticket String getTicketUrl = "https://api.weixin.qq.com/cgi-bin/qrcode/create?access_token=" + accessToken; // 封装参数 Map 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获取二维码url String encodeTicket = URLEncoder.encode(ticket, "utf-8"); // 编码ticket String getQRUrl = "https://mp.weixin.qq.com/cgi-bin/showqrcode?ticket=" + encodeTicket; QRUrl = getQRUrl; // 二维码url } catch (Exception e) { e.printStackTrace(); } return ResultUtils.success(QRUrl); }

注:该接口只注重了功能实现,具体安全性并未考虑;

可以调用该接口测试一下:

image-20220922234115109

那么前端只需要获取到这个url并展示出来就行了:

image-20220922234223728

image-20220922234237697

可以测试一下:

GIF

可以看到每次都能获取到不同二维码;

到这里二维码生成就完成了,可以扫码测试一下: 在这里插入图片描述

扫码候就是公众号界面了,接下来就是一些授权操作了,下集在讲;

总结

总的来说只要细心看文档还不算是特别难理解的,所以一定要多看文档!

总的来说流程就是那三步:

image-20220922235213262

因为文章是本人理解记录的,可能会有错误,有错误问题欢迎交流!



【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有