第三方登录之微信扫码登录

2023-11-06


小伙伴们有各种疑问可以去参考官方文档进行详细的学习下 微信开发文档 ,此次介绍的将是前后端不分离的微信扫码登录

微信登录开发流程:

  1. 申请微信接入
  2. 生成登录二维码
  3. 用户扫码并授权
  4. 调用回调方法
  5. 通过code去获取用户信息带到页面展示

官方流程图:
在这里插入图片描述

1. 申请微信接入:

先提醒下各位:申请微信接入很麻烦,本人因为公司业务需要,用的是公司申请好的。还没自己去申请过。

先去到 微信开放平台 https://open.weixin.qq.com

在这里插入图片描述
申请一个网站应用 (要审核通过之后才能用)

在这里插入图片描述
注:

  1. appid和appSecret就是调用微信接口的凭证
  2. 授权回调域:调用微信接口生成的二维码地址,用户扫码并授权后,会重定向到回调地址
  3. 因为在本地localhost进行测试,如果回调地址为localhost第三方微信将无法进行跳转。原因是外网访问不到本地,怎么办?解决办法:那就使用 ngrok 内网穿透把本地项目服务映射到公网,所以在测试时填写的回调地址是内网穿透时的访问域名
  4. 如果不知道内网穿透的小伙伴,建议先看看 Sunny-Ngrok 内网穿透的使用

在这里插入图片描述

如果不知道内网穿透的小伙伴,建议先看看 Sunny-Ngrok 内网穿透的使用

启动 Sunny-Ngrok 内网穿透的客户端,先将本地服务映射到公网。

在这里插入图片描述

2. 项目环境搭建:

下面正式开始代码部分,创建SpringBoot项目并导入所需Jar包:
环境:JDK1.8,SpringBoot2.3.5.RELEASE

<dependencies>
       <dependency>
           <groupId>org.springframework.boot</groupId>
           <artifactId>spring-boot-starter</artifactId>
       </dependency>

       <dependency>
           <groupId>org.springframework.boot</groupId>
           <artifactId>spring-boot-starter-thymeleaf</artifactId>
       </dependency>

       <dependency>
           <groupId>org.springframework.boot</groupId>
           <artifactId>spring-boot-starter-web</artifactId>
       </dependency>

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

yaml文件:
因为所需要这4个参数,所以直接配置在yaml文件中,使用时直接在类中直接通过@Value注解引入即可

  1. appid:
  2. appsecret:
  3. scope: snsapi_login
  4. callBack:
server:
  port: 80

spring:
  thymeleaf:
    prefix: classpath:/templates/
    suffix: .html
  mvc:
    static-path-pattern: classpath:/static/

wechat:
  #微信接口的AppID:
  appid: 
  #微信接口的AppSecret:
  appsecret: 
  #应用授权作用域(网站应用目前的固定写法就是snsapi_login)
  scope: snsapi_login
  #扫码之后的回调地址:
  callBack: http://wechatlogin.free.idcfengye.com/callBack

这里需要一个工具类HttpRequestUtils ,用于发起http请求。可以将代码直接复制过去用

package com.login.utils;

import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;

/**
 * @author: libo
 * @date: 2020/11/21  20:49
 * @motto: 即使再小的帆也能远航
 */
public class HttpRequestUtils {

    private static CloseableHttpClient httpClient;

    static {
        PoolingHttpClientConnectionManager cm = new PoolingHttpClientConnectionManager();
        cm.setMaxTotal(100);
        cm.setDefaultMaxPerRoute(20);
        cm.setDefaultMaxPerRoute(50);
        httpClient = HttpClients.custom().setConnectionManager(cm).build();
    }

