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(如 refreactiveonMounted 等),减少手动导入的代码。
  • 减少代码量:不用手动写 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

注册后进行配置

vue3 + springboot实现微信登录

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

vue3 + springboot实现微信登录

内网穿透(可选

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

vue3 + springboot实现微信登录

结果演示

vue3 + springboot实现微信登录

vue3 + springboot实现微信登录

发表评论

评论已关闭。

相关文章