即使通过React表单成功登录Spring Security后也无法访问Rest端点

问题描述 投票:0回答:1

我正在尝试在 Spring Boot 中实现登录身份验证,我使用了 Spring Security 并在前端使用了 React 表单来进行身份验证,我使用 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 reactjs spring-boot rest spring-security
1个回答
0
投票

对于自定义端点,缺少一个细节:在登录请求/响应期间未填充

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"); } } ...
    
© www.soinside.com 2019 - 2024. All rights reserved.