AnnotationConfigurationException после обновления до Spring Boot 3/JDK 17JAVA

Программисты JAVA общаются здесь
Ответить
Anonymous
 AnnotationConfigurationException после обновления до Spring Boot 3/JDK 17

Сообщение Anonymous »

Недавно я обновил свое приложение Spring Boot до Spring Boot 3 (с 2.6.4), Kotlin 1.8.0 (с 1.7.20) и JDK 17 (с 11). Прямо сейчас я сталкиваюсь с исключениями AnnotationConfigurationExceptions, вызванными метааннотациями @PreAuthorize, используемыми в унаследованных методах репозитория.
org.springframework.core.annotation.AnnotationConfigurationException: Found more than one annotation of type interface org.springframework.security.access.prepost.PreAuthorize attributed to public final java.util.List jdk.proxy2.$Proxy390.findAll() Please remove the duplicate annotations and publish a bean to handle your authorization logic.
at org.springframework.security.authorization.method.AuthorizationAnnotationUtils.findUniqueAnnotation(AuthorizationAnnotationUtils.java:64)
at org.springframework.security.authorization.method.PreAuthorizeExpressionAttributeRegistry.findPreAuthorizeAnnotation(PreAuthorizeExpressionAttributeRegistry.java:71)
at org.springframework.security.authorization.method.PreAuthorizeExpressionAttributeRegistry.resolveAttribute(PreAuthorizeExpressionAttributeRegistry.java:61)
at org.springframework.security.authorization.method.AbstractExpressionAttributeRegistry.lambda$getAttribute$0(AbstractExpressionAttributeRegistry.java:57)
at java.base/java.util.concurrent.ConcurrentHashMap.computeIfAbsent(ConcurrentHashMap.java:1708)
at org.springframework.security.authorization.method.AbstractExpressionAttributeRegistry.getAttribute(AbstractExpressionAttributeRegistry.java:57)
at org.springframework.security.authorization.method.AbstractExpressionAttributeRegistry.getAttribute(AbstractExpressionAttributeRegistry.java:46)
at org.springframework.security.authorization.method.PreAuthorizeAuthorizationManager.check(PreAuthorizeAuthorizationManager.java:63)
at org.springframework.security.authorization.method.PreAuthorizeAuthorizationManager.check(PreAuthorizeAuthorizationManager.java:40)
at org.springframework.security.authorization.ObservationAuthorizationManager.check(ObservationAuthorizationManager.java:55)
at org.springframework.security.config.annotation.method.configuration.DeferringObservationAuthorizationManager.check(DeferringObservationAuthorizationManager.java:47)
at org.springframework.security.authorization.method.AuthorizationManagerBeforeMethodInterceptor.attemptAuthorization(AuthorizationManagerBeforeMethodInterceptor.java:252)
at org.springframework.security.authorization.method.AuthorizationManagerBeforeMethodInterceptor.invoke(AuthorizationManagerBeforeMethodInterceptor.java:198)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:184)
at org.springframework.aop.aspectj.MethodInvocationProceedingJoinPoint.proceed(MethodInvocationProceedingJoinPoint.java:89)

После некоторой отладки я обнаружил, что Spring Security каким-то образом отражает одну аннотацию несколько раз. Как только я перемещаю методы из базового репозитория в реальный репозиторий, исключение больше не появляется.
У меня есть базовый репозиторий, реализующий некоторые распространенные методы, такие как findAll, findById и т. д., помечая их доступными с помощью REST-API и ограничивая доступ с помощью метааннотации @HasRoleAdminOrHasReadCollectionPermission (позже):
@NoRepositoryBean
interface BaseRepository : CrudRepository {

/* [...] */

@RestResource
@HasRoleAdminOrHasReadCollectionPermission
override fun findAll(): List

/* [...] */

}

Во-вторых, базовый репозиторий реализуется фактическим репозиторием сущностей:
@RepositoryRestResource(path = "categories", collectionResourceRel = "categories")
@Transactional(readOnly = true)
interface CategoryRepository : BaseRepository {

/* [...] */

}

В-третьих, вот метааннотация @HasRoleAdminOrHasReadCollectionPermission:
@Retention(AnnotationRetention.RUNTIME)
@Target(AnnotationTarget.CLASS, AnnotationTarget.FUNCTION)
@PreAuthorize("hasRole('$ROLE_ADMIN') or hasPermission($COLLECTION_TARGET, $DELETE_PERMISSION)")
annotation class HasRoleAdminOrHasDeleteCollectionPermission

У меня закончились идеи, просмотрел Java-файлы, сгенерированные из Kotlin, нигде не дублируются аннотации. Надеюсь, кто-нибудь здесь сможет мне в этом помочь.
Изменить: в соответствии с просьбой, вот класс приложения и класс, содержащий конфигурацию безопасности:
@SpringBootApplication(exclude = [ErrorMvcAutoConfiguration::class])
@ServletComponentScan
@EnableAsync
@EnableScheduling
class Application {
companion object {
val HOSTNAME = System.getenv("HOSTNAME")
val TIMESTAMP = System.currentTimeMillis()

/** Entry point, run with gradle `bootRun` task. */
@JvmStatic
fun main(args: Array) {
SpringApplication.run(Application::class.java, *args)
}
}
}

