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

Spring Web应用WebService接口被重定向至/login的问题排查

Spring WebService接口被意外重定向至/login的排查与解决

我正在给现有Spring Web应用改造一个WebService接口,需要避免该接口被重定向到/login。目标API已经添加到ignoring匹配器里,理论上应该可以直接调用不需要额外验证。但用Postman请求该URL时,日志显示这个URL无需安全验证,本该调用WebService端点,结果框架却把请求重定向到/login并尝试验证。请问遗漏了什么配置,才能让未认证用户调用这个WebService接口?

代码片段1:WebService端点

@CrossOrigin
@PostMapping(value = "/ws/createArchive", headers = "Accept=*/*")
public String createArchive (@RequestAttribute String apiKey,  @RequestBody Map<String, Object> body) {
    logger.info("/ws/createArchive received:\r\n", body.toString());
    return "Ok";
}

代码片段2:Spring Web Security初始化

public WebSecurityCustomizer webSecurityCustomizer() {
    return (web) -> web.ignoring()
            .requestMatchers("/", "/ws/**", "/index.html", "/app/**", "/register", "/favicon.ico","/ajax/cfgDownloadReport","/ajax/importLicense")
        ;
}

@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
    http
    .addFilterBefore(new ForwardedHeaderFilter(), ForceEagerSessionCreationFilter.class)
    .csrf().disable()
    .authorizeHttpRequests()
    .requestMatchers("/authenticate").permitAll() // permitAll permits all authenticated users
    .requestMatchers("/applicationVersion").permitAll()
    .requestMatchers("/ajax/licenseValidation", "/ajax/cfgFetchAllNotifications", "/notification/search","/ajax/importLicense",
                "/ajax/licenseValidation", "/ajax/cfgFetchAllNotifications", "/notification/searchCount",
                "/ajax/jobExecutionParams","/ajax/keyValuePair/","/ajax/instanceregistry")
        .permitAll()
    .anyRequest().denyAll().and()
    .addFilterBefore(new JWTFilter(), UsernamePasswordAuthenticationFilter.class)
    .logout().deleteCookies(JWTFilter.getAuthorizationHeader()).invalidateHttpSession(true)
    .and()
    .sessionManagement()
    .sessionFixation().migrateSession()
    .sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED)
    .maximumSessions(2);

    return http.getOrBuild();
}

Postman请求的URL

http://127.0.0.1:8080/my-api/ws/createArchive?apiKey=12345

日志片段

