Spring Boot&Wicket升级后认证引发无限循环问题求助
Spring Boot 2.6与Wicket 9.6升级后认证流程无限循环问题
将应用从Spring Boot 2.1升级到2.6、Wicket从8.0升级到9.6,已解决两个循环依赖问题,但使用H2数据库启动时,认证流程陷入无限循环,无法定位原因。
重复出现的StackTrace片段:
at com.xyz.ufa.app.TestUFSession.<init>(TestUFSession.java:15) at jdk.internal.reflect.GeneratedConstructorAccessor102.newInstance(Unknown Source) at java.base/jdk.internal.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45) at java.base/java.lang.reflect.Constructor.newInstance(Constructor.java:490) at org.apache.wicket.authroles.authentication.AuthenticatedWebApplication.newSession(AuthenticatedWebApplication.java:108) at org.apache.wicket.Application.fetchCreateAndSetSession(Application.java:1527) at org.apache.wicket.Session.get(Session.java:194) at org.apache.wicket.protocol.http.WebSession.get(WebSession.java:41) at com.giffing.wicket.spring.boot.starter.configuration.extensions.external.spring.security.SecureWebSession.authenticate(SecureWebSession.java:48) at org.apache.wicket.authroles.authentication.AuthenticatedWebSession.signIn(AuthenticatedWebSession.java:66) at com.xyz.ufa.app.TestUFSession.<init>(TestUFSession.java:15)
相关代码
TestUFSession类
import com.xyz.ufa.frontend.config.UFSession; import org.apache.wicket.request.Request; /** * Helper session to login autmatically. * */ public class TestUFSession extends UFSession { public TestUFSession(Request request) { super(request); signIn("admin", "admin"); // 调用authenticate方法 } }
UFSession类
import com.giffing.wicket.spring.boot.starter.configuration.extensions.external.spring.security.SecureWebSession; import java.util.Locale; import lombok.Getter; import org.apache.wicket.request.Request; @Getter public class UFSession extends SecureWebSession { private String username; private Locale locale; public UFSession(Request request) { super(request); locale = request.getLocale(); } @Override public void signOut() { username = null; super.signOut(); } }
WebSecurityConfiguration类
import com.xyz.uf.common.ApplicationProfile; import java.util.Optional; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Import; import org.springframework.context.annotation.Profile; import org.springframework.ldap.core.support.LdapContextSource; import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.authentication.AuthenticationProvider; import org.springframework.security.authentication.AuthenticationServiceException; import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; import org.springframework.security.config.annotation.authentication.configurers.userdetails.DaoAuthenticationConfigurer; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; import org.springframework.security.core.authority.mapping.GrantedAuthoritiesMapper; import org.springframework.security.crypto.password.NoOpPasswordEncoder; import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.security.ldap.authentication.AbstractLdapAuthenticationProvider; import org.springframework.security.ldap.authentication.LdapAuthenticationProvider; import org.springframework.security.ldap.authentication.PasswordComparisonAuthenticator; import org.springframework.security.ldap.authentication.ad.ActiveDirectoryLdapAuthenticationProvider; import org.springframework.security.ldap.search.FilterBasedLdapUserSearch; import org.springframework.security.ldap.search.LdapUserSearch; import org.springframework.security.ldap.userdetails.DefaultLdapAuthoritiesPopulator; import org.springframework.security.ldap.userdetails.LdapAuthoritiesPopulator; import org.springframework.security.ldap.userdetails.LdapUserDetailsService; @Slf4j @Configuration @Import(LdapProperties.class) @ComponentScan(basePackages = {"com.xyz.ufa.security.userinfo"}) public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter { private final String AUTHENTICATION_PROVIDER_BEAN_NAME = "authenticationProvider"; @Autowired private LdapProperties ldapProperties; @Bean(name = "authenticationManager") @Override public AuthenticationManager authenticationManagerBean() throws AuthenticationServiceException { try { return super.authenticationManagerBean(); } catch (Exception e) { log.error("Error in authenticationManagerBean", e); throw new AuthenticationServiceException(e.getMessage(), e); } } @Override protected void configure(HttpSecurity httpSecurity) throws AuthenticationServiceException { try { httpSecurity .csrf().disable() .authorizeRequests() .antMatchers("/*").permitAll() .antMatchers("/restservice/**").hasAuthority(UFARole.TECHNICAL_ADMIN) .and().httpBasic() .and().logout().permitAll(); httpSecurity.headers().frameOptions().disable(); } catch (Exception e) { throw new AuthenticationServiceException(String.format("Could not configure %s with csrf disabled and matching Pattern /*.", httpSecurity), e); } } @Override public void configure(AuthenticationManagerBuilder authenticationManagerBuilder) throws Exception { try { AbstractLdapAuthenticationProvider authentProvider = (AbstractLdapAuthenticationProvider) getApplicationContext().getBean(AUTHENTICATION_PROVIDER_BEAN_NAME); authentProvider.setAuthoritiesMapper(authoritiesMapper()); DaoAuthenticationConfigurer<AuthenticationManagerBuilder, LdapUserDetailsService> configurer = authenticationManagerBuilder .authenticationProvider(authentProvider) .userDetailsService(ldapUserDetailsService()); passwordEncoder.ifPresent(configurer::passwordEncoder); } catch (Exception e) { throw new AuthenticationServiceException("Could not configure authentication manager ", e); } } @Bean public LdapUserDetailsService ldapUserDetailsService() { LdapUserDetailsService userDetailsService = new LdapUserDetailsService(userSearch(), ldapAuthoritiesPopulator()); return userDetailsService; } @Bean public LdapUserSearch userSearch() { return new FilterBasedLdapUserSearch(ldapProperties.getUserSearchBase(), ldapProperties.getUserSearchFilter(), contextSource()); } @Bean public GrantedAuthoritiesMapper authoritiesMapper() { return new GrantAuthoritiesMapperWithEnvTag(ldapProperties.getEnv()); } @Bean public LdapContextSource contextSource() { LdapContextSource ldapContextSource = new LdapContextSource(); ldapContextSource.setUrl(ldapProperties.getServerUrl()); ldapContextSource.setAnonymousReadOnly(true); return ldapContextSource; } @Bean public LdapAuthoritiesPopulator ldapAuthoritiesPopulator() { DefaultLdapAuthoritiesPopulator ldapAuthoritiesPopulator = new DefaultLdapAuthoritiesPopulator(contextSource(), ldapProperties.getGroupSearchBase()); ldapAuthoritiesPopulator.setGroupSearchFilter(ldapProperties.getGroupSearchFilter()); ldapAuthoritiesPopulator.setGroupRoleAttribute(ldapProperties.getGroupRoleAttribute()); ldapAuthoritiesPopulator.setRolePrefix(""); ldapAuthoritiesPopulator.setConvertToUpperCase(false); return ldapAuthoritiesPopulator; } @Bean(name = AUTHENTICATION_PROVIDER_BEAN_NAME) @Profile(value = { ApplicationProfile.Values.TEST, ApplicationProfile.Values.PROD }) public AuthenticationProvider activeDirectory() { ActiveDirectoryLdapAuthenticationProvider authenticationProvider = new ActiveDirectoryLdapAuthenticationProvider("HRE.LOC", ldapProperties.getServerUrl()); authenticationProvider.setSearchFilter(ldapProperties.getUserSearchFilter()); return authenticationProvider; } @Bean(name = AUTHENTICATION_PROVIDER_BEAN_NAME) @Profile(value = { ApplicationProfile.Values.DEFAULT, ApplicationProfile.Values.DEV }) public AuthenticationProvider defaultAuthenticationProvider() { PasswordComparisonAuthenticator authenticator = new PasswordComparisonAuthenticator(contextSource()); authenticator.setPasswordAttributeName("userPassword"); passwordEncoder.ifPresent(authenticator::setPasswordEncoder); authenticator.setUserSearch(userSearch()); LdapAuthenticationProvider authenticationProvider = new LdapAuthenticationProvider(authenticator, ldapAuthoritiesPopulator()); return authenticationProvider; } /** * 该Bean为可选,仅部分Profile可用。密码编码器仅用于嵌入式LDAP,生产环境的Active Directory不需要 */ @Bean("passwordEncoder") @Profile(value = { ApplicationProfile.Values.DEFAULT, ApplicationProfile.Values.DEV }) public static PasswordEncoder passwordEncoder() { return NoOpPasswordEncoder.getInstance(); } @Autowired(required = false) @Qualifier("passwordEncoder") private Optional<PasswordEncoder> passwordEncoder; }
测试使用的Profile为default。
补充说明
经提示已理解问题,但无法修复。TestUFApplication通过ReflectionTestUtils实例化:
@SpringBootApplication public class TestApplicationWithH2Database { @Autowired private UFyWicketWebApplication webApplication; @Value("${test.autologin.active}") private boolean testAutologinActive; public static void main(String[] args) { new SpringApplicationBuilder() .sources(Application.class) .run(args); } @PostConstruct public void PostConstruct() { if (testAutologinActive) { ReflectionTestUtils.setField(webApplication, "sessionClass", TestUFSession.class); } } }
尝试在TestUFSession中添加signIn()方法并调用:
TestUFSession testUFSession = (TestUFSession) ReflectionTestUtils.getField(webApplication, "sessionClass"); testUFSession.signIn();
但出现ClassCastException。
内容的提问来源于stack exchange,提问作者clatadia
相关产品推荐
相关产品推荐