@Configuration
@EnableWebSecurity(debug = true)
@EnableMethodSecurity(prePostEnabled = true)
class SecurityConfig(
private val environment: Environment,
private val baseProperties: BaseProperties,
private val accountService: AccountService,
private val integrationService: IntegrationService,
private val jwtService: JwtService,
private val loginAttemptService: LoginAttemptService,
private val handlerExceptionResolver: HandlerExceptionResolver
) {

@Bean
fun webSecurityCustomizer(): WebSecurityCustomizer =
WebSecurityCustomizer { web: WebSecurity -> web.debug(true) }

@Bean
fun corsConfigurationSource(): CorsConfigurationSource {
val configuration = CorsConfiguration()
configuration.allowedOrigins = baseProperties.corsAllowedOrigins()
configuration.allowedMethods = listOf(CorsConfiguration.ALL)
configuration.allowedHeaders = listOf(CorsConfiguration.ALL)
configuration.allowCredentials = true
val source = UrlBasedCorsConfigurationSource()
source.registerCorsConfiguration(ANY_PATH, configuration)
return source
}

@Bean
fun userDetailsService(): UserDetailsService =
AccountUserDetailsService(accountService, integrationService)

@Bean
fun authenticationManager(
http: HttpSecurity,
userDetailsService: UserDetailsService
): AuthenticationManager {
val authenticationManagerBuilder = http.getSharedObject(AuthenticationManagerBuilder::class.java)

val loginAttemptDaoAuthenticationProvider = LoginAttemptDaoAuthenticationProvider(loginAttemptService, userDetailsService)
val jwtPreAuthenticationProvider = PreAuthenticatedAuthenticationProvider()

jwtPreAuthenticationProvider.setPreAuthenticatedUserDetailsService(UserDetailsByNameServiceWrapper(userDetailsService))

return authenticationManagerBuilder
.userDetailsService(userDetailsService).and()
.authenticationProvider(loginAttemptDaoAuthenticationProvider)
.authenticationProvider(jwtPreAuthenticationProvider)
.build()
}

@Bean
fun filterChain(
http: HttpSecurity,
authenticationManager: AuthenticationManager,
userDetailsService: UserDetailsService
): SecurityFilterChain {
return http
.requireSSL()
.disableLocalHSTS()
.enableCors()
.enableStatelessCsrf()
.statelessSessionManagement()
.jwtAuthentication(authenticationManager)
.setRequestRestrictions()
.build()
}

private fun HttpSecurity.requireSSL(): HttpSecurity =
requiresChannel().anyRequest().requiresSecure().and()

private fun HttpSecurity.disableLocalHSTS(): HttpSecurity =
if (environment.acceptsProfiles(Profiles.of(DEVELOPMENT, TESTING)))
headers().httpStrictTransportSecurity().disable().and()
else this

private fun HttpSecurity.enableCors(): HttpSecurity =
cors().and()

private fun HttpSecurity.enableStatelessCsrf(): HttpSecurity =
csrf().disable().addFilterBefore(StatelessCsrfFilter(), CsrfFilter::class.java)

private fun HttpSecurity.statelessSessionManagement(): HttpSecurity =
sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and()

private fun HttpSecurity.jwtAuthentication(
authenticationManager: AuthenticationManager
): HttpSecurity =
addFilterBefore(JwtPreAuthenticatedProcessingFilter(authenticationManager, jwtService), UsernamePasswordAuthenticationFilter::class.java)
.addFilterBefore(JwtCookiePreAuthenticationFilter(jwtService), UsernamePasswordAuthenticationFilter::class.java)
.formLogin()
.loginProcessingUrl(LOGIN_URL)
.successHandler(JwtCookieAuthenticationSuccessHandler(jwtService))
.failureHandler(AuthenticationFailureResolver(handlerExceptionResolver))
.and()
.logout()
.logoutUrl(LOGOUT_URL)
.logoutSuccessHandler(Http200LogoutSuccessHandler())
.addLogoutHandler(JwtCookieLogoutHandler(jwtService))
.and()

private fun HttpSecurity.setRequestRestrictions(): HttpSecurity =
authorizeHttpRequests {
it.requestMatchers(HttpMethod.GET, ROOT_URL).permitAll()
.requestMatchers([...])
.anyRequest().denyAll().and()
}
}



Подробнее здесь: https://stackoverflow.com/questions/751 ... t-3-jdk-17
Ответить

Быстрый ответ

Изменение регистра текста: 
Смайлики
:) :( :oops: :roll: :wink: :muza: :clever: :sorry: :angel: :read: *x)
Ещё смайлики…
   
К этому ответу прикреплено по крайней мере одно вложение.

Если вы не хотите добавлять вложения, оставьте поля пустыми.

Максимально разрешённый размер вложения: 15 МБ.

Вернуться в «JAVA»