微信登录模块封装

文章目录

    • 1.资质申请
    • 2.combinations-wx-login-starter
        • 1.目录结构
        • 2.pom.xml 引入okhttp依赖
        • 3.WxLoginProperties.java 属性配置
        • 4.WxLoginUtil.java 后端通过 code 获取 access_token的工具类
        • 5.WxLoginAutoConfiguration.java 自动配置类
        • 6.spring.factories 激活自动配置类
    • 3.combinations-wx-starter-demo
        • 1.目录结构
        • 2.pom.xml 引入依赖
        • 3.application.yml 配置AppID和AppSecret
        • 4.application-prod.yml 配置生产环境的日志和.env文件路径
        • 5.CodeAndState.java 接受code和state的bean
        • 6.WxLoginController.java 微信登录Controller
        • 7.WxApplication.java 启动类
    • 4.微信登录流程梳理
        • 1.用户点击微信登录按钮
        • 2.前端向开放平台发送请求主要携带appId和redirectUri
        • 3.此时开放平台会弹出一个扫码的页面,用户扫码确认
        • 4.用户确认成功后,开放平台会将code和state作为参数去请求redirectUri(前端页面)
        • 5.前端页面获取code和state,再向后端发送请求
        • 6.后端使用code进行微信登录,可以获取到AccessTokenResponse

1.资质申请

  1. 主体为企业的域名和备案的服务器
  2. 主体为企业的微信开放平台的开发者资质认证
  3. 微信开放平台创建应用获取AppID和AppSecret

2.combinations-wx-login-starter

1.目录结构

CleanShot 2025-01-23 at 21.17.45@2x

