电脑端(pc)前端vue,后端springboot如何实现微信扫描登录
先考一下我做的效果点击登录成功后拿到头像和基本信息不用去问AI了AI没用搞不定这种的耐心看下去吧想自己看效果的可以点击链接点透办公-微信登录实现思路1.必须注册微信开放平台去申请微信登录需要的appkey和appsert2.最好去申请一下域名登录只能域名操作如果想自己本地玩一下的就使用内网穿透去配置我自己试过内网穿透比较麻烦进入开发平台后点击创建应用pc端的必须是网站应用不要搞错了上面的配置好了点击应用进去拿到自己的appKey和秘钥下面就开始代码了步骤1.接口api/user/getWxLoginUrl?keywx_code:mpamfustfko5nq2xd 随机生成key去拿到跳转微信授权登录的页面url;2.前端打开url我下面直接用的是window.open打开设置一下窗口高度和宽度就行了3.打开收录成功后微信会调用回调通知的接口接口在第一步自己设置通知我们的后端获取到code后续会根据code去拿到用户信息比如昵称,openId头像等比如我这里拿到微信返回给后端的code,直接根据这个code去调用登录接口/api/user/wxLogin?authCode0612H0Ga10WOIL0dVcGa17zK5342H0G14.拿到用户信息走自己的登录业务逻辑即可微信通知后端返回code是如何处理code的目前我这里用的是socket长连接如果不想自己麻烦的就前端定时轮询调用后端去查看code是否返回后端把返回的code放redis里面就行了如果使用soket那微信回到通知后端后端就把code传给前端前端调用登录接口直接登录并且关闭弹窗即可前端代码async function wxLogin() { if(!wxLoginChecked.value){ ElMessage({ message: 未勾选用户协议, type: error, customClass:zZindex }) return; } try { // 生成唯一键 var key generateUniqueString(wx_code:); // 从后端获取微信登录二维码URL不暴露appId等敏感信息 const res await useClientRequest{ code: number; data?: { loginUrl: string; appId: number }; msg?: string }( api/user/getWxLoginUrl, { query: { key } } ); if (res.code ! 200 || !res.loginUrl) { ElMessage({ message: res.msg || 获取微信登录二维码失败, type: error, }); return; } //类似https://open.weixin.qq.com/connect/qrconnect?appidwx19df0fb3e3ba3f08redirect_urihttps%3A%2F%2Foffice.zjdiante.com%2Fprod-api%2Fapi%2Fuser%2FnotifyWxCode%2 //这个链接后端根据引用appId和秘钥去组装返回的直接打开微信弹窗 const loginUrl res.loginUrl; // 打开微信登录二维码窗口 const screenWidth window.screen.width; const screenHeight window.screen.height; const width 700; const height 440; const left (screenWidth - width) / 2; const top (screenHeight - height) / 2; const features width${width},height${height},left${left},top${top}; newWindow window.open(loginUrl, QRCodeWindow, features); // 开启socket连接等待微信回调 if (typeof window ! undefined typeof WebSocket ! undefined) { connectWebSocket(key); } wsUserId.value key; } catch (error) { console.error(微信登录失败:, error); ElMessage({ message: 获取微信登录二维码失败请稍后重试, type: error, }); } } const receivedCode ref([]); function connectWebSocket(userId: string) { // 如果已有连接先关闭 if (wsManager) { wsManager.close(); } // 创建新的 WebSocket 连接 wsManager useWebSocket({ url: websocketUrl userId, autoReconnect: true, reconnectInterval: 3000, maxReconnectAttempts: 5, heartbeatInterval: 30000, onOpen: () { console.log(WebSocket 连接成功); wsManager?.send(发送微信登录的code userId userId); }, onMessage: async (data: string) { console.log(socket返回的消息:, data); receivedCode.value data; //登录接口 //loginType 登录类型 0:微信 1手机号 //authCode 微信code码 //appId 不再从前端传递后端使用默认appId const res await useClientRequest{ code: number; token?: string; msg?: string }(api/user/wxLogin, {method: post, query: {authCode: data, loginType: 0}} ) if (res.code 200 res.token) { ElMessage({ message: 登录成功, type: success, }) token.value Bearer res.token await userStore.getUserInfo(Bearer res.token) loginDialogVisible.value false if (newWindow) { newWindow.close(); } wsManager?.close(); } }, onError: (error) { console.error(WebSocket 出错:, error); }, onClose: () { console.log(WebSocket 连接关闭); } }); wsManager.connect(); }; onUnmounted(() { if (wsManager) { wsManager.close(); } }); //处理微信登录弹窗 function closeWxDialog(){ if(pollTimer!null){ clearInterval(pollTimer); //清除定时器 } if(newWindow!null){ newWindow.close(); } //处理socket连接 if (wsManager) { wsManager.close(); } }后端代码获取微信弹窗的链接/** * 获取微信登录二维码URL * 不暴露appId等敏感信息由后端生成完整的登录URL * param key 前端生成的唯一键用于标识本次登录请求 * return 微信登录二维码URL */ GetMapping(/getWxLoginUrl) ApiOperation(value 获取微信登录二维码URL, notes 获取微信登录二维码URL不暴露敏感信息, httpMethod GET) public AjaxResult getWxLoginUrl(RequestParam(key) String key) { try { // 使用默认appId从配置或数据库获取这里使用5作为默认值 // 实际项目中应该从配置文件读取默认appId Long defaultAppId 5L; App app appService.selectAppByAppId(defaultAppId); if (ObjectUtils.isEmpty(app) || StringUtils.isEmpty(app.getWxAppId())) { return AjaxResult.error(微信登录配置未找到); } // 构建回调地址 String redirectUri https://www.xxxx.com/prod-api/api/user/notice/?key key; // 构建微信登录URL String wxLoginUrl https://open.weixin.qq.com/connect/qrconnect ?appid app.getWxAppId() redirect_uri URLEncoder.encode(redirectUri, StandardCharsets.UTF_8) response_typecode scopesnsapi_login stateSTATE#wechat_redirect; return AjaxResult.success().put(loginUrl, wxLoginUrl).put(appId, defaultAppId); } catch (Exception e) { log.error(获取微信登录URL失败, e); return AjaxResult.error(获取微信登录URL失败); } }微信异步通知的接口, 这里很重要用户授权成功后微信会通知这个接口拿到code, 如果你不想使用soket,那前端就定时2秒或者3秒轮询查询这个接口去找redis里面的code只能根据key查询存在的就说明用户已经授权了直接登录注意这个key是掉i用接口getWxLoginUrl获取弹窗链接 时前端自定义的而且需要唯一后端需要用这个去作为redis的键code作为值缓存/** * 获取异步通知的微信code * param key 前端点击登录按钮时产生的唯一键 * param code */ GetMapping(/notice) public void notice(RequestParam(key)String key,RequestParam(code)String code){ //有效时间两分钟 redisCache.setCacheObject(key, code, 120, TimeUnit.SECONDS); //发消息给用户 webSocketServer.sendOneMessage(key,code); }拿到code前端关闭弹窗去做自己的登录逻辑RepeatSubmit PostMapping(value /wxLogin) ResponseBody ApiOperation(value 微信授权登录接口, notes 接口描述, httpMethod POST) public AjaxResult wxLogin(UserLoginDTO form) { //微信登录 if(0.equals(form.getLoginType())){ if(StringUtils.isEmpty(form.getAuthCode())){ return AjaxResult.error(code参数不能为空); } // 不再从前端获取appId使用默认appId从配置或数据库获取 // 如果前端传递了appId则使用传递的否则使用默认值5 Long appId form.getAppId() ! null ? Long.valueOf(form.getAppId()) : 5L; App app appService.selectAppByAppId(appId); if (ObjectUtils.isEmpty(app)) { return AjaxResult.error(应用配置未找到); } String url https://api.weixin.qq.com/sns/oauth2/access_token ?appid app.getWxAppId() secret app.getWxAppSert() code form.getAuthCode() grant_typeauthorization_code; String result HttpUtils.sendGet(url); JSONObject resultObject JSON.parseObject(result); if(ObjectUtils.isNotEmpty(resultObject.getString(errcode))){ return AjaxResult.error(登录失败); } String openId resultObject.getString(openid); String accessToken resultObject.getString(access_token); //获取微信用户信息 String infoUrl https://api.weixin.qq.com/sns/userinfo ?access_token accessToken openid openId langzh_CN; String resultInfo HttpUtils.sendGet(infoUrl); JSONObject jsonObject JSON.parseObject(resultInfo); if(ObjectUtils.isNotEmpty(jsonObject.getString(errcode))){ return AjaxResult.error(登录失败); } //查看用户是否已绑定手机号 https://developers.weixin.qq.com/doc/oplatform/openApi/OpenApiDoc/cloudbase-common/admin-management/checkMobileConfig.html //查询用户是否存在 User user userService.selectUserByOpenId(openId, appId); if(ObjectUtils.isEmpty(user)){ usernew User(); user.setSex(jsonObject.getString(sex));//1 为男性2 为女性 user.setProvince(jsonObject.getString(province)); user.setCity(jsonObject.getString(city)); user.setHeadImg(jsonObject.getString(headimgurl)); user.setUnionId(jsonObject.getString(unionid)); user.setWxOpenId(jsonObject.getString(openid)); user.setNickName(jsonObject.getString(nickname)); user.setCreateTime(new Date()); user.setLoginType(1); user.setAppId(appId); user.setPassword(SecurityUtils.encryptPassword(123456)); user.setPhoneRelease(form.getRelease()); user.setBrand(form.getBrand()); userService.insertUser(user); }else{ user.setSex(jsonObject.getString(sex));//1 为男性2 为女性 user.setProvince(jsonObject.getString(province)); user.setCity(jsonObject.getString(city)); user.setHeadImg(jsonObject.getString(headimgurl)); user.setUnionId(jsonObject.getString(unionid)); user.setWxOpenId(jsonObject.getString(openid)); user.setNickName(jsonObject.getString(nickname)); user.setUpdateTime(new Date()); user.setPhoneRelease(form.getRelease()); user.setBrand(form.getBrand()); userService.updateUser(user); } //将值赋给security 创建token String token loginService.login(String.valueOf(user.getId()),123456); //将值赋给security 创建token return AjaxResult.success(user).put(token,token); } return AjaxResult.error(服务正忙登录失败); }