В настоящее время я переношу наше приложение REST с Spring Boot 2.7.5 на 3.0.0-RC2. Я хочу, чтобы все было безопасно, кроме URL-адреса Open API. В Spring Boot 2.7.5 мы делали это:
@Named
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/openapi/openapi.yml").permitAll()
.anyRequest().authenticated()
.and()
.httpBasic();
}
}
и это работало нормально. В Spring Boot 3 мне пришлось изменить его на
@Configuration
@EnableWebSecurity
public class WebSecurityConfig {
@Bean
public SecurityFilterChain configure(HttpSecurity http) throws Exception {
http.authorizeHttpRequests((requests) -> requests
.requestMatchers("/openapi/openapi.yml").permitAll()
.anyRequest()
.authenticated())
.httpBasic();
return http.build();
}
}
поскольку WebSecurityConfigurerAdapter был удален. Однако это не работает. URL-адрес Open API также защищен базовой аутентификацией. Я сделал ошибку при обновлении кода или это, возможно, проблема в Spring Boot 3 RC 2?
Обновлять Поскольку большая часть нового API уже была доступна в версии 2.7.5, я обновил наш код в базе кода версии 2.7.5 следующим образом:
@Configuration
@EnableWebSecurity
public class WebSecurityConfig {
@Bean
public SecurityFilterChain configure(HttpSecurity http) throws Exception {
http
.csrf().disable()
.authorizeHttpRequests((requests) -> requests
.antMatchers(OPTIONS).permitAll() // allow CORS option calls for Swagger UI
.antMatchers("/openapi/openapi.yml").permitAll()
.anyRequest().authenticated())
.httpBasic();
return http.build();
}
}
В нашей ветке для 3.0.0-RC2 код теперь такой:
@Configuration
@EnableWebSecurity
public class WebSecurityConfig {
@Bean
public SecurityFilterChain configure(HttpSecurity http) throws Exception {
http
.csrf().disable()
.authorizeHttpRequests((requests) -> requests
.requestMatchers(OPTIONS).permitAll() // allow CORS option calls for Swagger UI
.requestMatchers("/openapi/openapi.yml").permitAll()
.anyRequest().authenticated())
.httpBasic();
return http.build();
}
}
Как видите, единственная разница в том, что я вызываю requestMatchers вместо antMatchers. Этот метод, кажется, был переименован. Метод antMatchers больше недоступен. Хотя конечный эффект все равно тот же. В нашей ветке для 3.0.0-RC2 Spring Boot запрашивает базовую аутентификацию для URL-адреса OpenAPI. Все еще отлично работает на 2.7.5.
У вас действительно есть обработчик (отображение контроллера) для "/openapi/openapi.yml"
? Если нет обработчика, разрешается не 404 NOT_FOUND
. Что, в свою очередь, перенаправляет на /error
. Поскольку /error
также защищен, он попросит вас войти в систему.
Да. Как только я ввожу учетные данные базовой аутентификации, отображается Open API.
Может быть матчером. Неужели нельзя сделать requests.antMatchers("/openapi/openapi.yml").permitAll()
?
Нет, я только что обновил вопрос. Метод antMatchers больше недоступен.
Обновление: вы должны использовать новую версию Springdoc. springdoc.org . Сосредоточиться на тексте Для поддержки spring-boot v3 убедитесь, что вы используете springdoc-openapi v2.
Официальная документация предлагает пример, который я сократил здесь с вашей конфигурацией:
http
.authorizeExchange((exchanges) ->
exchanges
.pathMatchers("/openapi/openapi.yml").permitAll()
.anyExchange().authenticated())
.httpBasic();
return http.build();
Вы можете попробовать это, поскольку он меняет «запрос» на формулировку «обмена» в соответствии с переходом на декларативные клиенты (@PostExchange vs. @PostMapping), я полагаю. Надеюсь, это поможет.
Нет, это не работает. HttpSecurity не имеет метода authorizeExchange. Ваш пример для WebFlux, который я не использую: docs.spring.io/spring-security/site/docs/current/api/org/…
Кажется, это ошибка в Spring Boot 3. Я поднял проблему.
Я думаю, что это не так, пожалуйста, проверьте метод securityMatcher() для HttpSecurity
Использовать
http.securityMatcher("<patterns>")...
указать аутентификацию для конечных точек.
authorizeHttpRequests((requests) -> requests
.requestMatchers("<pattern>")
работает только для авторизации, если не установить securityMatcher
, SecurityFilterChain по умолчанию получает any request
для аутентификации. И любой запрос будет аутентифицирован поставщиком аутентификации.
В вашем случае вы можете определить две цепочки фильтров безопасности: одну для общедоступных конечных точек, другую для защищенных. И дайте им правильный порядок:
@Bean
@Order(1)
public SecurityFilterChain configure(HttpSecurity http) throws Exception {
http.securityMatcher(OPTIONS,"/openapi/openapi.yml").csrf().disable()
.authorizeHttpRequests((requests) -> requests
.anyRequest().permitAll() // allow CORS option calls for Swagger UI
);
return http.build();
}
@Bean
Order(2)
public SecurityFilterChain configure(HttpSecurity http) throws Exception {
http.securityMatcher("/**")
.csrf().disable()
.authorizeHttpRequests((requests) -> requests.anyRequest().authenticated())
.httpBasic();
return http.build();
}
Это кажется излишне сложным. Я согласился с предложением Энди Уилкинсона (см. ответ Джеймса Грея)
Автор: https://github.com/wilkinsona
@Bean
public SecurityFilterChain configure(HttpSecurity http) throws Exception {
http
.authorizeHttpRequests((requests) -> requests
.requestMatchers(new AntPathRequestMatcher("/openapi/openapi.yml")).permitAll()
.anyRequest().authenticated())
.httpBasic();
return http.build();
}
Источник: https://github.com/spring-projects/spring-boot/issues/33357#issuecomment-1327301183
Я рекомендую вам прямо сейчас использовать Spring Boot 3.0.0 (GA), а не версию RC.
Внутри моего WebSecurityConfig я сделал это:
private static final String[] AUTH_WHITELIST = {
// -- Swagger UI v2
"/v2/api-docs",
"v2/api-docs",
"/swagger-resources",
"swagger-resources",
"/swagger-resources/**",
"swagger-resources/**",
"/configuration/ui",
"configuration/ui",
"/configuration/security",
"configuration/security",
"/swagger-ui.html",
"swagger-ui.html",
"webjars/**",
// -- Swagger UI v3
"/v3/api-docs/**",
"v3/api-docs/**",
"/swagger-ui/**",
"swagger-ui/**",
// CSA Controllers
"/csa/api/token",
// Actuators
"/actuator/**",
"/health/**"
};
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
return http
.csrf(AbstractHttpConfigurer::disable)
.authorizeHttpRequests( auth -> auth
.requestMatchers(AUTH_WHITELIST).permitAll()
.anyRequest().authenticated()
)
.sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
.httpBasic(withDefaults())
.addFilterBefore(authenticationJwtTokenFilter, UsernamePasswordAuthenticationFilter.class)
//.addFilterAfter(authenticationJwtTokenFilter, UsernamePasswordAuthenticationFilter.class)
.build();
}
@Bean
public SecurityFilterChain configure(HttpSecurity httpSecurity) throws Exception {
httpSecurity
.authorizeHttpRequests((requests) -> requests
.requestMatchers( new AntPathRequestMatcher("swagger-ui/**")).permitAll()
.requestMatchers( new AntPathRequestMatcher("/swagger-ui/**")).permitAll()
.requestMatchers( new AntPathRequestMatcher("v3/api-docs/**")).permitAll()
.requestMatchers( new AntPathRequestMatcher("/v3/api-docs/**")).permitAll()
.anyRequest().authenticated())
.httpBasic();
return httpSecurity.build();
}
Это и использование Dockerfile (выполнение mvn clean package
и запуск .jar из Docker) избавили меня от проблем с аутентификацией внутри пользовательского интерфейса swagger.
Надеюсь, это поможет вам :)
Мой конфиг безопасности выглядит так:
Spring 3.0.0
@Bean
public SecurityFilterChain configure(HttpSecurity http) throws Exception {
http
.csrf().disable()
.authorizeHttpRequests(requests -> requests
.requestMatchers(HttpMethod.GET, "/", "/static/**", "/index.html", "/api/users/me").permitAll()
.requestMatchers(HttpMethod.POST, "/api/users").permitAll()
.requestMatchers(HttpMethod.GET, "/api/users/login", "/api/users/{username}", "/api/users/logout", "/api/costumers", "/api/storages").authenticated()
.requestMatchers(HttpMethod.POST, "/api/costumers", "/api/storages").authenticated()
.requestMatchers(HttpMethod.PUT, "/api/costumers/{id}", "/api/storages/{id}").authenticated()
.requestMatchers(HttpMethod.DELETE, "/api/users/{id}", "/api/storages/{id}", "/api/costumers/{id}").authenticated()
.anyRequest().denyAll())
.httpBasic();
return http.build();
}
оно работает
Я должен, вероятно, упомянуть, что я использую Джерси. Может, это как-то связано?