Closed JohnZ1385 closed 7 months ago
Thanks for the report, @JohnZ1385, sorry you are having trouble. Are you able to produce a minimal GitHub sample that demonstrates the issue? I think that will help us get to the bottom of your error more quickly.
For example, it may be that your ApplicationContextConfig
is in some way dependent on Spring Security's filter chain, but it's unclear from the current configuration.
@jzheaux that might be a bit of an issue because of it's EAR file dependency, if need be we can explore that option but I'll first share the ApplicationContextConfig (i.e. the RootConfig), SecurityConfig and ServletConfig classes here. Luckily they are pretty generic so I only made minimal obfuscation attempts here. I was initially under the impression that I might have that exact scenario, i.e. where my ApplicationContextConfig is potentially picking up spring security components unknowingly, but there is no component scan in the ApplicationContextConfig. The only component scan I use is for @Controller classes in the ServletConfig.
Here is the ApplicationContextConfig (i.e. RootConfig) file. As I mentioned earlier that app leverages an EAR file level shared application context .. so beans such as "ClientEmailService, ClientService" etc etc are defined in that parent application context.
Those are loaded by registering a ContextLoaderListener and overriding the method loadParentContext.
@Configuration
public class ApplicationContextConfig {
@Autowired
private StaticNameSpaceDataService staticNameSpaceDataService;
@Bean
public MonitorService monitorService(ClientEmailService clientEmailService, ClientService clientService, DeviceService deviceService,
DeviceStatusService deviceStatusService, UserClientService userClientService) {
return new MonitorServiceImpl(clientEmailService, clientService, deviceService, deviceStatusService, screenPreferenceService(), userClientService);
}
@Bean
public ScreenPreferenceService screenPreferenceService() {
return new ScreenPreferenceServiceImpl(staticNameSpaceDataService);
}
}
the SecurityConfig
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.ProviderManager;
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.provisioning.UserDetailsManager;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.access.AccessDeniedHandler;
import org.springframework.security.web.access.AccessDeniedHandlerImpl;
@Configuration
@EnableWebSecurity
public class SecurityConfig {
private static final String ROLE_CLIENT_USER = "CLIENT_USER";
@Autowired
private UserService userService;
@Bean("accessDeniedHandler")
public AccessDeniedHandler accessDeniedHandler() {
AccessDeniedHandlerImpl handler = new AccessDeniedHandlerImpl();
handler.setErrorPage("/accessDenied");
return handler;
}
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
return http
.sessionManagement().enableSessionUrlRewriting(false)
.and()
.authorizeHttpRequests()
.requestMatchers(HttpMethod.GET, "/common/css/**").permitAll()
.requestMatchers(HttpMethod.GET, "/common/images/**").permitAll()
.requestMatchers(HttpMethod.GET, "/common/jquery/**").permitAll()
.requestMatchers(HttpMethod.GET, "/common/js/**").permitAll()
.requestMatchers("/login").permitAll()
.requestMatchers("/home/**").hasRole(ROLE_CLIENT_USER)
.requestMatchers("/accessDenied/**", "/logout/").permitAll()
.requestMatchers("/**").hasRole(ROLE_CLIENT_USER)
.anyRequest().denyAll()
.and()
.formLogin()
.loginPage("/login").defaultSuccessUrl("/home/").usernameParameter("username").passwordParameter("password")
.loginProcessingUrl("/login").failureUrl("/login?error=1")
.and()
.logout().logoutUrl("/logout")
.and()
.exceptionHandling().accessDeniedHandler(accessDeniedHandler())
.and()
.headers().disable()
.csrf().disable()
.build();
}
@Bean("passwordEncoder")
public PasswordEncoder passwordEncoder() {
return new CustomMessageDigestPasswordEncoder("SHA-256");
}
@Bean("customUserDetailManager")
@Autowired
public UserDetailsManager customUserDetailManager() {
return new CustomUserDetailManager(userService);
}
@Bean("authenticationManager")
public AuthenticationManager authenticationManager() {
DaoAuthenticationProvider authenticationProvider = new DaoAuthenticationProvider();
authenticationProvider.setUserDetailsService(customUserDetailManager());
authenticationProvider.setPasswordEncoder(passwordEncoder());
return new ProviderManager(authenticationProvider);
}
}
and the ServletConfig (i.e. WebConfig)
import java.util.List;
import java.util.TimeZone;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.ComponentScan.Filter;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.FilterType;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.json.AbstractJackson2HttpMessageConverter;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
import org.springframework.stereotype.Controller;
import org.springframework.web.servlet.View;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.ViewResolverRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@EnableWebMvc
@Configuration
@ComponentScan(basePackages = { "com.organization.app.console" }, useDefaultFilters = false, includeFilters = @Filter(type = FilterType.ANNOTATION, value = Controller.class))
public class ServletConfig implements WebMvcConfigurer {
@Override
public void configureViewResolvers(ViewResolverRegistry registry) {
registry.enableContentNegotiation(new View[0]);
registry.jsp("/WEB-INF/views/", ".jsp");
}
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/**").addResourceLocations("/content/");
}
@Override
public void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
for (HttpMessageConverter<?> mc : converters) {
if (mc instanceof MappingJackson2HttpMessageConverter) {
((AbstractJackson2HttpMessageConverter) mc).getObjectMapper().setTimeZone(TimeZone.getDefault());
}
}
return;
}
}
@jzheaux I was able to solve this by following this comment: https://github.com/spring-projects/spring-security/issues/12319#issuecomment-1338377623 and setting the attribute org.springframework.web.servlet.FrameworkServlet.CONTEXT.[myServletName]
I could only actually accomplish this by overloading the WebApplicationInitializer.onStartup(ServletContext servletContext) method .. based on the numerous similar issues/posts I've seen for issues like this it might not be the worst idea in the future for Spring to support some sort of subclass of WebApplicationInitializer or feature set to handle this setup.
Hi, I tried asking this on community forums and stackoverflow and got no response but I'm also not sure there isn't potentially something wrong here regardless. I have an application that's an EAR deployment comprised of shared libraries, EJB's, and various WARs .. each of those WARs has a spring mvc + spring security context along with a root context for bean definitions local to that webapp along with a reference to a shared context defined in the lib jars that comprise the EAR file. Since spring security 5.8.9 I've seen the message:
org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'org.springframework.security.config.annotation.web.configuration.WebSecurityConfiguration': Unsatisfied dependency expressed through method 'setFilterChains' parameter 0; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'securityFilterChain' defined in com.[organization].[app].console.SecurityConfig: Bean instantiation via factory method failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [org.springframework.security.web.SecurityFilterChain]: Factory method 'securityFilterChain' threw exception; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No bean named 'A Bean named mvcHandlerMappingIntrospector of type org.springframework.web.servlet.handler.HandlerMappingIntrospector is required to use MvcRequestMatcher. Please ensure Spring Security & Spring MVC are configured in a shared ApplicationContext.' available
the web.xml (slightly obfuscated) for quick reference:
The error message is pretty clear that the SecurityConfig and ServletConfig should be in a shared ApplicationContext .. so I moved them accordingly.
A startup with this setup in the web.xml just leads to a generic error:
At very least I have attempted to simulate loading this configuration setup via a Junit test and it seems to execute just fine. (ignore the code obfuscation)
Am I fundamentally missing something here? Is there perhaps an issue with the application server that I'm (unfortunately) stuck using for this application? (WebLogic 14.1.1) (their latest release though definitely a bit dated tech stack wise).