我正在将服务从 Spring Boot 2.7 升级到 3.2.0,并遇到一个问题,在访问执行器端点时出现
HTTP Status 500 – Internal Server Error
错误。
查看堆栈跟踪,我可以看到,当通过端口 9090 访问执行器端点时,
org.springframework.security.config.annotation.web.AbstractRequestMatcherRegistry.DispatcherServletDelegatingRequestMatcher#matcher
方法无法在 dispatcherServletRegistration
调用中找到 this.servletContext.getServletRegistration(name);
。
当访问端口 8080 上的普通应用程序端点时,我可以看到它发现它很好。
这是我的执行器配置,除了
defaults
更新之外,它与我的 spring-boot 2.7 配置相比没有变化。
management:
defaults:
metrics:
export:
enabled: true
endpoints:
web:
exposure:
include: '*'
info:
build:
enabled: true
endpoint:
health:
show-details: when_authorized
server:
port: 9090
启动日志似乎表明一切设置正常
2023-12-23T21:36:28.729 [main] [] INFO o.a.coyote.http11.Http11NioProtocol.log - Starting ProtocolHandler ["http-nio-8080"]
2023-12-23T21:36:28.755 [main] [] INFO o.s.b.w.e.tomcat.TomcatWebServer.start - Tomcat started on port 8080 (http) with context path ''
HOTSWAP AGENT: 21:36:28.756 INFO (org.hotswap.agent.plugin.spring.SpringPlugin) - Spring plugin initialized - Spring core version '6.1.1'
2023-12-23T21:36:28.813 [main] [] INFO o.s.b.w.e.tomcat.TomcatWebServer.initialize - Tomcat initialized with port 9090 (http)
2023-12-23T21:36:28.813 [main] [] INFO o.a.coyote.http11.Http11NioProtocol.log - Initializing ProtocolHandler ["http-nio-9090"]
2023-12-23T21:36:28.813 [main] [] INFO o.a.catalina.core.StandardService.log - Starting service [Tomcat]
2023-12-23T21:36:28.813 [main] [] INFO o.a.catalina.core.StandardEngine.log - Starting Servlet engine: [Apache Tomcat/10.1.16]
(不带热插拔代理启动没有区别)
完成了很多其他 spring-boot 2->3 迁移,也许我错过了一些东西(也许与 spring-security 相关)?
任何有关问题可能是什么或如何解决问题的建议都非常感谢。
Edit1:这是访问时的堆栈跟踪,例如本地主机:9090/执行器
java.lang.IllegalArgumentException: Failed to find servlet [dispatcherServletRegistration] in the servlet context
org.springframework.util.Assert.notNull(Assert.java:172)
org.springframework.security.config.annotation.web.AbstractRequestMatcherRegistry$DispatcherServletDelegatingRequestMatcher.matcher(AbstractRequestMatcherRegistry.java:529)
org.springframework.security.web.access.intercept.RequestMatcherDelegatingAuthorizationManager.check(RequestMatcherDelegatingAuthorizationManager.java:79)
org.springframework.security.web.access.intercept.RequestMatcherDelegatingAuthorizationManager.check(RequestMatcherDelegatingAuthorizationManager.java:48)
org.springframework.security.authorization.ObservationAuthorizationManager.check(ObservationAuthorizationManager.java:63)
org.springframework.security.web.access.intercept.AuthorizationFilter.doFilter(AuthorizationFilter.java:95)
org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240)
org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:227)
org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137)
org.springframework.security.web.access.ExceptionTranslationFilter.doFilter(ExceptionTranslationFilter.java:126)
org.springframework.security.web.access.ExceptionTranslationFilter.doFilter(ExceptionTranslationFilter.java:120)
org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240)
org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:227)
org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137)
org.springframework.security.web.authentication.AnonymousAuthenticationFilter.doFilter(AnonymousAuthenticationFilter.java:100)
org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240)
org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:227)
org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137)
org.springframework.security.web.authentication.rememberme.RememberMeAuthenticationFilter.doFilter(RememberMeAuthenticationFilter.java:145)
org.springframework.security.web.authentication.rememberme.RememberMeAuthenticationFilter.doFilter(RememberMeAuthenticationFilter.java:101)
org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240)
org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:227)
org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137)
org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter.doFilter(SecurityContextHolderAwareRequestFilter.java:179)
org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240)
org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:227)
org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137)
org.springframework.security.web.savedrequest.RequestCacheAwareFilter.doFilter(RequestCacheAwareFilter.java:63)
org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240)
org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:227)
org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137)
org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter.doFilter(AbstractAuthenticationProcessingFilter.java:227)
org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter.doFilter(AbstractAuthenticationProcessingFilter.java:221)
org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240)
org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:227)
org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137)
org.springframework.security.web.authentication.logout.LogoutFilter.doFilter(LogoutFilter.java:107)
org.springframework.security.web.authentication.logout.LogoutFilter.doFilter(LogoutFilter.java:93)
org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240)
org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:227)
org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137)
org.springframework.security.web.csrf.CsrfFilter.doFilterInternal(CsrfFilter.java:117)
org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116)
org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240)
org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:227)
org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137)
org.springframework.web.filter.CorsFilter.doFilterInternal(CorsFilter.java:91)
org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116)
org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240)
org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:227)
org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137)
org.springframework.security.web.header.HeaderWriterFilter.doHeadersAfter(HeaderWriterFilter.java:90)
org.springframework.security.web.header.HeaderWriterFilter.doFilterInternal(HeaderWriterFilter.java:75)
org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116)
org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240)
org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:227)
org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137)
org.springframework.security.web.context.SecurityContextHolderFilter.doFilter(SecurityContextHolderFilter.java:82)
org.springframework.security.web.context.SecurityContextHolderFilter.doFilter(SecurityContextHolderFilter.java:69)
org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240)
org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:227)
org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137)
org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter.doFilterInternal(WebAsyncManagerIntegrationFilter.java:62)
org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116)
org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240)
org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:227)
org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137)
org.springframework.security.web.session.DisableEncodeUrlFilter.doFilterInternal(DisableEncodeUrlFilter.java:42)
org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116)
org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240)
org.springframework.security.web.ObservationFilterChainDecorator$AroundFilterObservation$SimpleAroundFilterObservation.lambda$wrap$0(ObservationFilterChainDecorator.java:323)
org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:224)
org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137)
org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:233)
org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:191)
org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:352)
org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:268)
编辑2:
创建了一个全新的 Spring Initilizer 项目,并看到以下看起来相关的差异,但尚未确定原因。
Spring Initilizer 新项目:
2023-12-26T18:23:07.912Z INFO 43176 --- [2)-192.168.0.31] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring DispatcherServlet 'dispatcherServlet'
2023-12-26T18:23:07.912Z INFO 43176 --- [2)-192.168.0.31] o.s.web.servlet.DispatcherServlet : Initializing Servlet 'dispatcherServlet'
2023-12-26T18:23:07.913Z INFO 43176 --- [2)-192.168.0.31] o.s.web.servlet.DispatcherServlet : Completed initialization in 0 ms
2023-12-26T18:23:13.567Z INFO 43176 --- [nio-9090-exec-1] o.a.c.c.C.[Tomcat-1].[localhost].[/] : Initializing Spring DispatcherServlet 'dispatcherServletRegistration'
2023-12-26T18:23:13.567Z INFO 43176 --- [nio-9090-exec-1] o.s.web.servlet.DispatcherServlet : Initializing Servlet 'dispatcherServletRegistration'
2023-12-26T18:23:13.567Z INFO 43176 --- [nio-9090-exec-1] o.s.web.servlet.DispatcherServlet : Completed initialization in 0 ms
我的应用程序
2023-12-26T18:24:40.958 [RMI TCP Connection(4)-192.168.0.31] [] INFO o.a.c.c.C.[Tomcat].[localhost].[/].log - Initializing Spring DispatcherServlet 'dispatcherServlet'
2023-12-26T18:24:40.958 [RMI TCP Connection(4)-192.168.0.31] [] INFO o.s.web.servlet.DispatcherServlet.initServletBean - Initializing Servlet 'dispatcherServlet'
2023-12-26T18:24:40.960 [RMI TCP Connection(4)-192.168.0.31] [] INFO o.s.web.servlet.DispatcherServlet.initServletBean - Completed initialization in 1 ms
干杯, 迈克
短短版
对于执行器,请勿使用
.requestMatcher("/actuator/health")
而是使用 MvcRequestMatcher.Builder#pattern
稍微长一点的版本
创建小poc后进行故障排除;我的问题归结为如何将请求匹配器添加到
SecurityFilterChain
。
旧代码只是简单地使用类似
.requestMatchers("/actuator/health").permitAll()
的东西,这不再是正确的方法。
org.springframework.security.web.access.intercept.RequestMatcherDelegatingAuthorizationManager#check(java.util.function.Supplier, jakarta.servlet.http.HttpServletRequest)
迭代配置的请求匹配器列表。
当遇到类似
requestMatcher("/path")
的匹配器时,使用的底层请求匹配器的类型为 org.springframework.security.config.annotation.web.AbstractRequestMatcherRegistry.DispatcherServletDelegatingRequestMatcher
。
匹配器逻辑在失败时执行以下操作(注册为空)
@Override
public MatchResult matcher(HttpServletRequest request) {
String name = request.getHttpServletMapping().getServletName();
ServletRegistration registration = this.servletContext.getServletRegistration(name); <-- Doesn't find a registration
Assert.notNull(registration, "Failed to find servlet [" + name + "] in the servlet context");
if (isDispatcherServlet(registration)) {
return this.mvc.matcher(request);
}
return this.ant.matcher(request);
}
我通过定义这个 bean 来创建不同的 Matcher 来解决我的问题
@Bean
MvcRequestMatcher.Builder mvc(HandlerMappingIntrospector introspector) {
return new MvcRequestMatcher.Builder(introspector);
}
我将其注入到我的
SecurityFilterChain
bean 创建方法中并像这样使用
.requestMatchers(mvc.pattern("/actuator/health"), mvc.pattern("/actuator/info")).permitAll()
我还遇到过各种各样的其他问题
antMatcher("/css/**")
。.requestMatchers(PathRequest.toStaticResources().atCommonLocations()).permitAll()
不再有效。我仍在思考这个问题,在尝试了大量荒谬的变化之后,我还没有完全理解指定请求匹配器的不同方法,但希望上面的内容对其他人有用。
您可以使用:
.requestMatchers(new AntPathRequestMatcher("/actuator/health")).permitAll()
在不同的端口(例如 9090)上拥有执行器端点将创建额外的 Servlet 上下文(dispatcherServletRegistration),为此您不能使用为应用程序的其余部分(端口 8080)定义的 Spring Security,因为请求匹配器严格绑定到它自己的 servlet 上下文 (dispatcherServlet)。
要解决此问题,请指示主应用程序的 SecurityFilterChain 不要用于执行器端点
public SecurityFilterChain webFilterChain(HttpSecurity http) {
http.securityMatcher(new NegatedRequestMatcher(new AntPathRequestMatcher("/actuator/**")));
...
return http.build();
}
并为执行器定义新的SecurityFilterChain
@Bean
public SecurityFilterChain actuatorFilterChain(HttpSecurity http) throws Exception {
http.securityMatcher(EndpointRequest.toAnyEndpoint());
http.authorizeHttpRequests((requests) -> requests.anyRequest().permitAll());
return http.build();
}
请注意,此配置将允许任何人无需任何凭据即可访问执行器。