当前位置: 首页 > 工具软件 > ruoyi-vue-pro > 使用案例 >

Ruoyi-Vue是如何实现微信公众号免密登录的

顾骏祥
2023-12-01
  1. 在前端页面中引入 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>

  2. 后端实现微信公众平台的认证、验证签名等操作,并且能够处理 /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());
          }
      }
    }
  3. 绑定页面 /bindUser 显示用户绑定表单,并上传用户的 openid 和其他必填信息。

  4. 后端实现 /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));
      }
    }
  5. 自动登录页面 /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>

 类似资料: