在我的应用程序中,我有一个必须始终有效的访问令牌(Spotify)。当此访问令牌过期时,应用程序必须每 60 分钟访问一次刷新令牌端点,并获取另一个访问令牌。
授权功能
出于安全原因,这2个来电,来/get_token和/refresh_token正在处理的python,服务器端,以及美国目前都已经在我的父母来处理App.jsx,就像这样:
class App extends Component {
constructor() {
super();
this.state = {
users: [],
isAuthenticated: false,
isAuthorizedWithSpotify: false,
spotifyToken: '',
isTokenExpired:false,
isTokenRefreshed:false,
renewing: false,
id: '',
};
componentDidMount() {
this.userId(); //<--- this.getSpotifyToken() is called here, inside then(), after async call;
};
getSpotifyToken(event) {
const options = {
url: `${process.env.REACT_APP_WEB_SERVICE_URL}/get_token/${this.state.id}`,
method: 'get',
headers: {
'Content-Type': 'application/json',
Authorization: `Bearer ${window.localStorage.authToken}`,
}
};
// needed for sending cookies
axios.defaults.withCredentials = true
return axios(options)
.then((res) => {
console.log(res.data)
this.setState({
spotifyToken: res.data.access_token,
isTokenExpired: res.data.token_expired // <--- jwt returns expiration from server
})
// if token has expired, refresh it
if (this.state.isTokenExpired === true){
console.log('Access token was refreshed')
this.refreshSpotifyToken();
}
})
.catch((error) => { console.log(error); });
};
refreshSpotifyToken(event) {
const options = {
url: `${process.env.REACT_APP_WEB_SERVICE_URL}/refresh_token/${this.state.id}`,
method: 'get',
headers: {
'Content-Type': 'application/json',
Authorization: `Bearer ${window.localStorage.authToken}`,
}
};
axios.defaults.withCredentials = true
return axios(options)
.then((res) => {
console.log(res.data)
this.setState({
spotifyToken: res.data.access_token,
isTokenRefreshed: res.data.token_refreshed,
isTokenExpired: false,
isAuthorizedWithSpotify: true
})
})
.catch((error) => { console.log(error); });
};
然后,我传递this.props.spotifyToken给我的所有子组件,其中使用访问令牌发出请求,并且一切正常。
观察者功能
问题在于,当应用程序在给定页面上闲置超过 60 分钟并且用户发出请求时,这会发现访问令牌已过期,并且其状态不会更新,因此请求将被拒绝。
为了解决这个问题,我想过App.jsx在后台有一个跟踪令牌过期时间的观察者函数,如下所示:
willTokenExpire = () => {
const accessToken = this.state.spotifyToken;
console.log('access_token in willTokenExpire', accessToken)
const expirationTime = 3600
const token = { accessToken, expirationTime } // { accessToken, expirationTime }
const threshold = 300 // 300s = 5 minute threshold for token expiration
const hasToken = token && token.spotifyToken
const now = (Date.now() / 1000) + threshold
console.log('NOW', now)
if(now > token.expirationTime){this.getSpotifyToken();}
return !hasToken || (now > token.expirationTime)
}
handleCheckToken = () => {
if (this.willTokenExpire()) {
this.setState({ renewing: true })
}
}
和:
shouldComponentUpdate(nextProps, nextState) {
return this.state.renewing !== nextState.renewing
}
componentDidMount() {
this.userId();
this.timeInterval = setInterval(this.handleCheckToken, 20000)
};
子组件
然后,从render()Parent App.jsx 中,我将handleCheckToken()作为回调函数以及传递this.props.spotifyToken给可能空闲的子组件,如下所示:
<Route exact path='/tracks' render={() => (
<Track
isAuthenticated={this.state.isAuthenticated}
isAuthorizedWithSpotify={this.state.isAuthorizedWithSpotify}
spotifyToken={this.state.spotifyToken}
handleCheckToken={this.handleCheckToken}
userId={this.state.id}
/>
)} />
在 Child 组件中,我会有:
class Tracks extends Component{
constructor (props) {
super(props);
this.state = {
playlist:[],
youtube_urls:[],
artists:[],
titles:[],
spotifyToken: this.props.spotifyToken
};
};
componentDidMount() {
if (this.props.isAuthenticated) {
this.props.handleCheckToken();
};
};
以及需要有效的、更新的 spotifyToken 的调用,如下所示:
getTrack(event) {
const {userId} = this.props
const options = {
url: `${process.env.REACT_APP_WEB_SERVICE_URL}/get-tracks/${userId}/${this.props.spotifyToken}`,
method: 'get',
headers: {
'Content-Type': 'application/json',
Authorization: `Bearer ${window.localStorage.authToken}`
}
};
return axios(options)
.then((res) => {
console.log(res.data.message)
})
.catch((error) => { console.log(error); });
};
但这行不通。
handleCheckToken即使我在 Child 处空闲,也会定期使用 获取新令牌。但是,如果我在 60 分钟后提出请求,则在 Child 中,this.props.spotifyToken传递已过期,因此 props 不会正确传递给 Child.jsx。
我错过了什么?