vue3 + springboot实现微信登录
创建VUE3项目
创建初始文件
进入项目存放位置
右键用命令行打开(终端打开)
npm create vite@latest wechat-report --template vue
npm:包管理需要安装node.js
Vite:用于热部署和生成、打包项目
--template vue:模板指定为vue
可能报错: 因为在此系统上禁止运行脚本。有关详细信息
会提示是否继续:
Ok to proceed? (y)
输入y回车
会让选择版本和类型:
如:◆ Select a framework:
│ ○ Vanilla
│ ● Vue
│ ○ React
用方向键移动到VUE,回车
用方向键移动到JavaScript ,回车
移动到创建好的项目中初始化
cd wechat-report
npm install
安装Element Plus
# 安装Element Plus
npm install element-plus @element-plus/icons-vue
# 安装自动导入插件(可选但推荐)
npm install -D unplugin-vue-components unplugin-auto-import
第一行安装elementPlus 和图标库
第二行是自动导入插件:自动按需导入 Vue 组件(如 Element Plus 的 <el-button>),无需手动 import
unplugin-auto-import:自动导入 Vue 相关的 API(如ref、reactive、onMounted等),减少手动导入的代码。
- 减少代码量:不用手动写
import { ElButton } from 'element-plus'。 - 优化打包体积:只打包实际用到的组件,避免全量引入。
配置项目
进入项目,找到
修改vite.config.js:
设置elementui,@/路径功能,后台地址映射
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import { fileURLToPath, URL } from 'node:url'
import AutoImport from 'unplugin-auto-import/vite'
import Components from 'unplugin-vue-components/vite'
import { ElementPlusResolver } from 'unplugin-vue-components/resolvers'
// https://vitejs.dev/config/
export default defineConfig({
plugins: [
vue(),
AutoImport({
resolvers: [ElementPlusResolver()],
}),
Components({
resolvers: [ElementPlusResolver()],
}),
],
resolve: {
alias: {
'@': fileURLToPath(new URL('./src', import.meta.url)) // 使用现代ESM方式
}
},
server: {
proxy: {
'/api': {
target: 'http://your-backend-api.com', // 替换为实际后端地址
changeOrigin: true,
rewrite: path => path.replace(/^\/api/, '')
}
}
}
})
安装微信JSSDK
npm install weixin-js-sdk
创建API服务
在src/api目录下创建wechat.js:
import axios from 'axios'
const service = axios.create({
baseURL: '/api', // 根据实际后端API地址配置
timeout: 5000
})
// 获取校验url,用于登录
export function getAuthorizationUrl(url) {
return service({
method: 'post',
url: '/api/emergency/wxLoginReport/getAuthorizationUrl',
data: { url }
})
}
// 其他API...
修改App.vue
清空内容
<template>
<router-view />
</template>
<script>
export default {
name: 'App',
setup() {
return {}
}
}
</script>
<style>
/* 全局样式 */
#app {
font-family: Avenir, Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
color: #2c3e50;
max-width: 100vw;
min-height: 100vh;
background-color: #f5f5f5;
}
</style>
配置路由
安装Vue Router:
npm install vue-router@4
创建src/router/index.js:
创建登录页面和结果页面
import { createRouter, createWebHashHistory } from 'vue-router'
const routes = [
//登录页面
{
path: '/',
name: 'Home',
component: () => import('@/views/Home.vue')
},
//登录结果
{
path: '/Home2',
name: 'Home2',
component: () => import('@/views/Home2.vue')
},
// 其他路由...
]
const router = createRouter({
history: createWebHashHistory(), // 使用hash模式,兼容微信公众号
routes
})
export default router
创建示例页面
创建src/views/Home.vue:
用于测试微信功能登录
<template>
<div class="home">
<el-card class="box-card">
<template #header>
<div class="card-header">
<span>微信公众号示例</span>
</div>
</template>
<el-button @click="loginAuth">登录</el-button>
</el-card>
</div>
</template>
<script>
import {getAuthorizationUrl} from '@/api/wechat'
export default {
name: 'Home',
setup() {
const loginAuth = () => {
// let url = window.location.href;
let url = 'http://192.168.10.213:5173/#/Home2';
// console.log(url)
getAuthorizationUrl(url).then(res => {
console.log(res.data.msg)
window.location.href = res.data.msg;
})
};
return {
loginAuth
}
}
}
</script>
<style scoped>
.home {
padding: 20px;
}
.box-card {
max-width: 500px;
margin: 0 auto;
}
.card-header {
font-size: 18px;
font-weight: bold;
}
</style>
创建src/views/Home2.vue:
登录结果页面
<template>
<div class="wechat-auth-result">
<!-- 加载状态 -->
<div v-if="loading" class="loading">加载中...</div>
<!-- 成功状态 -->
<div v-if="userInfo && !error" class="success">
<div class="header">
<h2>微信登录成功</h2>
<p>{{ userInfo.message }}</p>
</div>
<div class="profile">
<img :src="userInfo.headimgurl" alt="用户头像" class="avatar">
<div class="details">
<h3>{{ userInfo.nickname }}</h3>
<!-- <button @click="goToDashboard" class="btn btn-primary">进入首页</button>-->
</div>
</div>
</div>
<!-- 错误状态 -->
<div v-if="error" class="error">
<h2>登录失败</h2>
<p>{{ error }}</p>
<button @click="retryLogin" class="btn btn-secondary">重新登录</button>
</div>
</div>
</template>
<script>
import {ref, onMounted} from 'vue';
import {useRoute, useRouter} from 'vue-router';
export default {
setup() {
const route = useRoute();
const router = useRouter();
const userInfo = ref(null);
const loading = ref(true);
const error = ref(null);
// 获取并处理URL参数
const parseUserInfo = () => {
try {
if (route.query.openid) {
userInfo.value = {
openid: route.query.openid,
nickname: decodeURIComponent(route.query.nickname || '未知用户'),
headimgurl: decodeURIComponent(route.query.headimgurl || ''),
message: decodeURIComponent(route.query.msg || '登录成功')
};
// 清理URL
window.history.replaceState({}, '', window.location.pathname);
// 这里可以添加将用户信息存储到Vuex/Pinia或发送到后端验证的逻辑
} else {
error.value = '未获取到用户信息';
}
} catch (e) {
error.value = '解析用户信息失败: ' + e.message;
} finally {
loading.value = false;
}
};
// const goToDashboard = () => {
// router.push('/dashboard');
// };
const retryLogin = () => {
router.push('/');
};
onMounted(() => {
parseUserInfo();
});
return {userInfo, loading, error, retryLogin};
}
};
</script>
<style scoped>
.wechat-auth-result {
max-width: 500px;
margin: 0 auto;
padding: 20px;
}
.loading {
text-align: center;
padding: 40px 0;
}
.success .header {
text-align: center;
margin-bottom: 30px;
}
.profile {
display: flex;
align-items: center;
gap: 20px;
margin-top: 20px;
}
.avatar {
width: 80px;
height: 80px;
border-radius: 50%;
object-fit: cover;
}
.details {
flex: 1;
}
.error {
color: #dc3545;
text-align: center;
padding: 40px 0;
}
.btn {
margin-top: 15px;
padding: 8px 16px;
border-radius: 4px;
cursor: pointer;
}
.btn-primary {
background-color: #007bff;
color: white;
border: none;
}
.btn-secondary {
background-color: #6c757d;
color: white;
border: none;
}
</style>
修改main.js
import { createApp } from 'vue'
import App from './App.vue'
import router from './router'
// 引入Element Plus样式
import 'element-plus/dist/index.css'
const app = createApp(App)
app.use(router)
app.mount('#app')
package.json
加上--host,用与显示内容
"scripts": {
"dev": "vite --host"
}
npm run dev 启动
springboot后台
controller层
用于拼接微信调用地址与处理回调信息
package yunline.controller;
import yunline.entity.TdSspUserEntity;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import yunline.base.ActionResult;
import yunline.service.TdSspUserService;
import yunline.utils.WxReportUtils;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.net.URLDecoder;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.*;
@Api(tags = "应急排班系统", value = "emergency")
@RestController
@RequestMapping("/api/emergency/wxLoginReport")
public class WxReportController {
@Autowired
private TdSspUserService tdSspUserService;
@ApiOperation("获取微信授权地址")
@PostMapping("/getAuthorizationUrl")
public ActionResult getAuthorizationUrl(@RequestBody Map<String, String> params) throws Exception {
System.out.println("进入");
String url = params.get("url");
url = URLEncoder.encode(url, StandardCharsets.UTF_8.toString());
return ActionResult.success(WxReportUtils.getAuthorizationUrl(url));
}
/**
* 当用户授权后,微信会重定向到你指定的URI,并携带一个code参数。你需要捕获这个请求并提取code。
*/
@ApiOperation("捕获微信授权回执")
@GetMapping("/handleAuthorizationCallback")
public void handleAuthorizationCallback(HttpServletRequest request, HttpServletResponse response) throws Exception {
String code = request.getParameter("code");
String state = request.getParameter("state");
Map<String, String[]> c = request.getParameterMap();
if (code == null) {
// 处理错误情况
// response.getWriter().println("授权失败,请重试!");
// return ActionResult.fail("授权失败,请重试!");
String frontendUrl = URLDecoder.decode(state, "UTF-8")
+"?openid="
+"&msg=授权失败!";
response.sendRedirect(frontendUrl);
return ;
}
// 获取access_token和openid
Map<String, String> tokenInfo = WxReportUtils.getTokenInfo(code);
if (tokenInfo == null || !tokenInfo.containsKey("access_token") || !tokenInfo.containsKey("openid")) {
// response.getWriter().println("获取access_token失败,请重试!");
// return ActionResult.fail("获取access_token失败,请重试!");
String frontendUrl = URLDecoder.decode(state, "UTF-8")
+"?openid="
+"&msg=获取access_token失败!";
response.sendRedirect(frontendUrl);
return ;
}
String accessToken = tokenInfo.get("access_token");
String openid = tokenInfo.get("openid");
// 获取用户信息
Map<String, Object> userInfo = WxReportUtils.getUserInfo(accessToken, openid);
if (userInfo == null || !userInfo.containsKey("nickname") || !userInfo.containsKey("headimgurl")) {
// response.getWriter().println("获取用户信息失败,请重试!");
// return ActionResult.fail("获取用户信息失败,请重试!");
String frontendUrl = URLDecoder.decode(state, "UTF-8")
+"?openid="
+"&msg=获取用户信息失败!";
response.sendRedirect(frontendUrl);
return ;
}
// 完成本地用户认证
String nickname = (String) userInfo.get("nickname");
String headimgurl = (String) userInfo.get("headimgurl");
// String sex = (String) userInfo.get("sex");
Map<String, Object> map = new HashMap<>();
int has = tdSspUserService.lambdaQuery().eq(TdSspUserEntity::getRemark, openid).count();
if (has > 0) {
//修改
tdSspUserService.lambdaUpdate().eq(TdSspUserEntity::getRemark, openid)
.set(TdSspUserEntity::getPhotopath,headimgurl)
.set(TdSspUserEntity::getUserId,nickname).update();
}else{
//创建
TdSspUserEntity entity = new TdSspUserEntity();
entity.setUserId(nickname);
entity.setPhotopath(headimgurl);
entity.setRemark(openid);
entity.setNickName(nickname);
entity.setUserRegdate(new Date());
tdSspUserService.create(entity);
}
map.put("userInfo", userInfo);
map.put("openid", openid);
// return ActionResult.success(map);
//重定向
String frontendUrl = URLDecoder.decode(state, "UTF-8")
+"?openid=" + openid
+"&msg=登录成功"
+"&nickname="+nickname
+"&headimgurl="+headimgurl
;
response.sendRedirect(frontendUrl);
// return null;
}
}
创建WxReportUtils,用于对接微信
package yunline.utils;
import cn.hutool.json.JSONUtil;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.net.*;
import java.nio.charset.StandardCharsets;
import java.util.Map;
public class WxReportUtils {
private static final String IP = getIP();
private static final String APPID = "你的appid";
private static final String APPSECRET = "你的APPSECRET";
// private static final String SCOPE = "snsapi_base";
// private static final String SCOPE = "snsapi_login";
private static final String SCOPE = "snsapi_userinfo";
private static final String PORT = "28888";
private static final String STATE = "STATE";
private static final String REDIRECT_URI = "后台调用地址,就是controller层的callback";
private static String getIP() {
try {
InetAddress localhost = InetAddress.getLocalHost();
// IP=localhost.getHostAddress();
return localhost.getHostAddress();
} catch (UnknownHostException e) {
e.printStackTrace();
}
return "";
}
public static String getAuthorizationUrl() throws Exception {
return getAuthorizationUrl(STATE);
}
public static String getAuthorizationUrl(String url) throws Exception {
return "https://open.weixin.qq.com/connect/oauth2/authorize?"
+ "appid=" + APPID
+ "&redirect_uri=" + URLEncoder.encode(REDIRECT_URI, StandardCharsets.UTF_8.toString())
+ "&response_type=code"
+ "&scope=" + SCOPE
// + "&state=" + STATE
+ "&state=" + url
+ "#wechat_redirect";
}
/**
* 获取token和openid
*
* @param code
* @return
* @throws Exception
*/
public static Map<String, String> getTokenInfo(String code) throws Exception {
String url = "https://api.weixin.qq.com/sns/oauth2/access_token?"
+ "appid=" + APPID
+ "&secret=" + APPSECRET
+ "&code=" + code
+ "&grant_type=authorization_code";
HttpURLConnection connection = (HttpURLConnection) new URL(url).openConnection();
connection.setRequestMethod("GET");
int responseCode = connection.getResponseCode();
if (responseCode == 200) {
BufferedReader in = new BufferedReader(new InputStreamReader(connection.getInputStream()));
String inputLine;
StringBuilder content = new StringBuilder();
while ((inputLine = in.readLine()) != null) {
content.append(inputLine);
}
in.close();
connection.disconnect();
return JSONUtil.toBean(content.toString(), Map.class, false);
} else {
throw new RuntimeException("请求失败:" + responseCode);
}
}
/**
* 获取用户信息
*
* @param accessToken
* @param openId
* @return
* @throws Exception
*/
public static Map<String, Object> getUserInfo(String accessToken, String openId) throws Exception {
String url = "https://api.weixin.qq.com/sns/userinfo?"
+ "access_token=" + accessToken
+ "&openid=" + openId
+ "&lang=zh_CN";
HttpURLConnection connection = (HttpURLConnection) new URL(url).openConnection();
connection.setRequestMethod("GET");
int responseCode = connection.getResponseCode();
if (responseCode == 200) {
BufferedReader in = new BufferedReader(new InputStreamReader(connection.getInputStream()));
String inputLine;
StringBuilder content = new StringBuilder();
while ((inputLine = in.readLine()) != null) {
content.append(inputLine);
}
in.close();
connection.disconnect();
return JSONUtil.toBean(content.toString(), Map.class, false);
} else {
throw new RuntimeException("请求失败:" + responseCode);
}
}
}
微信配置
进入微信测试地址:
https://mp.weixin.qq.com/debug/cgi-bin/sandboxinfo
注册后进行配置

