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

React-Spring Boot项目/api/quiz/random异常重定向至/login问题排查

问题:React + Spring Boot 会话验证异常,API请求被重定向至/login导致404

Auth Controller已成功设置登录用户会话(日志显示“User session set for: willow”),前端受保护路由正常工作,但调用/api/quiz/random接口时,请求始终被重定向至http://localhost:8080/login,触发404错误(错误信息:GET http://localhost:8080/login 404 (Not Found),对应Quizpage.js第48行的axios请求)。

前端请求代码(Quizpage.js)

const fetchQuizItem = async () => {
        try {
            const response = await axios.get('http://localhost:8080/api/quiz/random', {
                withCredentials: true,  // Ensures cookies are sent with the request
            });
            console.log('Quiz Item:', response.data);
            setQuizItem(response.data);
        } catch (err) {
            console.error('Failed to load quiz item', err);
            setError('Failed to load quiz item');
        }
    };

后端QuizController代码

package quiz.Controller;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import quiz.Entity.QuizItem;
import quiz.Entity.User;
import quiz.Entity.UserQuizAttempt;
import quiz.Service.QuizItemService;
import quiz.Service.UserQuizAttemptService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.servlet.http.HttpServletRequest;

@RestController
@CrossOrigin(origins = "http://localhost:3000", allowCredentials = "true", allowedHeaders = "*")
@RequestMapping("/api/quiz")
public class QuizController {

    private static final Logger logger = LoggerFactory.getLogger(QuizController.class);

    @Autowired
    private QuizItemService quizService;

    @Autowired
    private UserQuizAttemptService userQuizAttemptService;

    @PostMapping("/submit")
    public boolean submitQuiz(@RequestParam int guessedYear, @RequestParam Long quizItemId, HttpServletRequest request) {
        User user = (User) request.getSession().getAttribute("user");
        logger.info("User object: {}", user);

        if (user == null) {
            logger.error("User not logged in");
            throw new IllegalArgumentException("User not logged in");
        }

        QuizItem quizItem = quizService.getQuizItemById(quizItemId);
        UserQuizAttempt attempt = userQuizAttemptService.saveUserQuizAttempt(user, quizItem, guessedYear);
        return attempt.getPoints() > 0;
    }

    @GetMapping("/random")
    public QuizItem getRandomQuizItem(HttpServletRequest request) {
        User user = (User) request.getSession().getAttribute("user");
        logger.info("User object: {}", user);

        if (user == null) {
            logger.error("User not logged in");
            throw new IllegalArgumentException("User not logged in");
        }

        return quizService.getDailyQuizItem();
    }
}

已排查信息

  • 所有前端JS页面无主动重定向至/login的逻辑
  • 用户会话已成功创建(Auth Controller日志确认)
  • 前端受保护路由可正常识别登录状态

可能的原因及解决方案

1. Spring Security 触发默认登录重定向

Spring Security默认拦截所有请求,若未将/api/quiz/**配置正确的认证规则,会自动重定向到默认/login端点(项目未配置该端点导致404)。

解决步骤:
修改SecurityConfig,禁用默认重定向,改为返回401错误:

@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
            .csrf().disable()
            .authorizeRequests()
                .antMatchers("/api/auth/**").permitAll() // 放行登录注册接口
                .antMatchers("/api/quiz/**").authenticated() // 要求认证
                .anyRequest().authenticated()
            .and()
            .sessionManagement()
                .sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED)
            .and()
            .exceptionHandling()
                .authenticationEntryPoint((request, response, authException) -> {
                    // 未认证时返回401,而非重定向
                    response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Unauthorized");
                });
    }
}

2. 会话Cookie跨域配置缺失

尽管前端设置了withCredentials: true,但后端Cookie的SameSite、Domain等属性配置错误,会导致Cookie未被正确携带到请求中,后端判定用户未登录。

检查application.properties:

server.servlet.session.cookie.same-site=Lax # 跨域场景可改为None
server.servlet.session.cookie.domain=localhost
server.servlet.session.cookie.http-only=true

3. 手动会话验证与Spring Security冲突

手动从HttpServletRequest获取会话用户的逻辑,可能与Spring Security的会话管理机制冲突,导致Security认为用户未认证。

优化方案:
改用Spring Security提供的@AuthenticationPrincipal注解获取用户:

@GetMapping("/random")
public QuizItem getRandomQuizItem(@AuthenticationPrincipal User user) {
    logger.info("User object: {}", user);

    if (user == null) {
        logger.error("User not logged in");
        throw new IllegalArgumentException("User not logged in");
    }

    return quizService.getDailyQuizItem();
}

需确保User类实现UserDetails接口,或在Security配置中正确配置用户认证逻辑。

4. Spring Security层面跨域配置缺失

仅在Controller添加@CrossOrigin可能不够,需在Security中补充全局跨域配置:

@Configuration
public class CorsConfig {
    @Bean
    public CorsFilter corsFilter() {
        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        CorsConfiguration config = new CorsConfiguration();
        config.setAllowCredentials(true);
        config.addAllowedOrigin("http://localhost:3000");
        config.addAllowedHeader("*");
        config.addAllowedMethod("*");
        source.registerCorsConfiguration("/**", config);
        return new CorsFilter(source);
    }
}

同时在SecurityConfig中启用CORS:

http.cors().and()...

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.20 21:05:57