尝试在 React Web 应用程序中请求访问令牌时发生刷新无限循环

问题描述 投票:0回答:1

我只学习了几个月的前端开发。我写了一个模块:

使用 Spotify API 进行 GET 获取访问令牌的请求

解析生成的授权 URL 中的访问令牌和过期时间,并将其设置到浏览器中的本地存储。

我知道出于安全原因,不建议使用隐式拨款流程,但这只是一个小型组合项目,不适合大规模使用。

我遇到了一个问题,它不是立即加载 Spotify 的授权页面,而是直接转到redirect_uri,然后 url 快速闪烁,我认为这意味着存在某种不断发生的刷新循环。我搜索了 Spotify 文档、聊天 GPT 等...但我无法弄清楚。

import React, { useEffect } from 'react';

const generateRandomString = (length) => {
  const characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
  let result = '';
  for (let i = 0; i < length; i++) {
    result += characters.charAt(Math.floor(Math.random() * characters.length));
  }
  return result;
};

const SpotifyAccessToken = ({ stateLength }) => {
  useEffect(() => {
    const clientId = '';
    const redirectUri = 'http://localhost:5173/';
    const scope = 'playlist-modify-private playlist-modify-public user-read-private';
    const responseType = 'token';
    const state = generateRandomString(stateLength)

    const queryParams = new URLSearchParams({
      client_id: clientId,
      redirect_uri: redirectUri,
      scope: scope,
      response_type: responseType,
      state: state,
    });

    const authorizeUrl = `https://accounts.spotify.com/authorize?${queryParams}`;

    // Redirect the user to the authorize URL
    window.location.href = authorizeUrl;
  }, []);

  useEffect(() => {
    const queryParams = new URLSearchParams(window.location.search);
    const error = queryParams.get('error');

    if (error) {
      console.error('Authorization error:', error);
      
      return;
    }

    const accessToken = queryParams.get('access_token');
    const expiresIn = parseInt(queryParams.get('expires_in'));
    const expirationTime = new Date().getTime() + expiresIn * 1000;

    localStorage.setItem('spotifyAccessToken', accessToken);
    localStorage.setItem('spotifyTokenExpirationTime', expirationTime);

    if (expirationTime < Date.now()) {
      // Token has expired, redirect to the initial authorization URL
      const clientId = 'd90891e3a05449b5992d7c531d9a8cc1';
      const redirectUri = 'http://localhost:5173/';
      const scope = 'playlist-modify-private playlist-modify-public user-read-private';
      const responseType = 'token';
      const state = generateRandomString(stateLength)

      const queryParams = new URLSearchParams({
        client_id: clientId,
        redirect_uri: redirectUri,
        scope: scope,
        response_type: responseType,
        state: state,
      });

      const authorizeUrl = `https://accounts.spotify.com/authorize?${queryParams}`;

      window.location.href = authorizeUrl;
    } else {
      // Clear parameters from the URL to avoid potential issues
      window.history.replaceState({}, document.title, window.location.pathname);
    }
  }, []);

  return (
    <div>
      <p>Redirecting to Spotify for access...</p>
    </div>
  );
};

export default SpotifyAccessToken;
javascript reactjs jsx fetch-api
1个回答
0
投票

第一个代码中的无限刷新循环可能是由于您将用户重定向到第一个

useEffect
挂钩内的 Spotify 授权 URL,该挂钩在组件安装([] 依赖项数组)上运行。然后,在用户从 Spotify 重定向回您的应用程序后,第二个
useEffect
挂钩将执行,但它没有指定任何依赖项([] 依赖项数组)。

因此,它在每次渲染时运行,包括收到访问令牌之后,导致重定向和重新渲染的连续循环。

[Render] --> [Redirect to Spotify] --> [Redirect back to application] --> [Render] --> ...

因此,我更改了您的代码并通过隐式授予流程在浏览器中显示访问令牌。 不需要 CLIENT_SECRET 因为:

CLIENT_SECRET 通常与 CLIENT_ID 一起用于服务器端身份验证流程,例如授权代码授予流程,其中客户端可以安全地存储并使用 CLIENT_SECRET 向授权服务器验证自身身份。 但是,在隐式授予流程中,访问令牌通过浏览器直接发送到客户端应用程序,因此客户端无需使用 CLIENT_SECRET 来验证自身。

App.js

import React from 'react';
import SpotifyAccessToken from './SpotifyAccessToken';
import './App.css';

function App() {
  const clientId = '{Your Spotify client ID}';
  const redirectUri = 'http://localhost:3000/callback'; // Your redirect with port URI and "start": port in package.json
  const scope = 'user-read-private user-read-email'; // Your desired scope

  return (
    <div className="App">
      <header className="App-header">
        <SpotifyAccessToken clientId={clientId} redirectUri={redirectUri} scope={scope} />
      </header>
    </div>
  );
}

export default App;

SpotifyAccessToken.js

import React, { useState, useEffect } from 'react';

const SpotifyAccessToken = ({ clientId, redirectUri, scope }) => {
  const [accessToken, setAccessToken] = useState(null);

  useEffect(() => {
    const params = new URLSearchParams(window.location.hash.substring(1));
    const token = params.get('access_token');
    if (token) {
      setAccessToken(token);
    }
  }, []);

  const handleLogin = () => {
    // Redirect to Spotify authorization endpoint
    window.location.href = `https://accounts.spotify.com/authorize?${
      new URLSearchParams({
        response_type: 'token',
        client_id: clientId,
        redirect_uri: redirectUri,
        scope: scope,
      }).toString()
    }`;
  };

  return (
    <div>
      {!accessToken ? (
        <button onClick={handleLogin}>Login with Spotify</button>
      ) : (
        <div>
          <p>Successfully logged in!</p>
          <p>Access Token: {accessToken}</p>
          {/* Now you can use the accessToken to make requests to the Spotify API */}
        </div>
      )}
    </div>
  );
};

export default SpotifyAccessToken;

package.json

{
  "dependencies": {
    "@testing-library/jest-dom": "^5.17.0",
    "@testing-library/react": "^13.4.0",
    "@testing-library/user-event": "^13.5.0",
    "react": "^18.3.1",
    "react-dom": "^18.3.1",
    "react-scripts": "5.0.1",
    "web-vitals": "^2.1.4"
  },
  "scripts": {
    "start": "PORT=3000 react-scripts start",
    "build": "react-scripts build",
    "test": "react-scripts test",
    "eject": "react-scripts eject"
  }
}

Note:
如果您的回调端口号不同,您需要更改

 "start": "PORT={your port number} react-scripts start",

运行它

npm start

结果

enter image description here

使用 Spotify 登录

enter image description here

© www.soinside.com 2019 - 2024. All rights reserved.