feat(auth): proteger dashboard e melhorar UX
- Remove HTTP Basic e trata 401 redirecionando para /login - Adiciona ProtectedRoute garantindo acesso ao dashboard apenas autenticado - Refina modais e menu com bulk upload e tipagens exportadasmaster
parent
b6ba3b8593
commit
a69aca5dc8
|
|
@ -12,6 +12,7 @@ import org.springframework.security.config.annotation.authentication.configurati
|
|||
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
|
||||
import org.springframework.security.config.http.SessionCreationPolicy;
|
||||
import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
|
||||
import org.springframework.security.core.userdetails.UserDetailsService;
|
||||
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
|
||||
import org.springframework.security.crypto.password.PasswordEncoder;
|
||||
|
|
@ -44,7 +45,7 @@ public class SecurityConfig {
|
|||
)
|
||||
.authenticationProvider(authenticationProvider())
|
||||
.addFilterBefore(jwtAuthFilter, UsernamePasswordAuthenticationFilter.class)
|
||||
.httpBasic(Customizer.withDefaults());
|
||||
.httpBasic(AbstractHttpConfigurer::disable);
|
||||
|
||||
return http.build();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -116,4 +116,18 @@ api.interceptors.request.use(async (config) => {
|
|||
return config;
|
||||
});
|
||||
|
||||
api.interceptors.response.use(
|
||||
(response) => response,
|
||||
(error) => {
|
||||
if (error?.response?.status === 401) {
|
||||
setAuthToken(undefined);
|
||||
const currentPath = window.location.pathname;
|
||||
if (currentPath !== "/login") {
|
||||
window.location.href = "/login";
|
||||
}
|
||||
}
|
||||
return Promise.reject(error);
|
||||
}
|
||||
);
|
||||
|
||||
export default api;
|
||||
|
|
|
|||
|
|
@ -2,13 +2,18 @@ import { BrowserRouter, Route, Routes } from 'react-router-dom';
|
|||
import './App.css';
|
||||
import { Login } from './pages/Login';
|
||||
import { Dashboard } from './pages/Dashboard';
|
||||
import { ProtectedRoute } from './routes/ProtectedRoute';
|
||||
|
||||
function App() {
|
||||
|
||||
return (
|
||||
<BrowserRouter>
|
||||
<Routes>
|
||||
<Route path="/" element={<Dashboard />} />
|
||||
<Route path="/" element={
|
||||
<ProtectedRoute>
|
||||
<Dashboard />
|
||||
</ProtectedRoute>
|
||||
} />
|
||||
<Route path="/login" element={<Login />} />
|
||||
</Routes>
|
||||
</BrowserRouter>
|
||||
|
|
|
|||
|
|
@ -134,8 +134,8 @@ const Stat = ({
|
|||
);
|
||||
|
||||
const Styles = {
|
||||
modalOverlay: "fixed inset-0 z-40 flex items-center justify-center bg-black/40 px-4",
|
||||
modal: "w-full max-w-2xl rounded-2xl border border-cardBorder bg-card p-6 shadow-xl",
|
||||
modalOverlay: "fixed inset-0 z-40 flex items-center justify-center bg-black/40 backdrop-blur-sm px-4 !mt-0",
|
||||
modal: "w-full max-w-2xl rounded-2xl border border-cardBorder bg-card p-6 shadow-xl transform transition-all duration-200 animate-fade-up",
|
||||
modalHeader: "flex items-start justify-between gap-4 pb-4 border-b border-cardBorder",
|
||||
modalTitle: "text-lg font-semibold text-text",
|
||||
closeButton: "text-2xl leading-none text-text-secondary hover:text-text",
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
interface HeaderActionsProps {
|
||||
export interface HeaderActionsProps {
|
||||
isMenuOpen: boolean;
|
||||
onToggleMenu: () => void;
|
||||
onAddServer: () => void;
|
||||
|
|
|
|||
|
|
@ -73,8 +73,8 @@ export const ProfileModal = ({
|
|||
};
|
||||
|
||||
const Styles = {
|
||||
modalOverlay: "fixed inset-0 z-40 flex items-center justify-center bg-black/40 px-4",
|
||||
modal: "w-full max-w-2xl rounded-2xl border border-cardBorder bg-card p-6 shadow-xl",
|
||||
modalOverlay: "fixed inset-0 z-40 flex items-center justify-center bg-black/40 backdrop-blur-sm px-4 !mt-0",
|
||||
modal: "w-full max-w-2xl rounded-2xl border border-cardBorder bg-card p-6 shadow-xl transform transition-all duration-200 animate-fade-up",
|
||||
modalHeader: "flex items-start justify-between gap-4 pb-4 border-b border-cardBorder",
|
||||
modalTitle: "text-lg font-semibold text-text",
|
||||
closeButton: "text-2xl leading-none text-text-secondary hover:text-text",
|
||||
|
|
|
|||
|
|
@ -100,8 +100,8 @@ export const ServerModal = ({
|
|||
};
|
||||
|
||||
const Styles = {
|
||||
modalOverlay: "fixed inset-0 z-40 flex items-center justify-center bg-black/40 px-4",
|
||||
modal: "w-full max-w-2xl rounded-2xl border border-cardBorder bg-card p-6 shadow-xl",
|
||||
modalOverlay: "fixed inset-0 z-40 flex items-center justify-center bg-black/40 backdrop-blur-sm px-4 !mt-0",
|
||||
modal: "w-full max-w-2xl rounded-2xl border border-cardBorder bg-card p-6 shadow-xl transform transition-all duration-200 animate-fade-up",
|
||||
modalHeader: "flex items-start justify-between gap-4 pb-4 border-b border-cardBorder",
|
||||
modalTitle: "text-lg font-semibold text-text",
|
||||
closeButton: "text-2xl leading-none text-text-secondary hover:text-text",
|
||||
|
|
|
|||
|
|
@ -0,0 +1,18 @@
|
|||
import type { ReactNode } from "react";
|
||||
import { Navigate, useLocation } from "react-router-dom";
|
||||
import { getAccessToken } from "../Api";
|
||||
|
||||
interface ProtectedRouteProps {
|
||||
children: ReactNode;
|
||||
}
|
||||
|
||||
export const ProtectedRoute = ({ children }: ProtectedRouteProps) => {
|
||||
const token = getAccessToken();
|
||||
const location = useLocation();
|
||||
|
||||
if (!token) {
|
||||
return <Navigate to="/login" state={{ from: location }} replace />;
|
||||
}
|
||||
|
||||
return <>{children}</>;
|
||||
};
|
||||
Loading…
Reference in New Issue