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

React+Flask集成Spotify认证:前端未同步登录状态求助

问题:Flask+Spotipy+React 认证后前端无法识别登录状态

问题背景

用Flask后端结合Spotipy实现Spotify用户认证,单独运行后端功能正常,但接入React前端后,前端检测不到用户已登录的状态。已确认后端发送Cookie,未依赖缓存记录登录状态,作为Web开发新手(尤其不熟悉前端),求解决思路。

预期流程

  • 页面加载时检查缓存令牌或用户认证状态
  • 未认证则重定向至Spotify登录页
  • 处理Spotify回调并验证认证状态
  • 重定向至前端localhost:3000/,此时前端应检测到用户已登录

后端相关代码

@app.route('/')
def index():
    cache_handler = spotipy.cache_handler.FlaskSessionCacheHandler(session)
    auth_manager = spotipy.oauth2.SpotifyOAuth(client_id=CLIENT_ID, client_secret=CLIENT_SECRET, redirect_uri=REDIRECT_URI, scope='user-read-currently-playing',
                                               cache_handler=cache_handler)
    

    if not auth_manager.validate_token(cache_handler.get_cached_token()):
        auth_url = auth_manager.get_authorize_url()
        #print(auth_url)
        return jsonify({"logged_in": False, "auth_url": auth_url})

    spotify = spotipy.Spotify(auth_manager=auth_manager)
    user_info = spotify.me()
    return jsonify({
        "logged_in": True,
        "user_info": {
            "display_name": user_info["display_name"],
            "id": user_info["id"],
            "uri": user_info["uri"],
            "profile_url": user_info["external_urls"]["spotify"]
        }
    })

@app.route('/callback')
def callback():
    code = request.args.get('code')
    print(code)
    if code:
        cache_handler = spotipy.cache_handler.FlaskSessionCacheHandler(session)
        auth_manager = spotipy.oauth2.SpotifyOAuth(client_id=CLIENT_ID, client_secret=CLIENT_SECRET, redirect_uri=REDIRECT_URI, scope='user-read-currently-playing',
                                                   cache_handler=cache_handler)
        auth_manager.get_access_token(code)
        session.modified = True
        response = make_response(redirect('http://localhost:3000/'))
        return response
    return 'Missing code parameter. Please try again.', 400

前端App.js代码

function App() {
  const [message, setMessage] = useState('');
  const [isLoggedIn, setIsLoggedIn] = useState(false);
  const [authUrl, setAuthUrl] = useState('');
  
  useEffect(() => {
    const url = `http://127.0.0.1:5000/?t=${Date.now()}`;
    axios.get(url, { withCredentials: true })
      .then(response => { 
        const { logged_in, auth_url } = response.data;
        console.log(response.data);
        setIsLoggedIn(logged_in);
        if (!logged_in) {
          setAuthUrl(auth_url);
          console.log('Redirecting to:', auth_url);
        }
      })  
      .catch(error => console.error('Error:', error));
  }, []);

  return (
    <Router>
      <div className="App">
        <div className="App-header">
          <Routes>
            <Route path="/login" element={!isLoggedIn ? <Login authUrl={authUrl} /> : <Navigate to="/" />} />  // Use the Login component here
            <Route path="/" element={isLoggedIn ? <SearchBar onSearch={setMessage} /> : <Navigate to="/login" />} />    
          </Routes>
        </div>
      </div>
    </Router>
  );
}
export default App;

测试情况

回调函数中重定向到localhost:5000时,后端能正确返回"logged_in": True及用户信息,但重定向到前端localhost:3000后,前端检测到的logged_in仍为false。


解决方案

1. 配置Flask跨域支持

Flask后端需开启CORS并允许前端携带Cookie,同时设置会话密钥:

from flask_cors import CORS

app = Flask(__name__)
# 配置CORS,允许前端域名并支持凭证传递
CORS(app, resources={r"/*": {"origins": "http://localhost:3000", "supports_credentials": True}})
# 设置会话加密密钥(必填,否则会话无法持久化)
app.secret_key = 'your-random-secret-key-here'

2. 调整回调的Cookie属性

在/callback路由中,修改Cookie的SameSite属性,避免跨域时被浏览器拦截:

@app.route('/callback')
def callback():
    code = request.args.get('code')
    if code:
        cache_handler = spotipy.cache_handler.FlaskSessionCacheHandler(session)
        auth_manager = spotipy.oauth2.SpotifyOAuth(client_id=CLIENT_ID, client_secret=CLIENT_SECRET, redirect_uri=REDIRECT_URI, scope='user-read-currently-playing',
                                                   cache_handler=cache_handler)
        auth_manager.get_access_token(code)
        session.modified = True
        response = make_response(redirect('http://localhost:3000/'))
        # 调整Cookie的SameSite属性
        for cookie in response.headers.getlist('Set-Cookie'):
            response.headers.remove('Set-Cookie')
            # 本地HTTP环境用Lax,HTTPS环境用None; Secure
            updated_cookie = cookie.replace('SameSite=Lax', 'SameSite=Lax')
            response.headers.add('Set-Cookie', updated_cookie)
        return response
    return 'Missing code parameter. Please try again.', 400

3. 前端优化认证检查逻辑

监听路由变化,每次页面跳转后重新检查登录状态,避免仅在组件挂载时检查一次:

import { useLocation } from 'react-router-dom';

function App() {
  const [message, setMessage] = useState('');
  const [isLoggedIn, setIsLoggedIn] = useState(false);
  const [authUrl, setAuthUrl] = useState('');
  const location = useLocation();

  // 路由变化时重新校验登录状态
  useEffect(() => {
    const checkAuthStatus = async () => {
      try {
        // 统一用localhost域名,避免Cookie域名不匹配
        const response = await axios.get('http://localhost:5000/', { withCredentials: true });
        const { logged_in, auth_url } = response.data;
        setIsLoggedIn(logged_in);
        if (!logged_in) {
          setAuthUrl(auth_url);
        }
      } catch (error) {
        console.error('认证检查失败:', error);
      }
    };
    checkAuthStatus();
  }, [location]);

  // 其余代码保持不变
}

4. 验证会话Cookie传递

打开浏览器开发者工具的Application标签,查看Cookies列表:

  • 确认前端域名localhost:3000下存在Flask的会话Cookie(默认名为session)
  • 检查Cookie的Domain、Path、SameSite属性是否符合配置要求

内容的提问来源于stack exchange,提问作者Stanley Wang

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.27 17:10:59