#spring-security #spring-security-oauth2
Вопрос:
Я реализовал код, как указано здесь, чтобы добавить функцию множественной аренды по эмитенту в мою конфигурацию Spring Security. Однако при запуске моего приложения Spring Boot я сталкиваюсь со следующей ошибкой:
2021-10-26 | 10:31:37.762 | main | WARN | ConfigServletWebServerApplicationContext | Trace: | Exception encountered during context initialization - cancelling refresh attempt: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'springSecurityFilterChain' defined in class path resource [org/springframework/security/config/annotation/web/configuration/WebSecurityConfiguration.class]: Bean instantiation via factory method failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [javax.servlet.Filter]: Factory method 'springSecurityFilterChain' threw exception; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'org.springframework.security.oauth2.jwt.JwtDecoder' available
2021-10-26 | 10:31:39.361 | main | ERROR | o.s.b.d.LoggingFailureAnalysisReporter | Trace: |
***************************
APPLICATION FAILED TO START
***************************
Description:
Method springSecurityFilterChain in org.springframework.security.config.annotation.web.configuration.WebSecurityConfiguration required a bean of type 'org.springframework.security.oauth2.jwt.JwtDecoder' that could not be found.
Action:
Consider defining a bean of type 'org.springframework.security.oauth2.jwt.JwtDecoder' in your configuration.
В документации указано:
Это хорошо, потому что конечные точки эмитента загружаются лениво. Фактически, экземпляр соответствующего JwtAuthenticationProvider создается только при отправке первого запроса с соответствующим эмитентом.
Я бы не подумал, что при запуске приложения JwtDecoder
ожидается, что экземпляр a будет уже создан в соответствии с этой документацией. Чего мне не хватает в моей конфигурации?
Обновить
После помощи Стива Ризенберга у меня теперь компилируется следующий код. Вы можете видеть в моем фрагменте кода, что у меня раньше работало (т. Е. До того, Как у нас было требование многопользовательского доступа), теперь закомментировано:
//.jwt().jwtAuthenticationConverter(jwtAccessTokenConverter);
String[] issuers = new String[] {"https://www.example.com/auth/realms/example"};
JwtIssuerAuthenticationManagerResolver jwtIssuerAuthenticationManagerResolver =
new JwtIssuerAuthenticationManagerResolver(issuers);
...
.anyRequest()
.authenticated()
.and()
.oauth2ResourceServer(
oauth2ResourceServerConfigurer ->
oauth2ResourceServerConfigurer
.authenticationManagerResolver(jwtIssuerAuthenticationManagerResolver)
.authenticationEntryPoint(authenticationExceptionHandler));
// .jwt().jwtAuthenticationConverter(jwtAccessTokenConverter);
Однако, не имея возможности теперь предоставить мой собственный конвертер токенов, поскольку мне пришлось удалить .jwt()
, мне все еще неясно, что предоставляет мне конвертер по умолчанию.
Кроме того, я не понимаю, зачем мне нужно использовать третий конструктор JwtIssuerAuthenticationManagerResolver
и предоставлять свой собственный AuthenticationManagerResolver<String>
? Если мой приведенный выше код компилируется, зачем мне это делать?
Ответ №1:
Требуется JwtDecoder
, если вы настроили сервер ресурсов с помощью a JwtAuthenticationProvider
(потому что для этого требуется определенный JwtDecoder
). Это произойдет, если вы, например,:
http
...
.oauth2ResourceServer(oauth2 -> oauth2
.authenticationManagerResolver(authenticationManagerResolver)
.jwt(Customizer.withDefaults())
)
Поскольку authenticationManagerResolver
это альтернатива, которая разветвляется на AuthenticationManager
уровне, вы не хотите использовать JwtAuthenticationProvider
. Он будет использоваться внутри JwtIssuerAuthenticationManagerResolver
.
Удалите .jwt()
в этом случае, чтобы конфигуратор не подключал его.
Обновить
В разделе документов о динамических арендаторах содержится дополнительная информация о различных параметрах настройки.
В вашем случае без использования .jwt()
вы не можете так же легко подключить a JwtAuthenticationConverter
, который может настраивать возвращаемые предоставленные полномочия.
Внутри JwtIssuerAuthenticationManagerResolver
используется a TrustedIssuerJwtAuthenticationManagerResolver
. Это то, что выполняет функцию множественной аренды, извлекая issuer
утверждение из JWT и создавая JwtDecoder
new JwtAuthenticationProvider(jwtDecoder)
на основе сопоставленного issuer
.
Чтобы настроить JwtAuthenticationProvider
, вам придется повторно реализовать этот класс, чтобы вы могли вводить свой JwtAuthenticationConverter
в каждый созданный экземпляр. AuthenticationManagerResolver<String>
Для этого необходимо выполнить реализацию. Вызовите его CustomTrustedIssuerJwtAuthenticationManagerResolver
(см. Эту строку).
Вам просто нужно предоставить это JwtIssuerAuthenticationManagerResolver
, например:
String[] issuers = new String[] {"https://www.example.com/auth/realms/example"};
AuthenticationManagerResolver<String> authenticationManagerResolver =
new CustomTrustedIssuerJwtAuthenticationManagerResolver(issuers);
JwtIssuerAuthenticationManagerResolver jwtIssuerAuthenticationManagerResolver =
new JwtIssuerAuthenticationManagerResolver(authenticationManagerResolver);
...
Комментарии:
1. В настоящее время моя конфигурация содержит
.jwt().jwtAuthenticationConverter(jwtAccessTokenConverter)
. Этот конвертер представляет собой компонент, который реализуетConverter<Jwt, AbstractAuthenticationToken>
. Его задача состоит в том, чтобы извлечь основные и предоставленные полномочия из Jwt иreturn new JwtAuthenticationToken(jwt, authorities, principalName)
. Если я удалю.jwt()
, как вы предлагаете, то где я могу подключить этот компонент в моей конфигурации? В настоящее времяjwtAuthenticationConverter
метод может быть вызван только непосредственно после.jwt()
метода.2. Чтобы дать вам представление о том, почему я настроил этот преобразователь, я затем использую
JwtAuthenticationToken
для выполнения таких действий, как авторизация вызова конечной точки контроллера REST, используя@PreAuthorize
разработанный мной пользовательский код, который оценивает предоставленные полномочия, содержащиеся вJwtAuthenticationToken
.3. Итак, часть, которую я упускаю, заключается в том, что при подходе с использованием
JwtIssuerAuthenticationManagerResolver
, как я могу получить доступ к экземпляруJwtAuthenticationToken
, и где и как этот экземпляр настроен? Я надеюсь, что мое объяснение того, что я сейчас делаю и почему, понятно.4. Мне нужно было бы проверить, работает ли он с 5.3 (если это версия, которую вы используете, по ссылке в документах), но вы можете попробовать добавить
@Bean public JwtAuthenticationConverter jwtAuthenticationConverter() { ... }
.5. Мы используем Spring Cloud 2020.0.3 и Spring Boot 2.5.6. Итак, транзитивно через этот импорт pom мы используем Spring Security 5.5.3.