前后端分离下oAuth2微信扫码登录

完整流程:前端传递临时Token方案(跨域友好)

适用场景

  • 前后端完全分离(跨域)
  • 无法使用 Cookie/Session(如移动端、不同域名)
  • 需要更高的灵活性

完整步骤

1. 前端:生成临时 polling_token

目的:让后端知道“当前轮询的是哪个用户”。
流程

  1. 用户点击 “微信登录” 按钮。
  2. 前端先请求后端,获取一个 临时 polling_token(用于后续轮询)。
    // 前端代码
    const getPollingToken = async () => {
      const response = await fetch("https://your-api.com/api/wechat/polling-token");
      const { polling_token } = await response.json();
      localStorage.setItem("wechat_polling_token", polling_token); // 存储临时Token
      return polling_token;
    };
    
  3. 后端生成 polling_token(如UUID),并存入 Redis(5分钟过期)
    // 后端 API(Node.js示例)
    router.get("/api/wechat/polling-token", (req, res) => {
      const pollingToken = generateUUID(); // 生成唯一Token
      await redis.set(`wechat:polling:${pollingToken}`, "pending", "EX", 300); // 5分钟过期
      res.json({ polling_token: pollingToken });
    });
    

2. 前端:生成微信二维码,并携带 polling_token

目的:让微信回调时能关联到当前用户。
流程

  1. 前端使用 polling_token 作为 state 传给微信登录接口:
    // 前端代码(使用微信JS-SDK)
    new WxLogin({
      self_redirect: false,
      id: "wx-login-container",
      appid: "微信APPID",
      scope: "snsapi_login",
      redirect_uri: encodeURIComponent("https://your-api.com/oauth/wechat/callback"),
      state: localStorage.getItem("wechat_polling_token"), // 关键!传递polling_token
    });
    
    • state 参数 会原样返回给后端回调接口,用于关联用户。

3. 用户扫码授权,微信回调后端

目的:微信回调后端,并返回 codestate(即 polling_token)。
流程

  1. 用户扫码后,微信会访问:
    GET https://your-api.com/oauth/wechat/callback?code=XXXX&state=POLLING_TOKEN
    
  2. 后端处理回调
    • code 换取 access_token 和用户信息。
    • state 获取 polling_token,并存储 token 到 Redis。
    // 后端回调处理(Node.js示例)
    router.get("/oauth/wechat/callback", async (req, res) => {
      const { code, state: pollingToken } = req.query;
      
      // 1. 用code换access_token
      const tokenRes = await axios.get(`https://api.weixin.qq.com/sns/oauth2/access_token?appid=APPID&secret=SECRET&code=${code}&grant_type=authorization_code`);
      const { access_token, openid } = tokenRes.data;
      
      // 2. 获取微信用户信息
      const userRes = await axios.get(`https://api.weixin.qq.com/sns/userinfo?access_token=${access_token}&openid=${openid}`);
      const { nickname, headimgurl } = userRes.data;
      
      // 3. 生成自己的JWT token
      const jwtToken = jwt.sign({ userId: openid }, "YOUR_SECRET", { expiresIn: "7d" });
      
      // 4. 关联 pollingToken 和 JWT token
      await redis.set(`wechat:polling:${pollingToken}`, jwtToken);
      
      // 5. 返回成功(前端轮询会检测到)
      res.send(""); // 如果是iframe模式,关闭窗口
    });
    

4. 前端轮询检测登录状态

目的:检查 polling_token 是否已绑定 token
流程

  1. 前端启动轮询,每隔 2秒 请求 /api/auth/status,并携带 polling_token
    // 前端代码
    const checkLoginStatus = async () => {
      const pollingToken = localStorage.getItem("wechat_polling_token");
      const response = await fetch(`https://your-api.com/api/auth/status?polling_token=${pollingToken}`);
      const { loggedIn, token } = await response.json();
      
      if (loggedIn) {
        localStorage.setItem("user_token", token); // 存储正式token
        clearInterval(pollingInterval); // 停止轮询
        window.location.href = "/home"; // 跳转首页
      }
    };
    
    const pollingInterval = setInterval(checkLoginStatus, 2000); // 每2秒轮询一次
    
  2. 后端检查 polling_token 是否已绑定 token
    // 后端 API
    router.get("/api/auth/status", async (req, res) => {
      const { polling_token } = req.query;
      const token = await redis.get(`wechat:polling:${polling_token}`);
      
      if (token) {
        res.json({ loggedIn: true, token }); // 登录成功
      } else {
        res.json({ loggedIn: false }); // 未登录
      }
    });
    

完整时序图

前端 → 获取 polling_token → 生成二维码(带 polling_token) → 用户扫码 → 微信回调后端 → 
后端换 token → 存储 polling_token:token → 前端轮询检测 → 返回 token → 登录成功

关键点

  1. polling_token 的作用
    • 唯一标识 当前登录会话
    • 微信回调时通过 state 回传,确保后端能正确关联用户。
  2. Redis 存储
    • polling_token 作为 Key,token 作为 Value。
    • 设置 5分钟过期,避免无效请求占用内存。
  3. 跨域支持
    • 不依赖 Cookie,适合前后端分离项目。

你可能感兴趣的:(微信,java)