You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在后端认证后仅允许前端执行指定函数?

解决OAuth2认证后前后端无法判断用户认证状态的问题

后端(Node.js/Express)修改

1. 存储Token并设置安全Cookie

在redirect函数中,获取到OAuth2返回的token后,将refresh token存入HTTP-only Cookie(防止XSS攻击),access token可存入Cookie或返回给前端。这样后端能通过Cookie识别用户认证状态:

export const redirect = async (req, res) => {
  const link = "external api link to get access token";
  try {
    const response = await axios.post(link);
    const { access_token, refresh_token, expires_in } = response.data;

    // 存储refresh token到HTTP-only Cookie,生产环境开启secure
    res.cookie('refreshToken', refresh_token, {
      httpOnly: true,
      secure: process.env.NODE_ENV === 'production',
      sameSite: 'strict',
      maxAge: 7 * 24 * 60 * 60 * 1000 // 7天有效期
    });

    // 存储access token到Cookie,按token有效期设置过期时间
    res.cookie('accessToken', access_token, {
      secure: process.env.NODE_ENV === 'production',
      sameSite: 'strict',
      maxAge: expires_in * 1000
    });

  } catch (error) {
    console.log(error);
    // 认证失败重定向时携带错误标识
    return res.redirect(307, "http://localhost:3000?authFailed=true");
  }
  res.redirect(307, "http://localhost:3000");
};

2. 新增认证状态检查接口

添加接口让前端查询当前认证状态,同时支持自动刷新过期的access token:

export const checkAuth = async (req, res) => {
  const { accessToken, refreshToken } = req.cookies;

  // 无access token时尝试用refresh token刷新
  if (!accessToken) {
    if (!refreshToken) return res.status(401).json({ isAuthenticated: false });
    
    try {
      const refreshResponse = await axios.post("refresh token api link", {
        refresh_token: refreshToken
      });
      const newAccessToken = refreshResponse.data.access_token;
      // 更新access token的Cookie
      res.cookie('accessToken', newAccessToken, {
        secure: process.env.NODE_ENV === 'production',
        sameSite: 'strict',
        maxAge: refreshResponse.data.expires_in * 1000
      });
      return res.status(200).json({ isAuthenticated: true });
    } catch (err) {
      res.clearCookie('refreshToken');
      res.clearCookie('accessToken');
      return res.status(401).json({ isAuthenticated: false });
    }
  }

  // 验证access token有效性(根据OAuth2提供商方式,如JWT解码或调用验证接口)
  try {
    await axios.get("token validation api link", {
      headers: { Authorization: `Bearer ${accessToken}` }
    });
    return res.status(200).json({ isAuthenticated: true });
  } catch (err) {
    // access token失效,尝试用refresh token刷新
    if (refreshToken) {
      try {
        const refreshResponse = await axios.post("refresh token api link", {
          refresh_token: refreshToken
        });
        const newAccessToken = refreshResponse.data.access_token;
        res.cookie('accessToken', newAccessToken, {
          secure: process.env.NODE_ENV === 'production',
          sameSite: 'strict',
          maxAge: refreshResponse.data.expires_in * 1000
        });
        return res.status(200).json({ isAuthenticated: true });
      } catch (refreshErr) {
        res.clearCookie('refreshToken');
        res.clearCookie('accessToken');
        return res.status(401).json({ isAuthenticated: false });
      }
    } else {
      res.clearCookie('accessToken');
      return res.status(401).json({ isAuthenticated: false });
    }
  }
};

在Express中注册该接口:

app.get('/api/check-auth', checkAuth);

3. 配置跨域支持(若前后端域名不同)

确保Express允许前端携带Cookie请求:

const cors = require('cors');
app.use(cors({
  origin: 'http://localhost:3000', // 前端地址
  credentials: true // 允许携带Cookie
}));

前端(React)修改

1. 管理认证状态并条件渲染

用useState和useEffect跟踪认证状态,页面加载时主动查询后端接口:

import { useState, useEffect } from 'react';

const App = () => {
  const [isAuthenticated, setIsAuthenticated] = useState(false);
  const [loading, setLoading] = useState(true);

  // 页面加载时检查认证状态
  useEffect(() => {
    const checkAuthStatus = async () => {
      try {
        const response = await fetch('http://localhost:5000/api/check-auth', {
          credentials: 'include' // 必须携带Cookie到后端
        });
        const data = await response.json();
        setIsAuthenticated(data.isAuthenticated);
      } catch (err) {
        setIsAuthenticated(false);
      } finally {
        setLoading(false);
      }
    };

    checkAuthStatus();

    // 处理认证失败的重定向参数
    const urlParams = new URLSearchParams(window.location.search);
    if (urlParams.get('authFailed')) {
      setIsAuthenticated(false);
      alert('认证失败,请重试');
      // 清除URL中的错误参数
      urlParams.delete('authFailed');
      window.history.replaceState({}, document.title, window.location.pathname);
    }
  }, []);

  const auth = async () => {
    window.open("http://localhost:5000/api/callback", "_self"); // 用当前窗口跳转,体验更流畅
  };

  const displayData = async () => {
    if (!isAuthenticated) {
      alert('请先完成认证');
      return;
    }
    try {
      // 请求数据接口,后端自动验证Cookie中的access token
      const response = await fetch('http://localhost:5000/api/data', {
        credentials: 'include'
      });
      if (response.status === 401) {
        setIsAuthenticated(false);
        alert('认证已失效,请重新登录');
        return;
      }
      const data = await response.json();
      // 展示数据的逻辑
      console.log(data);
    } catch (err) {
      console.error(err);
    }
  };

  if (loading) {
    return <div>加载中...</div>;
  }

  return (
    <>
      {!isAuthenticated ? (
        <button onClick={auth}>Authenticate</button>
      ) : (
        <button onClick={displayData}>Display data</button>
      )}
    </>
  );
};

export default App;

2. 后端数据接口的认证验证

后端/api/data接口需要先验证用户认证状态:

export const getData = async (req, res) => {
  const { accessToken } = req.cookies;
  if (!accessToken) {
    return res.status(401).json({ message: '未认证' });
  }
  // 验证access token有效性(复用checkAuth中的验证逻辑)
  try {
    await axios.get("token validation api link", {
      headers: { Authorization: `Bearer ${accessToken}` }
    });
    // 返回业务数据
    res.status(200).json({ data: '你的业务数据' });
  } catch (err) {
    res.status(401).json({ message: '认证失效' });
  }
};

关键注意事项

  • Cookie安全:生产环境必须开启secure: true,确保Cookie仅通过HTTPS传输;httpOnly: true避免XSS攻击窃取refresh token。
  • 自动刷新Token:通过refresh token自动更新access token,减少用户重复登录的操作。
  • 状态同步:前端重定向回来后必须重新检查认证状态,确保界面与实际状态一致。

内容的提问来源于stack exchange,提问作者program.exe

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.12 07:20:27