我只学习了几个月的前端开发。我写了一个模块:
使用 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;
第一个代码中的无限刷新循环可能是由于您将用户重定向到第一个
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
按 使用 Spotify 登录