    public static String httpGet(String url) {
        CloseableHttpResponse response = null;
        BufferedReader in = null;
        String result = "";
        try {
            HttpGet httpGet = new HttpGet(url);
            RequestConfig requestConfig = RequestConfig.custom().setConnectTimeout(30000).setConnectionRequestTimeout(30000).setSocketTimeout(30000).build();
            httpGet.setConfig(requestConfig);
            httpGet.setConfig(requestConfig);
            httpGet.addHeader("Content-type", "application/json; charset=utf-8");
            httpGet.setHeader("Accept", "application/json");
            response = httpClient.execute(httpGet);
            in = new BufferedReader(new InputStreamReader(response.getEntity().getContent()));
            StringBuffer sb = new StringBuffer("");
            String line = "";
            String NL = System.getProperty("line.separator");
            while ((line = in.readLine()) != null) {
                sb.append(line + NL);
            }
            in.close();
            result = sb.toString();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                if (null != response) {
                    response.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return result;
    }
}

3.后端Controller接口:

package com.login.controller;

import com.login.utils.HttpRequestUtils;
import org.json.JSONObject;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.RequestMapping;

import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.Map;

/**
 * @author: libo
 * @date: 2020/11/21  21:32
 * @motto: 即使再小的帆也能远航
 */
@Controller
@CrossOrigin
@SuppressWarnings("unchecked")
public class weChatController {

    @Value(value = "${wechat.appid}")
    private String appid;

    @Value(value = "${wechat.appsecret}")
    private String appsecret;

    @Value(value = "${wechat.scope}")
    private String scope;

    @Value(value = "${wechat.callback}")
    private String callBack;


    /*生产二维码链接进行扫码登录*/
    @RequestMapping("/login")
    public String index(Model model) throws Exception {

        String oauthUrl = "https://open.weixin.qq.com/connect/qrconnect?appid=APPID&redirect_uri=REDIRECT_URI&response_type=code&scope=SCOPE&state=STATE#wechat_redirect";
        String redirect_uri = URLEncoder.encode(callBack, "utf-8");
        oauthUrl = oauthUrl.replace("APPID", appid).replace("REDIRECT_URI", redirect_uri).replace("SCOPE", scope);
        model.addAttribute("oauthUrl", oauthUrl);

        return "login";
    }


    /*生成自定义二维码*/
    @RequestMapping("/custom")
    public String custom(Model model) throws UnsupportedEncodingException {

        String redirect_uri = URLEncoder.encode(callBack, "utf-8");

        model.addAttribute("appid", appid);
        model.addAttribute("scope", scope);
        model.addAttribute("redirect_uri", redirect_uri);

        return "custom";
    }


    /*回调方法*/
    @RequestMapping("/callBack")
    public String callBack(String code,Map<String,Object> map) {

        //1.通过code获取access_token
        String url = "https://api.weixin.qq.com/sns/oauth2/access_token?appid=APPID&secret=SECRET&code=CODE&grant_type=authorization_code";
        url = url.replace("APPID", appid).replace("SECRET", appsecret).replace("CODE", code);
        String tokenInfoStr = HttpRequestUtils.httpGet(url);

        JSONObject tokenInfoObject = new JSONObject(tokenInfoStr);

        //2.通过access_token和openid获取用户信息
        String userInfoUrl = "https://api.weixin.qq.com/sns/userinfo?access_token=ACCESS_TOKEN&openid=OPENID";
        userInfoUrl = userInfoUrl.replace("ACCESS_TOKEN", tokenInfoObject.getString("access_token")).replace("OPENID", tokenInfoObject.getString("openid"));
        String userInfoStr = HttpRequestUtils.httpGet(userInfoUrl);

        map.put("token", tokenInfoStr);

        /*转为JSON*/
        JSONObject user = new JSONObject(userInfoStr);

        /*只获取openid并返回,openid是微信用户的唯一标识,userInfoStr里面有用户的全部信息*/
        String openid = user.getString("openid");
        map.put("openid", openid);

        /*获取用户头像url*/
        String headimgurl = user.getString("headimgurl");
        map.put("headimgurl", headimgurl);

        /*获取用户昵称*/
        String nickname = user.getString("nickname");
        map.put("nickname", nickname);

        /*获取用户性别*/
        int sex = user.getInt("sex");
        map.put("sex", sex);

        /*获取用户国家*/
        String country = user.getString("country");
        map.put("country", country);

        /*获取用户省份*/
        String province = user.getString("province");
        map.put("province", province);

        /*获取用户城市*/
        String city = user.getString("city");
        map.put("city", city);

        return "callBack";
    }

}

4.HTML页面代码:

1.点击生成二维码页面:我这里是嵌入了一张微信的Logo图片,点击a标签跳转 weChatLogin.html在这里插入图片描述

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>wechat登录</title>
</head>
<body>
    <a th:href="${oauthUrl}">
        <img src="../static/wechat_logo.png" alt="微信登录">
    </a>
</body>
</html>

2.回调方法后返回用户信息页面 callBack.html

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>授权结果页</title>
</head>
<body>

        <h2>用戶,授权成功!</h2><br>
        <h3>通过code获取access_token 结果:</h3>
        <p th:text="${token}"></p>

        <h3>通过access_token获取用户信息 结果:</h3>
        头像:<img th:src="${headimgurl}" alt="用户头像"><br/>
        openid:<span th:text="${openid}"></span><br/>
        昵称:<span th:text="${nickname}"></span><br/>
        性别:<span th:text="${sex}"></span><br/>
        国家:<span th:text="${country}"></span><br/>
        省份:<span th:text="${province}"></span><br/>
        城市:<span th:text="${city}"></span>

</body>
</html>

3.内嵌(自定义二维码) custom.html

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>内嵌(自定义二维码)</title>
</head>
<script src="http://res.wx.qq.com/connect/zh_CN/htmledition/js/wxLogin.js"></script>
<body>

<center><div id="login_container"></div></center>
<script th:inline="javascript">
    var obj = new WxLogin({
        self_redirect:true,
        id:"login_container",
        appid: [[${appid}]],
        scope: [[${scope}]],
        redirect_uri: [[${redirect_uri}]],
        state: "",
        style: "",
        href: ""
    });
</script>
</body>
</html>

5.测试结果:

使用内网穿透的域名来访问接口哦,因为已经在公网映射到本地项目了

  1. 访问login接口,点击logo图标
    在这里插入图片描述

  2. 扫码测试

在这里插入图片描述

  1. 授权后调用回调方法,拿到用户信息放到页面展示
    注:openid是微信用户的唯一id

在这里插入图片描述

6.补充说明:

到这里呢微信登录SpringBoot前后端不分离就差不多了,在这里想给大家提一下如果是前后端分离怎么做?

1.回调地址一般是网站的主页对吧,例如:www.abc.com
2.前端按钮通过appid和回调地址生成二维码
3.用户扫码授权之后,微信接口会再通过回调地址重定向回主页 www.abc.com。在这是会有一个名为code的参数
4.此时前端拥有code之后,传到后端接口方法中去,后端通过code获取用户信息。再返回前端

总结为一句话:1.www.adc.com主页生成二维码,2.扫码授权登录,3.拿code参数去获取用户信息

如果我们有再三思考的机会,几乎没有一件事情是不能被简化的。
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

第三方登录之微信扫码登录 的相关文章

  • 策略模式还是命令模式?

    假设我有一个金融交易列表 我需要针对这些交易执行一系列验证规则 一个例子是我有一笔购买产品的交易 但是首先我需要验证交易中的帐户是否有足够的可用资金 产品没有售完等 由于这些规则 交易将是标记为拒绝 并应指定错误代码 当然 我正在考虑用一个
  • 将 MouseListener 添加到面板

    我正在尝试将鼠标操作添加到我的面板中 这就是程序应该做的事情 编写一个程序 允许用户通过按三下鼠标来指定一个三角形 第一次按下鼠标后 画一个小点 第二次按下鼠标后 绘制一条连接前两个点的线 第三次按下鼠标后 绘制整个三角形 第四次按下鼠标会
  • 在 jTextfield 中禁用“粘贴”

    我有一个用 Swing awt 编写的应用程序 我想阻止用户将值粘贴到文本字段中 有没有办法在不使用动作监听器的情况下做到这一点 您可以使用 null 参数调用 setTransferHandler 如下所示 textComponent s
  • 对象数组的数组(二维数组)JNI

    我正在努力创建自定义对象类型 ShareStruct 的二维数组 jobjectArray ret jobjectArray ins jobjectArray outs jclass myClass env gt FindClass env
  • 使类只能从特定类实例化

    假设我有 3 节课class1 class2 and class3 我怎样才能拥有它class1只能通过实例化class2 class1 object new class1 但不是 class3 或任何其他类 我认为它应该与修饰符一起使用
  • 查询 MongoDB 集合中的字段。

    我正在尝试查询 mongodb 集合中的特定字段 这是我的代码和输出 Mongo m new Mongo DB db m getDB mydb DBCollection coll db getCollection student addin
  • Java 卡布局。多张卡中的一个组件

    一个组件 例如JLabel 在多张卡中使用CardLayout 目前看来该组件仅出现在它添加到的最后一张卡上 如果有办法做到这一点 我应该吗 这是不好的做法吗 或者有其他选择吗 你是对的 它只出现在 添加到的最后一张卡 中 但这与CardL
  • 使用 Spring 控制器处理错误 404

    I use ExceptionHandler处理我的网络应用程序抛出的异常 在我的例子中我的应用程序返回JSON回应HTTP status用于对客户端的错误响应 但是 我正在尝试弄清楚如何处理error 404返回与处理的类似的 JSON
  • 以有效的方式从 Map 中删除多个键?

    我有一个Map
  • 用 C++ 解析 HTTP 标头

    我正在使用curl 与服务器通信 当我发出数据请求时 我收到 HTTP 标头 后跟由边界分隔的 jpeg 数据 如下所示 我需要解析出 边界字符串 内容长度 我已将传入数据复制到 char 数组 如下所示 static size t OnR
  • JUnit5 平台启动器 API - 如果没有至少一个测试引擎,则无法创建启动器

    我正在尝试升级我们的自动化测试套件的测试能力以接受 JUnit5 测试并遵循JUnit 平台启动器 API 说明 https junit org junit5 docs current user guide launcher api我收到错
  • 在java中将字符串日期转换为美国格式

    我有下面的代码 其中日期为字符串类型 我必须将其设置为美国格式 所以下面我已经展示了它 private static final SimpleDateFormat usOutputDate new SimpleDateFormat MM d
  • 确定序列化对象的类型

    我需要通过套接字发送消息 从用户到引擎的请求 以及从引擎到用户的响应 所以流程本质上是 serialized request Server lt network gt Client serialized response request r
  • 在 Java 中创建 XML 文件的最佳方法是什么?

    我们目前使用 dom4j 来创建 XML 文件 不过 我猜现在有更好的东西了 如果我们使用的是 Java 1 6 或更高版本 那么在编写 XML 文件时最好使用什么类 运行速度最快 使用简单 我不需要构建一个 DOM 然后编写整个 DOM
  • Java 中 JButton 的击键/热键

    最初我使用 JMenu 并建立热键以使用加速器工作 它运行得很好 现在我想在 JButton 中实现相同的行为 但我陷入困境 这是我编写的代码 请分享您的想法 以便我可以走上正确的道路 import javax swing import j
  • BadPaddingException:无效的密文

    我需要一些帮助 因为这是我第一次编写加密代码 加密代码似乎工作正常 但解密会引发错误 我得到的错误是 de flexiprovider api exceptions BadPaddingException 无效的密文 in the 解密函数
  • 如何以编程方式创建 CardView

    我正在开发一个 Android 应用程序Java Android Studio 我想在活动中创建CardView以编程方式 我想将以下属性设置为CardView layout width wrap content layout row 0
  • 删除 JFX 中选项卡后面的灰色背景

    So is there any way to remove the gray area behind the tab s 我尝试过用 CSS 来做到这一点 但没有找到方法 要设置 tabpane 标题的背景颜色 请在 CSS 文件中写入 t
  • 如何制作一个makefile只用于编译一些java文件?

    我有三个java文件 名为A java B java C java A将创建对象B B将创建对象C 但我以前从未构建过makefile 有谁可以帮我构建一个 makefile 来编译这三个 java 文件吗 我应该使用什么工具来制作 mak
  • 在实现使用原始类型的接口时如何避免警告?

    我正在实施流程工厂 http help eclipse org ganymede index jsp topic org eclipse platform doc isv reference api org eclipse debug co

随机推荐

  • 茶叶的保健作用与有益成分

    保健作用 能减低心脑血管发病和死亡风险据研究报道 日本研究者从1994年起对某地4万多名40岁至79岁中老年人进行跟踪调查发现 与一天喝茶不到1杯的人相比 每天喝5杯以上绿茶的男性因脑血管病死亡的平均风险下降了22 女性下降了31 其中脑梗
  • 《Spring源码深度分析》第6章 容器的功能扩展

    目录标题 前言 一 ApplicationContext代码切入点 加载配置文件的方式 代码切入点 二 设置配置路径 三 扩展功能 四 环境准备 五 加载 BeanFactory 1 obtainFreshBeanFactory 源码 流程
  • 目标跟踪(OTB100、GOT10K、LaSOT)数据集pysot测试结果

    论文画图 必不可少的测试结果 因为接触跟踪较晚 且对于matlab实在是不熟悉 所以最开始直接尝试的就是pysot进行曲线图 各算法比较框图的绘制 但是最近因为写论文需要画图 发现一些算法的txt文件实在难找 所以写下了这个帖子 目前并不全
  • 键盘盲打练习打字软件 v6.30绿色版

    点击下载来源 键盘盲打练习打字软件 v6 30绿色版 键盘盲打练习是一款字母和数字相结合的键盘打字练习软件 跟其他同性质功能软件相比较 它最大的特点是 眼睛不用看键盘 击闪烁的键 仔细体会 单击鼠标右键或按Alt A键 可弹出快捷菜单 非常
  • # STM32错误积累01:error: #11-D: unrecognized preprocessing directive

    解决办法 在 ifndef 与 define 后加上一个空格
  • docker安装kafka

    Kafka 是一个分布式流媒体平台 类似于消息队列或企业消息传递系统 kafka介绍 名词解释 producer 发布消息的对象称之为主题生产者 Kafka topic producer topic Kafka 将消息分门别类 每一类的消息
  • 【多模态】14、Segment Anything

    文章目录 一 Intruduction 二 Segment Anything Task 三 Segment Anything Model 四 Segment Anything Data Engine 五 Segment Anything D
  • 关于Qt pro、pri、qmake、Makefile的资源整理

    QT中PRO文件写法的详细介绍 如何在Qt Creator中创建pri文件 以及pri文件的说明 qmake的使用 跟我一起写 Makefile 一 推荐大家仔细看陈皓的和我一起写Makefile 可以通透makefile的工作原理 其次里
  • 林园价值交易策略

    文章目录 选股策略 林园6条炒股 心经 选股策略 选股时可以考虑在低市盈率 高分红的绩优龙头股和确定性高的小盘股中选 所选择的上市公司的财务指标需符合七大标准 每股盈利不低于0 3元 净利润不少于7000万元 毛利率在20 以上 净资产回报
  • 病毒反调试跟踪

    跟踪一个反调试巨多的病毒样本 1 调用 QueryPerformanceCounter反调试 这个API调用了封装ZwQueryPerformanceCounter系统调用的ntdll NtQueryPerformanceCounter 0
  • 什么是mvvm模式

    MVVM 是把 MVC 的 Controller 和 MVP 的 Presenter 改成了 ViewModel View 的变化会 动更新到 ViewModel ViewModel 的变化也会 动同步到 View 上显示 这种 动 同步是
  • vue-cli3.0 多核编译ts及内存配置(解决项目过大编译内存溢出)

    主要配置webpack插件ForkTsChecker 在vue config js中配置 module exports configureWebpack config gt 多核启动编译及内存提升 const data config plu
  • 区块链将如何改变服装、改变时尚?

    越来越多的服装公司开始注意到在某些情况下 它们需要改变自己的商业模式 以耐克为例 他们不再把自己定位成一家服装公司 相反 他们说自己是一家科技公司 碰巧生产服装 他们的衣服和鞋子通常都装有传感器 用于跟踪心率 英里数或卡路里燃烧情况 这是因
  • MySQL - 表字段的默认值约束

    设置表字段的默认值 DEFAULT 当为数据库表中插入一条新记录时 如果没有为某个字段赋值 数据库系统就会自动为这个字段插入默认值 为了达到这种效果 可通过SQL语句关键字DEFAULT来设置 设置数据库表中某字段的默认值非常简单 可以在M
  • Vue UI 组件库大起底 element VS iview VS ...

    最近接触了几个开源项目 发现大家都在用iview框架 趁机整理一下自己接触过的几个基于Vue js的UI组件库 Element 一套为开发者 设计师和产品经理准备的基于 Vue 2 0 的桌面端组件库 由饿了么前端开源的UI框架 主要用于开
  • Java多线程实现抢票

    1 1抢票系统 多人抢票 package Demo8 多个线程同时操作一个对象 买车票例子 public class TestThread4 implements Runnable private int ticket nums 10 Ov
  • Couldn't read row 0, col -1 from CursorWindow. Make sure the Cursor is initialized correctly before

    在做项目时碰到一个问题 就是一直报 java lang IllegalStateException Couldn t read row 0 col 1 from CursorWindow Make sure the Cursor is in
  • QML设计登陆界面

    QML设计登陆界面 本文博客链接 http blog csdn net jdh99 作者 jdh 转载请注明 环境 主机 WIN7 开发环境 Qt5 2 说明 用QML设计一个应用的登陆界面 效果图 源代码 main qml javascr
  • python数据处理中的日期转换处理中的to_datetime()函数(一)

    python使用的是 jupyter notebook 话不多说 直接说说主要内容吧 一 函数简介 我们可以通过输入 import pandas as pd help pd to datetime 可以得到to datetime函数的相关作
  • 第三方登录之微信扫码登录

    文章目录 1 申请微信接入 2 项目环境搭建 3 后端Controller接口 4 HTML页面代码 5 测试结果 6 补充说明 小伙伴们有各种疑问可以去参考官方文档进行详细的学习下 微信开发文档 此次介绍的将是前后端不分离的微信扫码登录