Я столкнулся с некоторыми проблемами во время работы Spring Boot 2.7.0 и spring-boot-starter-oauth2-resource-server. Моя цель — использовать Keycloak для проверки токенов из http-запросов. Но поскольку я добавил spring-boot-starter-oauth2-resource-server в свое приложение, оно не запускается из-за исключения:
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'org.springframework.boot.autoconfigure.security.oauth2.resource.servlet.OAuth2ResourceServerOpaqueTokenConfiguration$OpaqueTokenIntrospectionClientConfiguration': Lookup method resolution failed; nested exception is java.lang.IllegalStateException: Failed to introspect Class [org.springframework.boot.autoconfigure.security.oauth2.resource.servlet.OAuth2ResourceServerOpaqueTokenConfiguration$OpaqueTokenIntrospectionClientConfiguration] from ClassLoader [jdk.internal.loader.ClassLoaders$AppClassLoader@6f94fa3e]
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.determineCandidateConstructors(AutowiredAnnotationBeanPostProcessor.java:289)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.determineConstructorsFromBeanPostProcessors(AbstractAutowireCapableBeanFactory.java:1302)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1219)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:582)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:542)
at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:335)
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:333)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:208)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:953)
at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:918)
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:583)
at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:147)
at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:734)
at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:408)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:308)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:1306)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:1295)
at com.test.TestApplication.main(TestApplication.java:14)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.base/java.lang.reflect.Method.invoke(Method.java:568)
at org.springframework.boot.devtools.restart.RestartLauncher.run(RestartLauncher.java:49)
Caused by: java.lang.IllegalStateException: Failed to introspect Class [org.springframework.boot.autoconfigure.security.oauth2.resource.servlet.OAuth2ResourceServerOpaqueTokenConfiguration$OpaqueTokenIntrospectionClientConfiguration] from ClassLoader [jdk.internal.loader.ClassLoaders$AppClassLoader@6f94fa3e]
at org.springframework.util.ReflectionUtils.getDeclaredMethods(ReflectionUtils.java:485)
at org.springframework.util.ReflectionUtils.doWithLocalMethods(ReflectionUtils.java:321)
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.determineCandidateConstructors(AutowiredAnnotationBeanPostProcessor.java:267)
... 23 common frames omitted
Caused by: java.lang.NoClassDefFoundError: org/springframework/security/oauth2/server/resource/introspection/SpringOpaqueTokenIntrospector
at java.base/java.lang.Class.getDeclaredMethods0(Native Method)
at java.base/java.lang.Class.privateGetDeclaredMethods(Class.java:3402)
at java.base/java.lang.Class.getDeclaredMethods(Class.java:2504)
at org.springframework.util.ReflectionUtils.getDeclaredMethods(ReflectionUtils.java:467)
... 25 common frames omitted
Caused by: java.lang.ClassNotFoundException: org.springframework.security.oauth2.server.resource.introspection.SpringOpaqueTokenIntrospector
at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:641)
at java.base/jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(ClassLoaders.java:188)
at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:520)
... 29 common frames omitted
Process finished with exit code 0
Зависимости проекта:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web-services</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.ws</groupId>
<artifactId>spring-ws-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-ldap</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-oauth2-resource-server</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-oauth2-jose</artifactId>
</dependency>
Вы хоть представляете, что не так с этой настройкой? заранее спасибо




Во-первых, если ваш сервер авторизации создает JWT (Keycloak делает), вам, вероятно, лучше использовать декодер JWT, чем самоанализ токена. Это намного эффективнее: все JWT проверяются с помощью открытого ключа, загружаемого только один раз, когда для самоанализа требуется, чтобы ресурс-сервер (Spring REST API) отправлял каждый токен доступа на сервер авторизации (Keycloak).
Во-вторых, дважды проверьте версию spring-security-oauth2-resource-server в вашем дереве зависимостей: SpringOpaqueTokenIntrospector есть только с 5.6.
В-третьих, я предложил более простой способ сопоставления полномочий для самоанализа токенов в версии 5.8: предоставьте преобразователь аутентификации (обработайте результат самоанализа) вместо перегрузки всего интроспектора:
http.oauth2ResourceServer().opaqueToken().authenticationConverter(authenticationConverter);
где authenticationConverter можно реализовать как:
(String introspectedToken, OAuth2AuthenticatedPrincipal authenticatedPrincipal) -> new BearerTokenAuthentication(
authenticatedPrincipal,
new OAuth2AccessToken(
OAuth2AccessToken.TokenType.BEARER,
introspectedToken,
authenticatedPrincipal.getAttribute(OAuth2TokenIntrospectionClaimNames.IAT),
authenticatedPrincipal.getAttribute(OAuth2TokenIntrospectionClaimNames.EXP)),
authoritiesConverter.convert(authenticatedPrincipal.getAttributes()));
а authoritiesConverter — это Converter<Map<String, Object>, Collection<? extends GrantedAuthority>> (сопоставляет авторитеты из ответа Keycloak самоанализа JSON, который вы уже должны были реализовать)
Наконец, вы можете найти полезные советы по настройке вашего сервера ресурсов в этом руководстве: это может быть так же просто, как
<dependency>
<groupId>com.c4-soft.springaddons</groupId>
<!-- change "webmvc" with "webflux" for reactive apps -->
<!-- change "jwt" with "introspecting" for access-token introspection instead of JWT decoding -->
<artifactId>spring-addons-webmvc-jwt-resource-server</artifactId>
<!-- 6.0.4 goes with spring-boot 3.0.0-RC1, use 5.x with boot 2.6.x and below -->
<version>6.0.4</version>
</dependency>
@EnableMethodSecurity
@Configuration
public class SecurityConfig { }
spring.security.oauth2.resourceserver.opaquetoken.introspection-uri=https://localhost:8443/realms/master/protocol/openid-connect/token/introspect
spring.security.oauth2.resourceserver.opaquetoken.client-id=spring-addons-confidential
spring.security.oauth2.resourceserver.opaquetoken.client-secret=change-me
com.c4-soft.springaddons.security.issuers[0].location=https://localhost:8443/realms/master
# shoud be configured with a list of private-claims this authorization-server puts user roles into
# below is default Keycloak conf for a `spring-addons` client with client roles mapper enabled
com.c4-soft.springaddons.security.issuers[0].authorities.claims=realm_access.roles,resource_access.spring-addons-public.roles,resource_access.spring-addons-confidential.roles
# Fine-grained CORS configuration can be set per path as follow:
com.c4-soft.springaddons.security.cors[0].path=/sample/**
# More useful resource-server security conf from preperties, use IDE auto-completion
Каково ваше намерение? Создание сервера Keycloak в приложении Spring Boot 2 или использование OAuth2 для защиты запросов к вашему приложению Spring Boot 2? Первое смотрите здесь , а второе смотрите там.