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会捕获这个异常,误判为未认证请求,进而触发重定向到登录页的逻辑。
解决方案
- 修正参数获取注解
把@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"; }
- 兜底安全配置(可选)
虽然web.ignoring()已经跳过了安全过滤器,但如果存在上下文路径匹配的潜在问题,可以在SecurityFilterChain里给/ws/**显式加上permitAll()作为双重保障:
.authorizeHttpRequests() .requestMatchers("/ws/**").permitAll() // 新增该行 .requestMatchers("/authenticate").permitAll() // 其他原有规则保持不变
- 确认路径匹配规则
你的应用上下文是/my-api,web.ignoring()里的/ws/**是相对路径,Spring Security默认会匹配相对于上下文的路径,这部分是正常的。如果仍有疑问,可以明确指定匹配器:
.web.ignoring() .requestMatchers(new AntPathRequestMatcher("/ws/**"))
内容的提问来源于stack exchange,提问作者pojo-guy
相关产品推荐
相关产品推荐

