我实现了一个全栈 React 和 Spring Boot 应用程序,我使用 Spring Security 进行身份验证和授权。关于授权有一个奇怪的行为。
每当我通过
npm start
重新启动前端应用程序并登录到该应用程序时,所有必要的参数,例如userId, jwt, roles, username 被正确设置为 localStorage 并且应用程序导航到不需要授权的主页。 但是,当我点击需要授权的菜单时,出现“访问此资源需要完整身份验证”错误。
但是,如果我在登录后刷新页面,通过主页上的
window.location.reload()
,然后单击菜单,则没有错误(如果我首先导航到页面而不是主页,则错误会在刷新前的短时间内发生).
我通过 Postman 多次尝试相同的端点,没有这样的问题。出于这个原因,似乎在 React 方面缺少一些东西。你有什么想法吗?
这是我通过 Axios 发送的标头,但我认为还可以:
const config = {
headers: {
// "Access-Control-Allow-Origin": "*",
// "Access-Control-Allow-Credentials": true,
// "Content-Type": "application/json",
Accept: "application/json",
Authorization: `Bearer ${AUTH_TOKEN}`
},
};
service.js
import axios from "./axios";
const headers = {
Accept: "application/json",
"Content-Type": "application/json",
};
const axiosInstance = axios.create({
headers,
});
export default axiosInstance;
axiosInstance.defaults.headers.common.Authorization = `Bearer ${localStorage.getItem("tokenKey")}`;
export const getWithAuth = (url) => {
const request = axiosInstance.get(url);
return request.then((response) => response.data);
};
您遇到的行为可能是因为当您刷新页面时,应用程序会重新加载并从服务器检索最新的身份验证信息。但是,当您在不刷新页面的情况下导航到另一个页面时,应用程序正在使用存储在本地存储中的旧身份验证信息。
为了解决这个问题,你可以尝试使用高阶组件(HOC)来包装需要认证的组件。此 HOC 可以在呈现组件之前检查用户是否已通过身份验证。这是一个例子:
import React from 'react';
import { Redirect } from 'react-router-dom';
export default function withAuth(Component) {
return class extends React.Component {
constructor(props) {
super(props);
this.state = {
isAuthenticated: localStorage.getItem('tokenKey') !== null,
};
}
render() {
const { isAuthenticated } = this.state;
if (isAuthenticated) {
return <Component {...this.props} />;
} else {
return <Redirect to={{ pathname: '/login' }} />;
}
}
};
}
在此示例中,
withAuth
函数返回一个新组件,该组件通过查看存储在本地存储中的令牌来检查用户是否已通过身份验证。如果用户通过身份验证,它会呈现包装的组件。否则,它将用户重定向到登录页面。
您可以使用此 HOC 来包装需要身份验证的组件。例如:
import withAuth from './withAuth';
class Dashboard extends React.Component {
// ...
}
export default withAuth(Dashboard);
通过使用这个 HOC,组件只会在用户通过身份验证时呈现,确保身份验证信息始终是最新的。
另一个潜在问题可能与 Spring Boot 应用程序处理身份验证的方式有关。确保您的服务器在用户登录时返回正确的响应标头,包括
Authorization
标头。此外,如果您正在使用它们,请确保您的服务器正确处理刷新令牌。
为什么当你第一次登录时你得到
unauthorize
但当你刷新它时它工作正常?
tokenKey
,但是当您登录时tokenKey
将存储在 localStorage 中并且当您刷新时您将获得令牌。如何解决这个问题?
service.js:
import axios from "axios";
const headers = {
Accept: "application/json",
"Content-Type": "application/json",
};
const axiosInstance = axios.create({
headers,
});
export default axiosInstance;
然后你应该在axios中设置从登录请求返回的
tokenKey
axiosInstance.defaults.headers.common.Authorization = `Bearer ${tokenKey}`;
你也可以摆脱
getWithAuth
功能,在任何请求中使用:
export const login = async (credentials) => {
let data;
try {
const response = await axiosInstance.post(`/auth/login`, {
email: credentials?.email,
password: credentials?.password,
});
const token = response?.data?.token
axiosInstance.defaults.headers.common.Authorization = `Bearer ${token}`
localStorage.setItem('tokenKey', token)
//...
} catch (err) {
console.log(err);
}
}