配置获取用户信息的权限,配置一下域名

内网穿透(可选
这里我使用的是花生壳网穿透获得的外网地址,用了10块钱巨款

结果演示


vue3 + springboot实现微信登录的更多相关文章
- SpringBoot实现微信小程序登录的完整例子
目录 一.登录流程 二.后端实现 1.SpringBoot项目结构树 2.实现auth.code2Session 接口的封装 3.建立用户信息表及用户增删改查的管理 4.实现登录认证及令牌生成 三.前 ...
- springboot如何实现微信登录,前期准备
现在网站用微信登录真的是很多,那么具体是怎么实现的呢? 首先介绍的是微信开放平台,我们如果需要微信登录或者支付都需要在上面注册一个账号,用这个账号去为我们的网站申请的话,需要用到企业资料(家里有营业执 ...
- 利用WxJava实现网站集成微信登录功能,核心代码竟然不超过10行
最近网站PC端集成微信扫码登录,踩了不少坑,在此记录下实现过程和注意事项. 本文目录 一.微信开放平台操作步骤1.创建“网站应用”2.获取AppID和AppSecret二.开发指南三.开发实战1.po ...
- Vue3.0聊天室|vue3+vant3仿微信聊天实例|vue3.x仿微信app界面
一.项目简介 基于Vue3.0+Vant3.x+Vuex4.x+Vue-router4+V3Popup等技术开发实现的仿微信手机App聊天实例项目Vue3-Chatroom.实现了发送图文表情消息/g ...
- 网站实现微信登录之回调函数中登录逻辑的处理--基于yii2开发的描述
上一篇文章网站实现微信登录之嵌入二维码中描述了如何在自己的登录页面内嵌入登录二维码,今天的这篇文章主要是描述下在扫码成功之后微信重定向回网站后登录逻辑的处理,其实也就是验证身份信息,授权用户登录的逻辑 ...
- 网站实现微信登录之嵌入二维码——基于yii2开发的描述
之前写了一篇yii2获取登录前的页面url地址的文章,然后发现自己对于网站实现微信扫码登录功能的实现不是很熟悉,所以,我会写2-3篇的文章来描述下一个站点如何实现微信扫码登录的功能,来复习下微信扫码登 ...
- php的laravel框架快速集成微信登录
最终的解决方案是:https://github.com/liuyunzhuge/php_weixin_provider,详细的介绍请往下阅读. 本文面向的是php语言laravel框架的用户,介绍的是 ...
- 关于使用微信登录第三方APP的实现(Android版)
使用微信登录APP,免去注册过程,现在已经有很多的类似应用了.集成该功能过程不复杂,但还是有一些地方需要注意的. 开始之前,需要做下面的准备工作. 1.到微信开放平台注册你的APP,并申请开通微信登录 ...
- PHP开发网站之微信登录、绑定
)))刷新access_token()); ); ); curl_setopt($curlobj, CURLOPT_SSL_VERIFYPEER, FALSE); curl_setopt($curlo ...
- 网站微信登录授权 ASP.NET
最新做一些项目都有微信登录注册什么的,今天就把自己整理的demo提供给大家 微信认证流程(我自己简称三次握手): 1.用户同意授权,获取code 2.通过code换取网页授权access_token, ...
随机推荐
- SpringBoot前后端接口加解密--解决方案
开放接口 - 通信方式采用HTTP+JSON或消息中间件进行通信. - 调用接口之前需要使用登录鉴权接口获得token. - 当鉴权成功之后才能调用其他接口(携带Token). 登录接口: Code ...
- 面试题-MySQL和Redis(更新版)
前言 MySQL和Redis部分的题目,是我根据Java Guide的面试突击版本V3.0再整理出来的,其中,我选择了一些比较重要的问题,并重新做出相应回答,并添加了一些比较重要的问题,希望对大家起到 ...
- FastAPI Pydantic动态调整Schema
title: FastAPI Pydantic动态调整Schema date: 2025/3/29 updated: 2025/3/29 author: cmdragon excerpt: Pydan ...
- 我对TamperMonkey的不满-更新中
我认为我的电脑上的TamperMonkey插件的值得考虑的不足: 没有提供一个把脚本最小化的功能 不能编辑热键 脚本icon不能使用svg 没有提供一种很好的能够区分别人的脚本和自己的脚本的方式,自己 ...
- 【安卓】使用Handler出现的警告
使用Handler出现的警告 零.原由 安卓中使用Hander时出现了如下警告: This Handler class should be static or leaks might occur (a ...
- Go 中 JSON 的序列化和反序列化
golang中对json的序列化/反序列化操作还是比较容易的, 序列化操作主要是通过encoding/json包的Marshal()方法来实现, 反序列化操作主要是通过encoding/json包的U ...
- FastAPI依赖注入实践:工厂模式与实例复用的优化策略
title: FastAPI依赖注入实践:工厂模式与实例复用的优化策略 date: 2025/04/06 01:22:25 updated: 2025/04/06 01:22:25 author: c ...
- .NET 原生驾驭 AI 新基建实战系列(二):Semantic Kernel 整合对向量数据库的统一支持
1. 引言 在人工智能(AI)应用开发迅猛发展的今天,向量数据库作为存储和检索高维数据的重要工具,已经成为许多场景(如自然语言处理.推荐系统和语义搜索)的核心组件. 对于.NET生态系统的开发者而言, ...
- 康谋分享 | 直面AD/ADAS快速开发挑战:IVEX自动驾驶场景管理及分析平台!
过去十年,自动驾驶和高级驾驶辅助系统 (AD/ADAS) 软件和硬件的开发成为了各大汽车公司的主要投资目标之一.各大汽车公司对 AD/ADAS 持续不断的投资加快了 AD/ADAS 组件的开发周期,但 ...
- MySQL 在设计表(建表)时需要注意什么?
MySQL 在设计表(建表)时需要注意的要点 设计数据库表是 MySQL 开发中非常重要的一环.合理的表结构设计可以提升性能.减少存储开销,并提高维护性.以下是一些关键的注意事项: 1. 明确需求和数 ...