You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

集成Spring Security后Fetch POST请求报404问题求助

问题:引入Spring Security后POST请求返回404错误

我正在开发一个小型项目,引入Spring Security后,原本正常的Fetch POST请求现在一直返回POST http://localhost:8080/movie/rate 404 (Not Found)错误。我是Spring Security新手,不知道怎么解决。


前端Fetch请求代码

<script>
        document.getElementById('ratingSubmit').addEventListener('click', () => {
            let stars = document.querySelectorAll('input[name="star-rating"]');
            let selectedValue = null;

            for (let i = 0; i < stars.length; i++) {
                if (stars[i].checked) {
                    selectedValue = stars[i].value;
                    break;
                }
            }

            if (selectedValue !== null) {
                const url = window.location.search;
                const urlParams = new URLSearchParams(url);
                if (urlParams.has('id')) {
                    const id = urlParams.get('id');
                    console.log(selectedValue);
                    console.log(id);
                    fetch("/movie/rate", {
                        method: 'POST',
                        headers: {
                            'Content-type' : 'application/json'
                        },
                        body: JSON.stringify({movie_id : id, rating : selectedValue})
                    }).then((resp) => {
                        return resp.text();
                    }).then((resp) => {
                        let message = 'Failed to submit';
                        if (resp === 'Success')
                            message = `Successfully submitted a rating of ${selectedValue} stars`;
                        alert(message)
                    })
                }
            } else {
                alert("No stars were selected");
            }
        });
    </script>

评分控制器代码

package javaproj.movieRental.controllers;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.security.core.Authentication;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;

import javaproj.movieRental.DTO.RatingRequest;
import javaproj.movieRental.entities.Rating;
import javaproj.movieRental.entities.User;
import javaproj.movieRental.repositories.MovieRepository;
import javaproj.movieRental.repositories.RatingRepository;
import javaproj.movieRental.repositories.UserRepository;
import javaproj.movieRental.security.CustomUserDetails;
import javaproj.movieRental.services.RatingServicesImp;

@Controller
@RequestMapping("/movie/rate")
public class RatingController {
    
    @Autowired
    private UserRepository userRepository;
    
    @Autowired
    private RatingRepository ratingRepository;
    
    @Autowired
    private MovieRepository movieRepository;

    @PostMapping(consumes = "application/json", produces = "application/json")
    public ResponseEntity<?> saveRating(@RequestBody RatingRequest rr, Authentication authentication) {
        CustomUserDetails userDetails = (CustomUserDetails) authentication.getPrincipal();
        User user = userDetails.getUser();
        
        Long user_id = user.getId();
        Long movie_id = rr.getMovie_id();
        int rating = rr.getRating();
        
        System.out.println("User id: " + user_id + "\nMovie id: " + movie_id + "\nRating: " + rating );
        Rating userRating = ratingRepository.getByUserIdAndMovieId(user_id, movie_id);
        if (userRating != null) {
            userRating.setRating(rating);
            ratingRepository.save(userRating);
            return ResponseEntity.ok("Success");
        }
        RatingServicesImp rsi = new RatingServicesImp(ratingRepository, userRepository, movieRepository);
        userRating = rsi.createRating(user_id, movie_id, rating);
        ratingRepository.save(userRating);
        return ResponseEntity.ok("Success");
    }
}

SecurityConfiguration配置代码

@Bean
    public SecurityFilterChain securityFilterChain2(HttpSecurity http) throws Exception {
        http
            .authorizeHttpRequests(authorize ->
                authorize
                .requestMatchers(HttpMethod.POST, "/movie/rate")
                .hasAnyRole("ADMIN", "USER")
                .anyRequest().authenticated() // All other requests require authentication
            )
            .formLogin(formLogin ->
                formLogin
                    .loginPage("/login") // Custom login page URL
                    .successHandler(customAuthenticationSuccessHandler) // Use custom success handler
                    .permitAll()
            )
            .logout(logout ->
                logout
                    .invalidateHttpSession(true)
                    .clearAuthentication(true)
                    .logoutRequestMatcher(new AntPathRequestMatcher("/logout"))
                    .logoutSuccessUrl("/login?logout")
                    .permitAll()
            )
            .exceptionHandling(exception ->
                exception
                    .accessDeniedPage("/error/403") // Custom 403 error page
            );
        return http.build();
    }

解决方向

1. 修正控制器注解

控制器使用@Controller注解,但方法返回ResponseEntity,这会导致Spring试图解析视图名称而非直接返回响应体,引发404。将注解替换为@RestController,或在方法上添加@ResponseBody:

@RestController // 替换原@Controller
@RequestMapping("/movie/rate")
public class RatingController {
    // 原有代码不变
}

2. 处理CSRF防护

Spring Security默认开启CSRF防护,POST请求需携带CSRF令牌,否则会被拦截(可能表现为404/403)。

  • 在页面添加CSRF元标签:
<meta name="_csrf" content="${_csrf.token}"/>
<meta name="_csrf_header" content="${_csrf.headerName}"/>
  • 在Fetch请求中添加CSRF头:
const csrfToken = document.querySelector('meta[name="_csrf"]').content;
const csrfHeader = document.querySelector('meta[name="_csrf_header"]').content;

fetch("/movie/rate", {
    method: 'POST',
    headers: {
        'Content-type' : 'application/json',
        [csrfHeader]: csrfToken
    },
    body: JSON.stringify({movie_id : id, rating : selectedValue})
})

3. 检查Security配置优先级

如果存在多个SecurityFilterChain Bean,需确保当前配置优先生效,可添加@Order注解:

@Bean
@Order(1)
public SecurityFilterChain securityFilterChain2(HttpSecurity http) throws Exception {
    // 原有配置不变
}

4. 验证RatingRequest属性匹配

确保RatingRequest类的movie_id、rating字段与前端JSON字段完全一致,且包含对应的getter/setter方法,否则Spring无法解析请求体。


内容的提问来源于stack exchange,提问作者LearningCode

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.23 09:44:54