Невозможно получить доступ к конечным точкам отдыха даже после успешного входа в систему Spring Security через форму реагирования

Я пытаюсь реализовать аутентификацию входа в систему при весенней загрузке, я использовал Spring безопасность и использовал форму реагирования во внешнем интерфейсе для аутентификации, я использую axios для вызова моего /login, который работает хорошо, форма открывается и аутентифицируется отлично, но проблема заключается здесь заключается в том, что после успешного входа в систему я не могу получить доступ к другим конечным точкам, объявленным в моем контроллере Spring

Это моя конфигурация безопасности Spring

   @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {

        return http
                .authorizeHttpRequests(auth->auth
                        .requestMatchers("/","/login/**","/register")
                        .permitAll()
                        .anyRequest()
                        .authenticated())
                .logout(logout->logout
                        .logoutUrl("/logout")
                        .logoutSuccessUrl("/")
                        .deleteCookies("JSESSIONID")
                        .invalidateHttpSession(true))
                .csrf(AbstractHttpConfigurer::disable)
                .sessionManagement(session->session.sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED))
                .build();
    }

Мой контроллер входа

    @PostMapping("/login")
    public ResponseEntity<?> login(@RequestBody RegisterUser registerUser) {
        try {
            Authentication authentication = authenticationManager.authenticate(
                    new UsernamePasswordAuthenticationToken(registerUser.getEmail(), registerUser.getPassword())
            );
            if (authentication.isAuthenticated()) {
                return ResponseEntity.ok().body("Login successful");
            } else {
                return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body("Login failed");
            }
        } catch (AuthenticationException e) {
            return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body("Login failed");
        }
        
    }

Это моя форма реакции

axios.defaults.baseURL = "http://localhost:8080"
axios.defaults.withCredentials=true;

const Login=()=>{
    const [email, setEmail]=React.useState('');
    const [password, setpassword]=React.useState('');
    const [error, setError] = React.useState('');
    const navigate =useNavigate();

    const handlesubmit= async (e)=>{
        e.preventDefault();
        try {
            const response = await axios.post("/login" ,{
                email,
                password,
            });

            if (response.status===200){
                console.error(email,password)
                navigate('/');
            }
            else {
                navigate('/logs')
            }
        }
        catch (e){
            console.error(e)
            setError("login failed")
        }
    }

Пробовал использовать login->login.form("/login") и loginprocesssurl("/login") в конфигурации безопасности, но не работает, я также установил прокси-сервер на порт, на котором работает Spring, но у меня нет результатов

Теперь я хочу, чтобы после успешного входа в систему (это так) я хочу получить доступ к другим конечным точкам, доступ к которым могут получить только прошедшие проверку подлинности пользователи.

🤔 А знаете ли вы, что...
Java активно развивается и обновляется с появлением новых версий и функциональных улучшений.


82
1

Ответ:

Решено

В пользовательской конечной точке отсутствует одна небольшая деталь: SecurityContext не заполняется во время запроса/ответа на вход. Без его сохранения дальнейшие запросы будут анонимными, несмотря на успешную аутентификацию.

Предпочтительный способ — использовать логику входа в SecurityFilterChain:

@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
    return http
            .authorizeHttpRequests(...)
            .formLogin(it -> it
                .loginPage("/") // mapped to index.html, letting react handle it
                .loginProcessingUrl("/login")
                .successHandler((request, response, authentication) -> {
                    response.setStatus(HttpStatus.OK.value());
                    response.getWriter().print("Login successful");
                })
                .failureHandler((request, response, exception) -> {
                    response.sendError(HttpStatus.UNAUTHORIZED.value(), "Login failed");
                })
            )
            .logout(...)
            ...
            .build();
}

Почему это? Он добавляет дополнительный UsernamePasswordAuthenticationFilter, который содержит логику аутентификации вашей конечной точки @PostMapping("/login") + , в которой хранится SecurityContext в ThreadLocal. Позже он сохраняется с помощью SecurityContextRepository в другом фильтре по цепочке. (Обратите внимание: начиная с версии 6 требуется явное сохранение)

Если вы действительно хотите контролировать процесс входа в свою конечную точку (возможно, из-за некоторых побочных эффектов), сохраните контекст там:

...
@PostMapping("/login")
public ResponseEntity<?> login(@RequestBody RegisterUser registerUser) {
try {
    Authentication authentication = authenticationManager.authenticate(
        new UsernamePasswordAuthenticationToken(registerUser.getEmail(), registerUser.getPassword())
            );
            if (authentication.isAuthenticated()) {

                // storing context in ThreadLocal, later will be saved in SecurityContextPersistenceFilter
                SecurityContext context = SecurityContextHolder.createEmptyContext();
                context.setAuthentication(authentication);
                SecurityContextHolder.setContext(context);

                return ResponseEntity.ok().body("Login successful");
            } else {
                return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body("Login failed");
            }
        } catch (AuthenticationException e) {
            return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body("Login failed");
        }
        
    }
...