http-nio-8080-exec-4 2024-04-22 15:41:35,831 TRACE o.s.s.w.FilterChainProxy - Trying to match request against DefaultSecurityFilterChain [RequestMatcher=Ant [pattern='/'], Filters=[]] (1/9)
http-nio-8080-exec-4 2024-04-22 15:41:35,832 TRACE o.s.s.w.FilterChainProxy - Trying to match request against DefaultSecurityFilterChain [RequestMatcher=Ant [pattern='/ws/**'], Filters=[]] (2/9)
http-nio-8080-exec-4 2024-04-22 15:41:35,833 TRACE o.s.s.w.FilterChainProxy - No security for POST /ws/createArchive?apiKey=12345
http-nio-8080-exec-4 2024-04-22 15:41:35,937 TRACE o.s.s.w.FilterChainProxy - Trying to match request against DefaultSecurityFilterChain [RequestMatcher=Ant [pattern='/'], Filters=[]] (1/9)
http-nio-8080-exec-4 2024-04-22 15:41:35,938 TRACE o.s.s.w.FilterChainProxy - Trying to match request against DefaultSecurityFilterChain [RequestMatcher=Ant [pattern='/ws/**'], Filters=[]] (2/9)
http-nio-8080-exec-4 2024-04-22 15:41:35,938 TRACE o.s.s.w.FilterChainProxy - Trying to match request against DefaultSecurityFilterChain [RequestMatcher=Ant [pattern='/index.html'], Filters=[]] (3/9)
http-nio-8080-exec-4 2024-04-22 15:41:35,938 TRACE o.s.s.w.FilterChainProxy - Trying to match request against DefaultSecurityFilterChain [RequestMatcher=Ant [pattern='/app/**'], Filters=[]] (4/9)
http-nio-8080-exec-4 2024-04-22 15:41:35,938 TRACE o.s.s.w.FilterChainProxy - Trying to match request against DefaultSecurityFilterChain [RequestMatcher=Ant [pattern='/register'], Filters=[]] (5/9)
http-nio-8080-exec-4 2024-04-22 15:41:35,938 TRACE o.s.s.w.FilterChainProxy - Trying to match request against DefaultSecurityFilterChain [RequestMatcher=Ant [pattern='/favicon.ico'], Filters=[]] (6/9)
http-nio-8080-exec-4 2024-04-22 15:41:35,939 TRACE o.s.s.w.FilterChainProxy - Trying to match request against DefaultSecurityFilterChain [RequestMatcher=Ant [pattern='/ajax/cfgDownloadReport'], Filters=[]] (7/9)
http-nio-8080-exec-4 2024-04-22 15:41:35,939 TRACE o.s.s.w.FilterChainProxy - Trying to match request against DefaultSecurityFilterChain [RequestMatcher=Ant [pattern='/ajax/importLicense'], Filters=[]] (8/9)
http-nio-8080-exec-4 2024-04-22 15:41:35,939 TRACE o.s.s.w.FilterChainProxy - Trying to match request against DefaultSecurityFilterChain [RequestMatcher=any request, Filters=[org.springframework.security.web.session.DisableEncodeUrlFilter@b760802, org.springframework.web.filter.ForwardedHeaderFilter@208e2b0, org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter@7e6707c5, org.springframework.security.web.context.SecurityContextPersistenceFilter@380f5cac, org.springframework.security.web.header.HeaderWriterFilter@1ce6d2f2, org.springframework.security.web.authentication.logout.LogoutFilter@15b3c1bf, com.mycompany.myapp.JWTFilter@752534aa, org.springframework.security.web.session.ConcurrentSessionFilter@130646f9, org.springframework.security.web.savedrequest.RequestCacheAwareFilter@59fdce16, org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter@792ce5eb, org.springframework.security.web.authentication.AnonymousAuthenticationFilter@4e280414, org.springframework.security.web.session.SessionManagementFilter@3b80ca33, org.springframework.security.web.access.ExceptionTranslationFilter@676d03bf, org.springframework.security.web.access.intercept.AuthorizationFilter@4a0a10b8]] (9/9)
http-nio-8080-exec-4 2024-04-22 15:41:35,939 DEBUG o.s.s.w.FilterChainProxy - Securing POST /login?apiKey=12345

问题根源

从日志能看到,第一次请求POST /ws/createArchive?apiKey=12345确实被Spring Security识别为无需安全验证,但随后出现了对POST /login?apiKey=12345的安全校验,说明请求被重定向了。

核心问题出在WebService端点的参数获取上:你用@RequestAttribute String apiKey来获取参数,但实际请求里apiKey是URL查询参数,不是请求属性。当Spring MVC找不到对应的请求属性时,会抛出MissingRequestAttributeException,而Spring Security的ExceptionTranslationFilter会捕获这个异常,误判为未认证请求,进而触发重定向到登录页的逻辑。

解决方案

  1. 修正参数获取注解
    把@RequestAttribute改成@RequestParam,这样就能正确读取URL里的apiKey参数,同时修复日志语句的占位符问题:
@CrossOrigin
@PostMapping(value = "/ws/createArchive", headers = "Accept=*/*")
public String createArchive (@RequestParam String apiKey,  @RequestBody Map<String, Object> body) {
    logger.info("/ws/createArchive received:\r\n{}", body.toString());
    return "Ok";
}
  1. 兜底安全配置(可选)
    虽然web.ignoring()已经跳过了安全过滤器,但如果存在上下文路径匹配的潜在问题,可以在SecurityFilterChain里给/ws/**显式加上permitAll()作为双重保障:
.authorizeHttpRequests()
.requestMatchers("/ws/**").permitAll() // 新增该行
.requestMatchers("/authenticate").permitAll()
// 其他原有规则保持不变
  1. 确认路径匹配规则
    你的应用上下文是/my-api,web.ignoring()里的/ws/**是相对路径,Spring Security默认会匹配相对于上下文的路径,这部分是正常的。如果仍有疑问,可以明确指定匹配器:
.web.ignoring()
.requestMatchers(new AntPathRequestMatcher("/ws/**"))

内容的提问来源于stack exchange,提问作者pojo-guy

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.25 12:54:56