Вопрос или проблема
Я пытаюсь настроить сервер авторизации с использованием spring-boot-starter-oauth2-authorization-server. Проблема в том, что когда я пытаюсь ввести свой логин и пароль, возникает ошибка “Не найден AuthenticationProvider”. Пожалуйста, помогите мне это исправить.
Класс SecurityConfig
@EnableWebSecurity
@Configuration(proxyBeanMethods = false)
public class SecurityConfig {
@Bean
public SecurityFilterChain defaultSecurityFilterChain(HttpSecurity http) throws Exception {
http.authorizeHttpRequests(authorize ->
authorize.anyRequest().authenticated())
.csrf(AbstractHttpConfigurer::disable);
return http.formLogin(Customizer.withDefaults()).build();
}
public UserDetailsService userDetailsService() {
UserDetails user = User.builder()
.username("admin")
.password("{noop}password")
.roles("USER")
.build();
return new InMemoryUserDetailsManager(user);
}
}
Класс AuthorizationServerConfig
@Configuration
@RequiredArgsConstructor
public class AuthorizationServerConfig {
private final OAuth2AuthorizationServerProperties authorizationServerProperties;
@Bean
@Order(Ordered.HIGHEST_PRECEDENCE)
public SecurityFilterChain authServerSecurityFilterChain(HttpSecurity http) throws Exception {
OAuth2AuthorizationServerConfiguration.applyDefaultSecurity(http);
http.exceptionHandling(exception ->
exception.authenticationEntryPoint(
new LoginUrlAuthenticationEntryPoint("/login")));
return http.build();
}
public RegisteredClientRepository registeredClientRepository() {
return new InMemoryRegisteredClientRepository(
RegisteredClient.withId("test-client-id")
.clientName("Test Client")
.clientId("test-client")
.clientSecret("{noop}test-client")
.redirectUri("http://localhost:5000/code")
.clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_BASIC)
.authorizationGrantType(AuthorizationGrantType.CLIENT_CREDENTIALS)
.authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE)
.authorizationGrantType(AuthorizationGrantType.REFRESH_TOKEN)
.build()
);
}
@Bean
public JWKSource<SecurityContext> jwkSource() {
KeyPair keyPair = generateRsaKey();
RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic();
RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate();
RSAKey rsaKey = new RSAKey.Builder(publicKey)
.privateKey(privateKey)
.keyID(UUID.randomUUID().toString())
.build();
JWKSet jwkSet = new JWKSet(rsaKey);
return ((jwkSelector, securityContext) -> jwkSelector.select(jwkSet));
}
private static KeyPair generateRsaKey() {
KeyPair keyPair;
try {
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
keyPairGenerator.initialize(2048);
keyPair = keyPairGenerator.generateKeyPair();
} catch (Exception ex) {
throw new IllegalStateException(ex);
}
return keyPair;
}
@Bean
public JwtDecoder jwtDecoder(JWKSource<SecurityContext> jwkSource) {
return OAuth2AuthorizationServerConfiguration.jwtDecoder(jwkSource);
}
@Bean
public AuthorizationServerSettings authorizationServerSettings() {
return AuthorizationServerSettings.builder()
.issuer(authorizationServerProperties.getIssuer())
.build();
}
}
application.yaml
server:
port: 7777
logging:
level:
root: DEBUG
org.apache.tomcat.util.net.NioEndpoint: ERROR
sun.rmi: ERROR
java.io: ERROR
javax.management: ERROR
spring:
application:
name: j-sso
security:
oauth2:
authorizationserver:
issuer-url: http://localhost:7777
логи трассировки
2024-11-13T01:03:51.717+03:00 TRACE 3596 --- [j-sso] [nio-7777-exec-6] o.s.s.w.s.HttpSessionRequestCache : Запрос не был сохранен, так как он не соответствует [And [Ant [pattern='/**', GET], Not [Ant [pattern='/**/favicon.*']], Not [MediaTypeRequestMatcher [contentNegotiationStrategy=org.springframework.web.accept.ContentNegotiationManager@1858a268, matchingMediaTypes=[application/json], useEquals=false, ignoredMediaTypes=[*/*]]], Not [RequestHeaderRequestMatcher [expectedHeaderName=X-Requested-With, expectedHeaderValue=XMLHttpRequest]], Not [MediaTypeRequestMatcher [contentNegotiationStrategy=org.springframework.web.accept.ContentNegotiationManager@1858a268, matchingMediaTypes=[multipart/form-data], useEquals=false, ignoredMediaTypes=[*/*]]], Not [MediaTypeRequestMatcher [contentNegotiationStrategy=org.springframework.web.accept.ContentNegotiationManager@1858a268, matchingMediaTypes=[text/event-stream], useEquals=false, ignoredMediaTypes=[*/*]]]]]
2024-11-13T01:03:51.717+03:00 DEBUG 3596 --- [j-sso] [nio-7777-exec-6] s.w.a.DelegatingAuthenticationEntryPoint : Пытаемся выполнить сопоставление, используя And [Not [RequestHeaderRequestMatcher [expectedHeaderName=X-Requested-With, expectedHeaderValue=XMLHttpRequest]], MediaTypeRequestMatcher [contentNegotiationStrategy=org.springframework.web.accept.ContentNegotiationManager@1858a268, matchingMediaTypes=[application/xhtml+xml, image/*, text/html, text/plain], useEquals=false, ignoredMediaTypes=[*/*]]]
2024-11-13T01:03:51.718+03:00 DEBUG 3596 --- [j-sso] [nio-7777-exec-6] s.w.a.DelegatingAuthenticationEntryPoint : Совпадение найдено! Выполнение org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint@2fd72332
2024-11-13T01:03:51.718+03:00 DEBUG 3596 --- [j-sso] [nio-7777-exec-6] o.s.s.web.DefaultRedirectStrategy : Перенаправление на http://localhost:7777/login
2024-11-13T01:03:51.718+03:00 TRACE 3596 --- [j-sso] [nio-7777-exec-6] o.s.s.w.header.writers.HstsHeaderWriter : Заголовок HSTS не вставляется, так как он не соответствует запросу [Является безопасным]
2024-11-13T01:03:51.732+03:00 TRACE 3596 --- [j-sso] [nio-7777-exec-7] o.s.security.web.FilterChainProxy : Пытаемся сопоставить запрос с DefaultSecurityFilterChain [RequestMatcher=any request, Filters=[org.springframework.security.web.session.DisableEncodeUrlFilter@9fdc290, org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter@1fc0cdb4, org.springframework.security.web.context.SecurityContextHolderFilter@7793ad58, org.springframework.security.web.header.HeaderWriterFilter@62765aec, org.springframework.security.web.csrf.CsrfFilter@5dfe23e8, org.springframework.security.web.authentication.logout.LogoutFilter@5af64ce0, org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter@49f3ff41, org.springframework.security.web.authentication.ui.DefaultLoginPageGeneratingFilter@4207852d, org.springframework.security.web.authentication.ui.DefaultLogoutPageGeneratingFilter@733e2d75, org.springframework.security.web.authentication.www.BasicAuthenticationFilter@4f2d014a, org.springframework.security.web.savedrequest.RequestCacheAwareFilter@16132f21, org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter@4640195a, org.springframework.security.web.authentication.AnonymousAuthenticationFilter@2a4db2b5, org.springframework.security.web.access.ExceptionTranslationFilter@50b0afd7, org.springframework.security.web.access.intercept.AuthorizationFilter@56846330]] (1/1)
2024-11-13T01:03:51.733+03:00 DEBUG 3596 --- [j-sso] [nio-7777-exec-7] o.s.security.web.FilterChainProxy : Защита GET /login
2024-11-13T01:03:51.733+03:00 TRACE 3596 --- [j-sso] [nio-7777-exec-7] o.s.security.web.FilterChainProxy : Вызов DisableEncodeUrlFilter (1/15)
2024-11-13T01:03:51.733+03:00 TRACE 3596 --- [j-sso] [nio-7777-exec-7] o.s.security.web.FilterChainProxy : Вызов WebAsyncManagerIntegrationFilter (2/15)
2024-11-13T01:03:51.733+03:00 TRACE 3596 --- [j-sso] [nio-7777-exec-7] o.s.security.web.FilterChainProxy : Вызов SecurityContextHolderFilter (3/15)
2024-11-13T01:03:51.733+03:00 TRACE 3596 --- [j-sso] [nio-7777-exec-7] o.s.security.web.FilterChainProxy : Вызов HeaderWriterFilter (4/15)
2024-11-13T01:03:51.733+03:00 TRACE 3596 --- [j-sso] [nio-7777-exec-7] o.s.security.web.FilterChainProxy : Вызов CsrfFilter (5/15)
2024-11-13T01:03:51.733+03:00 TRACE 3596 --- [j-sso] [nio-7777-exec-7] o.s.security.web.csrf.CsrfFilter : Защита от CSRF не была осуществлена, так как запрос не соответствовал CsrfNotRequired [TRACE, HEAD, GET, OPTIONS]
2024-11-13T01:03:51.733+03:00 TRACE 3596 --- [j-sso] [nio-7777-exec-7] o.s.security.web.FilterChainProxy : Вызов LogoutFilter (6/15)
2024-11-13T01:03:51.733+03:00 TRACE 3596 --- [j-sso] [nio-7777-exec-7] o.s.s.w.a.logout.LogoutFilter : Запрос не соответствует Ant [pattern='/logout', POST]
2024-11-13T01:03:51.733+03:00 TRACE 3596 --- [j-sso] [nio-7777-exec-7] o.s.security.web.FilterChainProxy : Вызов UsernamePasswordAuthenticationFilter (7/15)
2024-11-13T01:03:51.733+03:00 TRACE 3596 --- [j-sso] [nio-7777-exec-7] w.a.UsernamePasswordAuthenticationFilter : Запрос не соответствует Ant [pattern='/login', POST]
2024-11-13T01:03:51.734+03:00 TRACE 3596 --- [j-sso] [nio-7777-exec-7] o.s.security.web.FilterChainProxy : Вызов DefaultLoginPageGeneratingFilter (8/15)
2024-11-13T01:03:51.734+03:00 TRACE 3596 --- [j-sso] [nio-7777-exec-7] o.s.s.w.header.writers.HstsHeaderWriter : Заголовок HSTS не вставляется, так как он не соответствует запросу [Является безопасным]
Ошибка
вставьте описание изображения здесь
Я пытался настроить по примеру https://wankhedeshubham.medium.com/spring-boot-security-with-userdetailsservice-and-custom-authentication-provider-3df3a188993f, но это мне не помогло.
Ответ или решение
Решение проблемы "No AuthenticationProvider found" в Spring Authorization Server
При настройке Spring Authorization Server могут возникнуть различные ошибки, одной из которых является ошибка "No AuthenticationProvider found". Эта проблема обычно связана с конфигурацией аутентификации и управления пользователями. В этом ответе мы рассмотрим основные шаги, чтобы помочь вам решить эту проблему, основываясь на предоставленной вами информации.
Анализ кода и конфигурации
Ваш код состоит из двух основных классов конфигурации: SecurityConfig
и AuthorizationServerConfig
. Давайте рассмотрим каждый из них и поймем, где могут быть проблемы:
1. Класс SecurityConfig
@EnableWebSecurity
@Configuration(proxyBeanMethods = false)
public class SecurityConfig {
@Bean
public SecurityFilterChain defaultSecurityFilterChain(HttpSecurity http) throws Exception {
http.authorizeHttpRequests(authorize ->
authorize.anyRequest().authenticated())
.csrf(AbstractHttpConfigurer::disable);
return http.formLogin(Customizer.withDefaults()).build();
}
public UserDetailsService userDetailsService() {
UserDetails user = User.builder()
.username("admin")
.password("{noop}password")
.roles("USER")
.build();
return new InMemoryUserDetailsManager(user);
}
}
В этом месте вы создали UserDetailsService
, который предоставляет административного пользователя в памяти. Однако в вашем фильтре безопасности отсутствует явная связь между UserDetailsService
и AuthenticationProvider
, что приводит к ошибке "No AuthenticationProvider found".
2. Класс AuthorizationServerConfig
@Configuration
@RequiredArgsConstructor
public class AuthorizationServerConfig {
private final OAuth2AuthorizationServerProperties authorizationServerProperties;
@Bean
@Order(Ordered.HIGHEST_PRECEDENCE)
public SecurityFilterChain authServerSecurityFilterChain(HttpSecurity http) throws Exception {
OAuth2AuthorizationServerConfiguration.applyDefaultSecurity(http);
http.exceptionHandling(exception ->
exception.authenticationEntryPoint(
new LoginUrlAuthenticationEntryPoint("/login")));
return http.build();
}
// Другие методы конфигурации
}
Здесь вы настраиваете сервер авторизации, но не указываете UserDetailsService
для обработки аутентификации. Это означает, что аутентификация будет искать подходящий AuthenticationProvider
, но не сможет его найти, так как он не был явно определён.
Решение проблемы
Для решения вашей проблемы следует внести несколько изменений в класс SecurityConfig
. Вам необходимо зарегистрировать UserDetailsService
как бини, а также явно определить AuthenticationProvider
, чтобы обеспечить правильное взаимодействие между вашими конфигурациями. Добавьте метод для создания AuthenticationProvider
, который будет использовать ваш UserDetailsService
:
@Bean
public AuthenticationProvider authenticationProvider() {
DaoAuthenticationProvider provider = new DaoAuthenticationProvider();
provider.setUserDetailsService(userDetailsService());
provider.setPasswordEncoder(passwordEncoder()); // Не забудьте добавить кодировщик паролей
return provider;
}
@Bean
public PasswordEncoder passwordEncoder() {
return new NoOpPasswordEncoder(); // Используйте стартовый (noop) для простоты
}
Затем измените вашу конфигурацию SecurityFilterChain
в классе SecurityConfig
, чтобы добавить указание на использование этого AuthenticationProvider
:
@Bean
public SecurityFilterChain defaultSecurityFilterChain(HttpSecurity http) throws Exception {
http.authorizeHttpRequests(authorize ->
authorize.anyRequest().authenticated())
.and()
.authenticationProvider(authenticationProvider()) // Добавьте это
.csrf(AbstractHttpConfigurer::disable);
return http.formLogin(Customizer.withDefaults()).build();
}
Проверьте ваше приложение
После внесения изменений в конфигурацию, перезапустите ваше приложение и попробуйте снова выполнить вход. Убедитесь, что вы видите корректные журналы и что аутентификация происходит без ошибок.
Заключение
Ошибка "No AuthenticationProvider found" часто возникает из-за недостаточной интеграции различных компонентов аутентификации в Spring Security. Убедившись, что ваши классы конфигурации правильно связаны друг с другом, вы сможете устранить эту ошибку и успешно настроить свой сервер авторизации. Если у вас есть дополнительные вопросы или проблемы, не стесняйтесь обратиться за помощью.