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

Spring Boot所有请求重定向至/authenticate端点问题求助

问题描述

Spring Boot应用中,标注@RestController的JwtAuthenticationController类包含/authenticate和/register两个POST端点,但访问任意端点(包括/register)都会被重定向至/authenticate。已确认端点映射为POST方法,重启应用、检查URL、验证参数类及业务方法均无效,日志无错误信息。

控制器简化代码

@RestController
@CrossOrigin
public class JwtAuthenticationController {
    // Autowired字段及其他方法已省略

    @PostMapping(value = "/authenticate")
    public ResponseEntity<?> createAuthenticationToken(@RequestBody JwtRequest authenticationRequest) throws Exception {
        // 认证逻辑已省略
    }

    @PostMapping(value = "/register")
    public ResponseEntity<?> saveUser(@RequestBody UserRq user) throws Exception {
        // 用户注册逻辑已省略
    }
}

相关参考代码

JwtRequestFilter代码

public class JwtRequestFilter extends OncePerRequestFilter {
private final JwtTokenUtil jwtTokenUtil;
private final JwtUserDetailsService jwtUserDetailsService;

public JwtRequestFilter(JwtTokenUtil jwtTokenUtil,
                        JwtUserDetailsService jwtUserDetailsService) {
    this.jwtTokenUtil = jwtTokenUtil;
    this.jwtUserDetailsService = jwtUserDetailsService;
}
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
        throws ServletException, IOException {
    final String requestTokenHeader = request.getHeader("Authorization");
    String username = null;
    String jwtToken = null;
    // JWT Token格式为"Bearer token",移除Bearer前缀获取Token
    if (requestTokenHeader != null && requestTokenHeader.startsWith("Bearer ")) {
        jwtToken = requestTokenHeader.substring(7);
        try {
            username = jwtTokenUtil.getUsernameFromToken(jwtToken);
        } catch (IllegalArgumentException e) {
            System.out.println("Unable to get JWT Token");
        } catch (ExpiredJwtException e) {
            System.out.println("JWT Token has expired");
        }
    } else {
        logger.warn("JWT Token does not begin with Bearer String");
    }
    // 获取Token后进行验证
    if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) {
        UserDetails userDetails = this.jwtUserDetailsService.loadUserByUsername(username);
        // 若Token有效,配置Spring Security手动设置认证
        if (jwtTokenUtil.validateToken(jwtToken, userDetails)) {
            UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken = new UsernamePasswordAuthenticationToken(
                    userDetails, null, userDetails.getAuthorities());
            usernamePasswordAuthenticationToken
                    .setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
            // 在上下文设置认证后,当前用户即被认证,可通过Spring Security配置
            SecurityContextHolder.getContext().setAuthentication(usernamePasswordAuthenticationToken);
        }
    }
    chain.doFilter(request, response);
}

SecurityConfiguration代码

package jb.microservices.login.util;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpStatus;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.security.web.authentication.AnonymousAuthenticationFilter;
import org.springframework.security.web.authentication.HttpStatusEntryPoint;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import org.springframework.security.web.util.matcher.OrRequestMatcher;
import org.springframework.security.web.util.matcher.RequestMatcher;

@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {

    @Autowired
    private JwtAuthenticationEntryPoint jwtAuthenticationEntryPoint;

    @Autowired
    private UserDetailsService jwtUserDetailsService;

    @Autowired
    private JwtRequestFilter jwtRequestFilter;

    @Autowired
    public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
        // 配置AuthenticationManager,使其知道从何处加载用户凭证
        // 使用BCryptPasswordEncoder
        auth.userDetailsService(jwtUserDetailsService).passwordEncoder(new BCryptPasswordEncoder());
    }

    @Bean
    @Override
    public AuthenticationManager authenticationManagerBean() throws Exception {
        return super.authenticationManagerBean();
    }

    @Override
    public void configure(WebSecurity web) {
        web.ignoring().antMatchers("/authenticate", "/register");
    }

    @Override
    protected void configure(HttpSecurity httpSecurity) throws Exception {
        // 本示例无需CSRF
        httpSecurity.csrf().disable()
                // 不对该请求进行认证
                .authorizeRequests().antMatchers("/authenticate", "/register").permitAll().
                // 所有其他请求需认证
                        anyRequest().authenticated().and().
                // 使用无状态会话,不存储用户状态
                        exceptionHandling().authenticationEntryPoint(jwtAuthenticationEntryPoint).and().sessionManagement()
                .sessionCreationPolicy(SessionCreationPolicy.STATELESS);

        // 添加过滤器,在每个请求中验证Token
        httpSecurity.addFilterBefore(jwtRequestFilter, UsernamePasswordAuthenticationFilter.class);
    }
}

排查与解决思路

1. 修复安全配置规则冲突

当前同时在WebSecurity和HttpSecurity中配置了对/authenticate、/register的放行规则,两者作用不同:

  • WebSecurity.ignoring()会让请求完全绕过Spring Security过滤器链
  • HttpSecurity.permitAll()允许请求通过过滤器链但无需认证

重复配置可能导致路由异常,建议保留HttpSecurity的规则,修改WebSecurity配置为仅忽略静态资源:

@Override
public void configure(WebSecurity web) {
    web.ignoring().antMatchers("/css/**", "/js/**", "/images/**");
}

2. 检查请求重定向状态

使用Postman或浏览器开发者工具查看请求响应状态码:

  • 若出现301/302,需确认请求URL是否带多余斜杠(如/register/),或请求方法是否为POST(而非GET)

3. 校验认证入口点实现

检查JwtAuthenticationEntryPoint的commence方法,确保仅返回401错误而非重定向:

@Component
public class JwtAuthenticationEntryPoint implements AuthenticationEntryPoint {
    @Override
    public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException {
        response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Unauthorized");
    }
}

4. 开启请求映射日志排查

在application.properties中添加配置,查看请求实际映射的控制器方法:

logging.level.org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping=DEBUG

启动应用后访问/register,日志会明确显示请求被路由到哪个方法,定位是否为路由配置问题。

5. 排查过滤器干扰

暂时移除JwtRequestFilter,测试/register是否能正常访问,逐步排除其他自定义过滤器或拦截器的干扰。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.14 20:15:57