Apache Широ ломает конфигурацию CORS моего Spring Boot RestAPI - PullRequest
0 голосов
/ 21 марта 2020

После работы с различными структурами безопасности я решил go с Apache Shiro для моего Spring Boot RestAPI, потому что он, кажется, предлагает необходимую гибкость без слишком больших бюрократических c накладных расходов. До сих пор я ничего не делал, кроме добавления зависимости maven в свой проект:

<dependency>
  <groupId>org.apache.shiro</groupId>
  <artifactId>shiro-spring-boot-web-starter</artifactId>
  <version>1.5.1</version>
</dependency>

Это заставило меня определить bean-компонент Realm для запуска приложения:

@Bean
public Realm realm() {
    return new TMTRealm();
}

Бин почти ничего не делает, кроме реализации интерфейса Realm:

public class TMTRealm implements Realm {

private static final String Realm_Name = "realm_name";

@Override
public String getName() {
    return Realm_Name;
}

@Override
public boolean supports(AuthenticationToken token) {
    return false;
}

@Override
public AuthenticationInfo getAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
    return null;
}

}

Пока все хорошо. За исключением того, что теперь мой RestAPI нарушает политику CORS, не добавляя заголовок «Access-Control-Allow-Origin» в любой его ответов. Я заметил, что Chrome не отправляет какой-либо выделенный запрос OPTIONS, но два запроса одного и того же метода, в данном случае GET, с ошибкой первого из них выглядит следующим образом:

Access to XMLHttpRequest at 'http://localhost:8081/geo/country/names?typed=D&lang=en-US' from origin 'http://localhost:4200' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.

Без Shiro его представляет работает отлично, как элегантный способ использования аннотации Spring @CrossOrigin на контроллере, так и грубый метод старой школы определения bean-компонента CorsFilter:

@Bean
public CorsFilter corsFilter() {
    final UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
    final CorsConfiguration config = new CorsConfiguration();
    config.setAllowCredentials(true);
    config.setAllowedOrigins(Arrays.asList("*"));
    config.setAllowedHeaders(Arrays.asList("*"));
    config.setAllowedMethods(Arrays.asList("OPTIONS", "GET", "POST", "PUT", "DELETE"));
    source.registerCorsConfiguration("/**", config);
    return new CorsFilter(source);
}

Я реализовал интерфейс SpringL ApplicationListener для перехвата в то время, когда ApplicationContext запускается и может видеть, что bean-компонент corsFilter зарегистрирован и присутствует:

@Override
public void onApplicationEvent(ContextRefreshedEvent event) {
    System.out.println("############ - application context started with beans: ");
    final String[] beans = event.getApplicationContext().getBeanDefinitionNames();
    Arrays.parallelSort(beans);
    for (final String bean : beans) {
        System.out.println(bean);
    }
}

Вывод:

...
conditionEvaluationDeltaLoggingListener
conventionErrorViewResolver
corsFilter
countryController
...

Но фильтр никогда не вызывается при любом запросе (я установил точку останова и System.out, чтобы доказать это). Я также заметил, что присутствуют три боба Широ:

shiroEventBusAwareBeanPostProcessor
shiroFilterChainDefinition
shiroFilterFactoryBean

Поэтому я предполагаю, что, вероятно, shiroFilterFactoryBean как-то ломает его и требует дополнительного внимания и настройки. К сожалению, документация Apache Shiro, похоже, ничего не говорит о запросах между источниками, и я предполагаю, что это (не обязательно) часть проблем безопасности Shiro, а скорее базовый Restful API, то есть Spring. Поиск в Google не принес никаких полезных результатов, поэтому я подозреваю, что я упускаю что-то большое или, что еще хуже, что-то маленькое и очевидное. Пока я пытаюсь это выяснить, любая помощь или подсказка очень ценится, спасибо!

1 Ответ

1 голос
/ 21 марта 2020

Awight, я понял это. Некоторое время я был на земле фильтров-сервлетов, поэтому я не думал о порядке, в котором выполняются фильтры. Наивный способ, которым я это сделал, цепочка фильтров Shiro всегда выполнялась до моего пользовательского CorsFilter (и, по-видимому, также процессор Spring по умолчанию для аннотации @CrossOrigin). Поскольку я еще не настроил Shiro, любой запрос будет отклонен как ни аутентифицированный, ни авторизованный, и поэтому CorsFilter никогда не выполнялся, вызывая ответ без заголовка Access-Control-Allow-Origin .

Итак, я либо настраиваю Shiro должным образом, либо просто проверяю, чтобы CorsFilter выполнялся до фильтра Shiro, используя SpringRekistrationBean, например так (setOrder равен нулю):

@Configuration
public class RestApiConfig {

@Bean
public FilterRegistrationBean<CorsFilter> corsFilterRegistrationBean() {
    final FilterRegistrationBean<CorsFilter> registration = new FilterRegistrationBean<>(this.corsFilter());
    registration.setOrder(0);
    return registration;
}

private CorsFilter corsFilter() {
    final UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
    final CorsConfiguration config = new CorsConfiguration();
    config.setAllowCredentials(true);
    config.setAllowedOrigins(Arrays.asList("*"));
    config.setAllowedHeaders(Arrays.asList("*"));
    config.setAllowedMethods(Arrays.asList("OPTIONS", "GET", "POST", "PUT", "DELETE"));
    source.registerCorsConfiguration("/**", config);
    return new CorsFilter(source);
}

@Bean
public Realm realm() {
    return new TMTRealm();
}

}

Редактировать:

Да, мне было слишком легко это заметить. Все, что вам нужно сделать, это настроить ShiroFilterChainDefinition bean-компонент, чтобы он ссылался на аннотации классов или методов контроллера, например:

@Bean
public ShiroFilterChainDefinition shiroFilterChainDefinition() {
    final DefaultShiroFilterChainDefinition chainDefinition = new DefaultShiroFilterChainDefinition();
    chainDefinition.addPathDefinition("/**", "anon");
    return chainDefinition;
}

Так же, как это описано в документация . Теперь он работает как с компонентом CorsFilter, так и с аннотацией Spring @CrossOrigin. Если в методе контроллера нет аннотации Shiro, запрос будет пропущен.

...