2.pom.xml 引入okhttp依赖
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><parent><groupId>cn.sunxiansheng</groupId><artifactId>sunrays-combinations</artifactId><version>1.0.0</version></parent><artifactId>combinations-wx-login-starter</artifactId><!-- 项目名 --><name>${project.groupId}:${project.artifactId}</name><!-- 简单描述 --><description>微信登录模块封装</description><dependencies><!-- okhttp --><dependency><groupId>com.squareup.okhttp3</groupId><artifactId>okhttp</artifactId></dependency></dependencies>
</project>
3.WxLoginProperties.java 属性配置
package cn.sunxiansheng.wx.login.config.properties;import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;/*** Description: 微信登录的属性配置** @Author sun* @Create 2025/1/23 18:49* @Version 1.0*/
@ConfigurationProperties(prefix = "sun-rays.wx.login")
@Data
public class WxLoginProperties {/*** 微信开放平台应用的AppID*/private String appId;/*** 微信开放平台应用的AppSecret*/private String appSecret;/*** 微信开放平台的access_token_url前缀,有默认值,可以不填,为了防止变化!*/private String accessTokenUrlPrefix = "https://api.weixin.qq.com/sns/oauth2/access_token";
}
4.WxLoginUtil.java 后端通过 code 获取 access_token的工具类
package cn.sunxiansheng.wx.login.utils;import cn.sunxiansheng.wx.login.config.properties.WxLoginProperties;
import com.google.gson.Gson;
import com.google.gson.annotations.SerializedName;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;import javax.annotation.Resource;/*** Description: 微信登录工具类** @Author sun* @Create 2025/1/23 19:01* @Version 1.0*/
@Slf4j
public class WxLoginUtil {/*** 获取微信登录的配置*/@Resourceprivate WxLoginProperties wxLoginProperties;/*** 微信登录的响应类*/@Datapublic static class AccessTokenResponse {@SerializedName("access_token")private String accessToken;@SerializedName("expires_in")private Integer expiresIn;@SerializedName("refresh_token")private String refreshToken;@SerializedName("openid")private String openId;@SerializedName("scope")private String scope;@SerializedName("unionid")private String unionId;}/*** 根据code来完成微信登录** @param code 微信开放平台返回的code* @return 返回AccessTokenResponse*/public AccessTokenResponse wxLogin(String code) {return getAccessToken(wxLoginProperties.getAppId(), wxLoginProperties.getAppSecret(), code);}/*** 后端通过 code 获取 access_token** @param appid  微信应用的 appid* @param secret 微信应用的 secret* @param code   后端已经获得的 code 参数* @return 返回封装的 AccessTokenResponse 对象*/private AccessTokenResponse getAccessToken(String appid, String secret, String code) {// 构造请求 URLString url = String.format("%s?appid=%s&secret=%s&code=%s&grant_type=authorization_code",wxLoginProperties.getAccessTokenUrlPrefix(), appid, secret, code);// 创建 OkHttpClient 实例OkHttpClient client = new OkHttpClient();// 创建 Request 对象Request request = new Request.Builder().url(url).build();// 执行请求并处理响应try (Response response = client.newCall(request).execute()) {// 检查请求是否成功if (!response.isSuccessful()) {String responseBody = response.body() != null ? response.body().string() : "响应体为空";log.error("后端通过 code 获取 access_token 的请求失败,响应码:{}, 响应体:{}", response.code(), responseBody);return null;}// 打印成功的响应String jsonResponse = response.body() != null ? response.body().string() : "响应体为空";log.info("成功获取 access_token,响应:{}", jsonResponse);// 使用 Gson 解析 JSON 数据并封装成 AccessTokenResponse 对象Gson gson = new Gson();// 返回封装的对象return gson.fromJson(jsonResponse, AccessTokenResponse.class);} catch (Exception e) {log.error(e.getMessage(), e);// 返回 null 或者其他错误处理return null;}}
}
5.WxLoginAutoConfiguration.java 自动配置类
package cn.sunxiansheng.wx.login.config;import cn.sunxiansheng.wx.login.config.properties.WxLoginProperties;
import cn.sunxiansheng.wx.login.utils.WxLoginUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;import javax.annotation.PostConstruct;/*** Description: 微信登录自动配置类** @Author sun* @Create 2025/1/13 16:11* @Version 1.0*/
@Configuration
@EnableConfigurationProperties({WxLoginProperties.class})
@Slf4j
public class WxLoginAutoConfiguration {/*** 自动配置成功日志*/@PostConstructpublic void logConfigSuccess() {log.info("WxLoginAutoConfiguration has been loaded successfully!");}/*** 注入WxLoginUtil** @return*/@Bean@ConditionalOnMissingBeanWxLoginUtil wxLoginUtil() {return new WxLoginUtil();}
}
6.spring.factories 激活自动配置类
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
cn.sunxiansheng.wx.login.config.WxLoginAutoConfiguration

3.combinations-wx-starter-demo

1.目录结构

CleanShot 2025-01-23 at 21.25.48@2x

2.pom.xml 引入依赖
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><parent><groupId>cn.sunxiansheng</groupId><artifactId>sunrays-combinations-demo</artifactId><version>1.0.0</version></parent><artifactId>combinations-wx-starter-demo</artifactId><dependencies><!-- combinations-wx-login-starter --><dependency><groupId>cn.sunxiansheng</groupId><artifactId>combinations-wx-login-starter</artifactId><version>1.0.0</version></dependency><!-- common-web-starter --><dependency><groupId>cn.sunxiansheng</groupId><artifactId>common-web-starter</artifactId><version>1.0.0</version></dependency></dependencies><!-- maven 打包常规配置 --><build><!-- 打包成 jar 包时的名字为项目的artifactId + version --><finalName>${project.artifactId}-${project.version}</finalName><plugins><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId><!-- 引用父模块中统一管理的插件版本(与SpringBoot的版本一致! --><executions><execution><goals><!-- 将所有的依赖包都打到这个模块中 --><goal>repackage</goal></goals></execution></executions></plugin></plugins></build>
</project>
3.application.yml 配置AppID和AppSecret
sun-rays:log4j2:home: /Users/sunxiansheng/IdeaProjects/sunrays-framework/sunrays-combinations-demo/combinations-wx-starter-demo/logs # 日志存储根目录env:path: /Users/sunxiansheng/IdeaProjects/sunrays-framework/sunrays-combinations-demo/combinations-wx-starter-demo # .env文件的绝对路径wx:login:app-id: ${WX_LOGIN_APP_ID} # 微信开放平台应用的AppIDapp-secret: ${WX_LOGIN_APP_SECRET} # 微信开放平台应用的AppSecret
spring:profiles:active: prod # 激活的环境
4.application-prod.yml 配置生产环境的日志和.env文件路径
sun-rays:log4j2:home: /www/wwwroot/sunrays-framework/logs # 日志存储根目录env:path: /www/wwwroot/sunrays-framework # .env文件的绝对路径
5.CodeAndState.java 接受code和state的bean
package cn.sunxiansheng.wx.entity;import lombok.Data;/*** Description: 接受code和state的bean** @Author sun* @Create 2025/1/16 19:15* @Version 1.0*/@Data
public class CodeAndState {/*** 微信的code*/private String code;/*** 微信的state*/private String state;
}
6.WxLoginController.java 微信登录Controller
package cn.sunxiansheng.wx.controller;import cn.sunxiansheng.wx.entity.CodeAndState;
import cn.sunxiansheng.wx.login.utils.WxLoginUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;import javax.annotation.Resource;/*** Description: 微信登录Controller** @Author sun* @Create 2025/1/13 16:26* @Version 1.0*/
@Slf4j
@RestController
@RequestMapping("/wx")
public class WxLoginController {@Resourceprivate WxLoginUtil wxLoginUtil;@RequestMapping("/test")public String test() {return "test";}/*** 微信登录** @param codeAndState 前端传过来的code和state* @return 返回unionId*/@RequestMapping("/login")public String login(@RequestBody CodeAndState codeAndState) {// 使用code来完成微信登录WxLoginUtil.AccessTokenResponse accessTokenResponse = wxLoginUtil.wxLogin(codeAndState.getCode());if (accessTokenResponse == null) {log.error("accessToken is null");return "null";}// 获取unionIdString unionId = accessTokenResponse.getUnionId();if (unionId == null) {log.error("unionId is null");return "null";}// 获取unionIdreturn accessTokenResponse.getUnionId();}
}
7.WxApplication.java 启动类
package cn.sunxiansheng.wx;import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;/*** Description: 微信启动类** @Author sun* @Create 2025/1/13 17:54* @Version 1.0*/
@SpringBootApplication
public class WxApplication {public static void main(String[] args) {SpringApplication.run(WxApplication.class, args);}
}

4.微信登录流程梳理

1.用户点击微信登录按钮

CleanShot 2025-01-23 at 21.36.20@2x

2.前端向开放平台发送请求主要携带appId和redirectUri
<template><button @click="handleLogin" class="wechat-login-button">微信登录</button>
</template><script>
export default {methods: {handleLogin() {// 从环境变量中获取参数const appId = import.meta.env.VITE_APP_ID; // 从环境变量中读取 appIdconst redirectUri = encodeURIComponent(import.meta.env.VITE_REDIRECT_URI); // 从环境变量中读取 redirectUriconst responseType = 'code';const scope = 'snsapi_login'; // 网页应用固定填写 snsapi_login// 生成一个随机的 state 参数,用于防止 CSRF 攻击const state = Math.random().toString(36).substring(2); // 或者使用更安全的方式生成一个随机字符串// 拼接请求URL,并加入 state 参数const wechatLoginUrl = `https://open.weixin.qq.com/connect/qrconnect?appid=${appId}&redirect_uri=${redirectUri}&response_type=${responseType}&scope=${scope}&state=${state}#wechat_redirect`;// 跳转到微信登录页面window.location.href = wechatLoginUrl;},},
};
</script><style scoped>
.wechat-login-button {background-color: #1aad19;color: white;border: none;border-radius: 5px;padding: 10px 20px;cursor: pointer;transition: background-color 0.3s ease;
}.wechat-login-button:hover {background-color: #128c13;
}
</style>
3.此时开放平台会弹出一个扫码的页面,用户扫码确认

CleanShot 2025-01-23 at 21.36.33@2x

4.用户确认成功后,开放平台会将code和state作为参数去请求redirectUri(前端页面)

CleanShot 2025-01-23 at 21.36.47@2x

5.前端页面获取code和state,再向后端发送请求
<template><div class="login-container"><div class="loading-spinner"></div><p class="loading-text">微信登录中,请稍候...</p></div>
</template><script>
export default {async mounted() {const urlParams = new URLSearchParams(window.location.search);const code = urlParams.get("code");const state = urlParams.get("state");if (!code) {console.error("未获取到微信返回的 code");alert("登录失败,请重试");return;}try {const response = await fetch("/wx/login", {method: "POST",headers: {"Content-Type": "application/json",},body: JSON.stringify({ code, state }),});const result = await response.json();if (result.success) {const unionid = result.data;alert(`登录成功,您的unionid是:${unionid}`);this.$router.push({ path: "/products" });} else {alert("登录失败,请重试");}} catch (error) {console.error("请求失败", error);alert("网络错误,请稍后重试");}},
};
</script><style scoped>
@import url("https://fonts.googleapis.com/css2?family=Poppins:wght@400;600&display=swap");:root {--primary-color: #4facfe;--secondary-color: #00f2fe;--text-color: #333;
}.login-container {display: flex;flex-direction: column;align-items: center;justify-content: center;height: 100vh;background: linear-gradient(120deg, #ffffff, #f0f0f0);font-family: "Poppins", sans-serif;
}.loading-spinner {width: 60px;height: 60px;border: 6px solid #e0e0e0;border-top: 6px solid var(--primary-color);border-radius: 50%;animation: spin 1s linear infinite;
}@keyframes spin {0% {transform: rotate(0deg);}100% {transform: rotate(360deg);}
}.loading-text {margin-top: 20px;font-size: 18px;font-weight: 500;color: var(--text-color);animation: fadeIn 2s ease-in-out infinite alternate;
}@keyframes fadeIn {0% {opacity: 0.6;}100% {opacity: 1;}
}
</style>
6.后端使用code进行微信登录,可以获取到AccessTokenResponse
/*** 微信登录的响应类*/
@Data
public static class AccessTokenResponse {@SerializedName("access_token")private String accessToken;@SerializedName("expires_in")private Integer expiresIn;@SerializedName("refresh_token")private String refreshToken;@SerializedName("openid")private String openId;@SerializedName("scope")private String scope;@SerializedName("unionid")private String unionId;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/web/67729.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

DeepSeek 介绍及对外国的影响

DeepSeek 简介 DeepSeek&#xff08;深度求索&#xff09;是一家专注实现 AGI&#xff08;人工通用智能&#xff09;的中国科技公司&#xff0c;2023 年成立&#xff0c;总部位于杭州&#xff0c;在北京设有研发中心。与多数聚焦具体应用&#xff08;如人脸识别、语音助手&…

MySQL数据库(二)- SQL

目录 ​编辑 一 DDL (一 数据库操作 1 查询-数据库&#xff08;所有/当前&#xff09; 2 创建-数据库 3 删除-数据库 4 使用-数据库 (二 表操作 1 创建-表结构 2 查询-所有表结构名称 3 查询-表结构内容 4 查询-建表语句 5 添加-字段名数据类型 6 修改-字段数据类…

ARM嵌入式学习--第十天(UART)

--UART介绍 UART(Universal Asynchonous Receiver and Transmitter)通用异步接收器&#xff0c;是一种通用串行数据总线&#xff0c;用于异步通信。该总线双向通信&#xff0c;可以实现全双工传输和接收。在嵌入式设计中&#xff0c;UART用来与PC进行通信&#xff0c;包括与监控…

面试题-消失的数字-异或

消失的数字 数组nums包含从0到n的所有整数&#xff0c;但其中缺了一个。请编写代码找出那个缺失的整数。你有办法在 O(n) 时间内完成吗&#xff1f; 示例&#xff1a; 输入&#xff1a;[3,0,1] 输出&#xff1a;2 int missingNumber(int* nums, int numsSize) {}分析 本题对…

数据结构与算法之栈: LeetCode 1685. 有序数组中差绝对值之和 (Ts版)

有序数组中差绝对值之和 https://leetcode.cn/problems/sum-of-absolute-differences-in-a-sorted-array/description/ 描述 给你一个 非递减 有序整数数组 nums 请你建立并返回一个整数数组 result&#xff0c;它跟 nums 长度相同&#xff0c;且result[i] 等于 nums[i] 与数…

笔试-排列组合

应用 一个长度为[1, 50]、元素都是字符串的非空数组&#xff0c;每个字符串的长度为[1, 30]&#xff0c;代表非负整数&#xff0c;元素可以以“0”开头。例如&#xff1a;[“13”, “045”&#xff0c;“09”&#xff0c;“56”]。 将所有字符串排列组合&#xff0c;拼起来组成…

Python3 OS模块中的文件/目录方法说明十七

一. 简介 前面文章简单学习了 Python3 中 OS模块中的文件/目录的部分函数。 本文继续来学习 OS 模块中文件、目录的操作方法&#xff1a;os.walk() 方法、os.write()方法 二. Python3 OS模块中的文件/目录方法 1. os.walk() 方法 os.walk() 方法用于生成目录树中的文件名&a…

[Java]抽象类

1. 什么是抽象类&#xff1f; 1.1 定义&#xff1a; 抽象类是一个不能实例化的类&#xff0c;它是用来作为其他类的基类的。抽象类可以包含抽象方法和非抽象方法。抽象方法没有方法体&#xff0c;子类必须重写这些方法并提供具体的实现。抽象类可以有构造方法、成员变量、静态…

css三角图标

案例三角&#xff1a; <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title>Document</title><s…

跨越通信障碍:深入了解ZeroMQ的魅力

在复杂的分布式系统开发中&#xff0c;进程间通信就像一座桥梁&#xff0c;连接着各个独立运行的进程&#xff0c;让它们能够协同工作。然而&#xff0c;传统的通信方式往往伴随着复杂的设置、高昂的性能开销以及有限的灵活性&#xff0c;成为了开发者们前进道路上的 “绊脚石”…

深入解析 COUNT(DISTINCT) OVER(ORDER BY):原理、问题与高效替代方案

目录 一、累计去重需求场景 二、COUNT(DISTINCT) OVER(ORDER BY) 语法解析 2.1 基础语法 2.2 执行原理 三、三大核心问题分析

线性数据结构:单向链表

放弃眼高手低&#xff0c;你真正投入学习&#xff0c;会因为找到一个新方法产生成就感&#xff0c;学习不仅是片面的记单词、学高数......只要是提升自己的过程&#xff0c;探索到了未知&#xff0c;就是学习。 目录 一.链表的理解 二.链表的分类&#xff08;重点理解&#xf…

基于PyQt5打造的实用工具——PDF文件加图片水印,可调大小位置,可批量处理!

01 项目简介 &#xff08;1&#xff09;项目背景 随着PDF文件在信息交流中的广泛应用&#xff0c;用户对图片水印的添加提出了更高要求&#xff0c;既要美观&#xff0c;又需高效处理批量文件。现有工具难以实现精确调整和快速批量操作&#xff0c;操作繁琐且效果不理想。本项…

MCU内部ADC模块误差如何校准

本文章是笔者整理的备忘笔记。希望在帮助自己温习避免遗忘的同时&#xff0c;也能帮助其他需要参考的朋友。如有谬误&#xff0c;欢迎大家进行指正。 一、ADC误差校准引言 MCU 片内 ADC 模块的误差总包括了 5 个静态参数 (静态失调&#xff0c;增益误差&#xff0c;微分非线性…

嵌入式硬件篇---CPUGPUTPU

文章目录 第一部分&#xff1a;处理器CPU&#xff08;中央处理器&#xff09;1.通用性2.核心数3.缓存4.指令集5.功耗和发热 GPU&#xff08;图形处理器&#xff09;1.并行处理2.核心数量3.内存带宽4.专门的应用 TPU&#xff08;张量处理单元&#xff09;1.为深度学习定制2.低精…

03-机器学习-数据获取

一、流行机器学习数据集 主流机器学习数据集汇总 数据集名称描述来源MNIST手写数字图像数据集&#xff0c;由美国人口普查局员工书写。MNIST官网ImageNet包含数百万张图像&#xff0c;用于图像分类和目标检测。ImageNet官网AudioSet包含YouTube音频片段&#xff0c;用于声音分…

doris:STRUCT

STRUCT<field_name:field_type [COMMENT comment_string], ... > 表示由多个 Field 组成的结构体&#xff0c;也可被理解为多个列的集合。 不能作为 Key 使用&#xff0c;目前 STRUCT 仅支持在 Duplicate 模型的表中使用。一个 Struct 中的 Field 的名字和数量固定&…

一次端口监听正常,tcpdump无法监听到指定端口报文问题分析

tcpdump命令&#xff1a; sudo tcpdump -i ens2f0 port 6471 -XXnnvvv 下面是各个部分的详细解释&#xff1a; 1.tcpdump: 这是用于捕获和分析网络数据包的命令行工具。 2.-i ens2f0: 指定监听的网络接口。ens2f0 表示本地网卡&#xff09;&#xff0c;即计算机该指定网络接口捕…

“新月智能武器系统”CIWS,开启智能武器的新纪元

新月人物传记&#xff1a;人物传记之新月篇-CSDN博客 相关文章链接&#xff1a;星际战争模拟系统&#xff1a;新月的编程之道-CSDN博客 新月智能护甲系统CMIA--未来战场的守护者-CSDN博客 “新月之智”智能战术头盔系统&#xff08;CITHS&#xff09;-CSDN博客 目录 智能武…

实验六 项目二 简易信号发生器的设计与实现 (HEU)

声明&#xff1a;代码部分使用了AI工具 实验六 综合考核 Quartus 18.0 FPGA 5CSXFC6D6F31C6N 1. 实验项目 要求利用硬件描述语言Verilog&#xff08;或VHDL&#xff09;、图形描述方式、IP核&#xff0c;结合数字系统设计方法&#xff0c;在Quartus开发环境下&#xff…