在前端页面中引入 vue-qrcode 和 weixin-js-sdk 库,并使用以下代码生成二维码:
<template> <div id="qrcode"></div> </template> <script> import VueQrcode from 'vue-qrcode' import wx from 'weixin-js-sdk' export default { name: 'Qrcode', components: { VueQrcode, }, data() { return { url: '', } }, mounted() { this.generateQrcode() this.initWechat() }, methods: { generateQrcode() { // 生成二维码 const url = 'https://example.com/qrcode' this.url = url }, initWechat() { // 初始化微信 JS SDK const url = window.location.href.split('#')[0] axios.get(`/api/wechat/jsApiConfig?url=${encodeURIComponent(url)}`) .then(response => { const { data } = response.data wx.config({ debug: false, // 开启调试模式 appId: data.appId, timestamp: data.timestamp, nonceStr: data.nonceStr, signature: data.signature, jsApiList: ['scanQRCode'], }) wx.ready(() => { // 监听扫描事件 wx.scanQRCode({ needResult: 1, scanType: ['qrCode', 'barCode'], success: (result) => { const { resultStr } = result this.handleScan(resultStr) }, fail: (error) => { console.error(error) }, }) }) }) .catch(error => { console.error(error) }) }, handleScan(resultStr) { // 发送请求,获取 openid axios.post('/api/wechat/authWithQrCode', { resultStr }) .then(response => { const { code, msg, data } = response.data if (code === 200) { // 已经登录,跳转到首页 window.location.replace('/') } else if (code === 400) { // 未登录,跳转到绑定页面 window.location.replace(`https://open.weixin.qq.com/connect/oauth2/authorize?appid=wx123456789abcdef&redirect_uri=http://example.com/bindUser&response_type=code&scope=snsapi_userinfo&state=${data}&connect_redirect=1#wechat_redirect`) } else { alert(msg) } }) .catch(error => { console.error(error) }) }, }, } </script>
后端实现微信公众平台的认证、验证签名等操作,并且能够处理 /api/wechat/authWithQrCode 接口。
@RestController @RequestMapping("/api/wechat") public class WechatController { @Autowired private WxMpService wxMpService; @GetMapping("/jsApiConfig") public ResultEntity<WxJsapiSignature> jsApiConfig(@RequestParam String url) { try { WxJsapiSignature signature = wxMpService.createJsapiSignature(url); return ResultEntity.success(signature); } catch (WxMpException e) { e.printStackTrace(); return ResultEntity.error("获取签名失败:" + e.getMessage()); } } @PostMapping("/authWithQrCode") public ResultEntity<String> authWithQrCode(@RequestBody Map<String, String> params) { String resultStr = params.get("resultStr"); try { // 校验请求是否合法,获取 openid WxMpOAuth2AccessToken accessToken = wxMpService.oauth2getAccessToken(resultStr); String openid = accessToken.getOpenId(); // 查询数据库,判断该 openid 是否已经绑定过账户 User user = userService.selectUserByOpenid(openid); if (user != null) { // 已经登录,返回登录信息 String token = JwtUtils.generateToken(user); return ResultEntity.success(token); } else { // 未登录,返回绑定凭证 String state = UUID.randomUUID().toString(); redisService.set(state, openid, Duration.ofMinutes(5)); return ResultEntity.error(400, "请先绑定账户", state); } } catch (WxErrorException e) { e.printStackTrace(); return ResultEntity.error("请求微信接口失败:" + e.getMessage()); } } }
绑定页面 /bindUser 显示用户绑定表单,并上传用户的 openid 和其他必填信息。
后端实现 /bindUser 接口,更新用户信息并返回自动登录信息。
@RestController public class UserController { @Autowired private UserService userService; @Autowired private RedisService redisService; @PostMapping("/bindUser") public void bindUser(HttpServletRequest request, HttpServletResponse response) throws IOException { String code = request.getParameter("code"); String state = request.getParameter("state"); // 根据授权码获取 openid String openid = null; try { WxMpOAuth2AccessToken accessToken = wxMpService.oauth2getAccessToken(code); openid = accessToken.getOpenId(); } catch (WxErrorException e) { e.printStackTrace(); response.sendRedirect("/error"); return; } // 检查绑定凭证是否正确 String storedOpenid = redisService.get(state); if (!openid.equals(storedOpenid)) { response.sendRedirect("/error"); return; } // 处理用户绑定表单 User user = userService.selectUserByOpenid(openid); if (user == null) { user = new User(); user.setOpenid(openid); } user.setName(request.getParameter("name")); user.setGender(request.getParameter("gender")); user.setMobile(request.getParameter("mobile")); userService.saveOrUpdate(user); // 生成 token 并返回自动登录信息 String token = JwtUtils.generateToken(user); response.sendRedirect(String.format("/autoLogin?token=%s", token)); } }
自动登录页面 /autoLogin 接收 token 参数,将 token 存入 cookie 中,并重定向到首页。
<template> <div></div> </template> <script> import { setCookie } from '@/utils/cookie' export default { name: 'AutoLogin', props: { token: { type: String, required: true, }, }, mounted() { setCookie('token', this.token, 1) window.location.replace('/') }, } </script>