Spring boot ApacheShiro正在破坏我的Spring Boot RestAPI的CORS配置

Spring boot ApacheShiro正在破坏我的Spring Boot RestAPI的CORS配置,spring-boot,spring-mvc,cors,shiro,Spring Boot,Spring Mvc,Cors,Shiro,在修补了不同的安全框架之后,我决定使用ApacheShiro来完成我的Spring Boot RestAPI,因为它似乎提供了必要的灵活性,而且没有太多的官僚开销。到目前为止,除了将maven依赖项添加到我的项目中,我没有做任何事情: <dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-spring-boot-web-starter</artifactI

在修补了不同的安全框架之后,我决定使用ApacheShiro来完成我的Spring Boot RestAPI,因为它似乎提供了必要的灵活性,而且没有太多的官僚开销。到目前为止,除了将maven依赖项添加到我的项目中,我没有做任何事情:

<dependency>
  <groupId>org.apache.shiro</groupId>
  <artifactId>shiro-spring-boot-web-starter</artifactId>
  <version>1.5.1</version>
</dependency>
bean除了实现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不会发送任何专用选项请求,但会发送两个相同方法的请求,在本例中为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 present的情况下,它工作得非常好,既有在控制器上使用Spring的@CrossOrigin注释的优雅方式,也有定义CorsFilter bean的蛮力“老派”方式:

@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);
}
我已经实现了Spring的ApplicationListener接口,以便在ApplicationContext启动时挂钩,因此可以看到corsFilter bean已注册并存在:

@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可能以某种方式破坏了它,需要额外的注意和配置。不幸的是,ApacheShiro文档似乎没有提到任何关于跨源请求的内容,我认为这(不一定)是Shiro安全问题的一部分,而是底层Restful API的一部分,即Spring。谷歌搜索这个问题也没有产生任何有用的结果,所以我怀疑我遗漏了一些大的东西,或者更糟糕的是,一些小而明显的东西。当我试图弄明白这一点的时候,任何帮助或暗示都非常感谢,谢谢

好吧,我想起来了。我在过滤器servlet领域已经有一段时间了,所以我没有考虑过滤器的执行顺序。我这样做很天真,Shiro过滤器链总是在我的定制CorsFilter(显然也是@CrossOrigin annotation的默认Spring处理器)之前执行。因为我还没有配置Shiro,所以任何请求都会被拒绝,因为既没有经过身份验证也没有经过授权,因此CorsFilter从未执行过,从而导致响应没有访问控制Allow Origin头

因此,我要么正确配置Shiro,要么使用Spring的FilterRegistrationBean确保在Shiro过滤器之前执行CorsFilter,如下所示(将顺序设置为零):


就像书中描述的那样。现在它可以与CorsFilter bean或Spring的@CrossOrigin注释一起使用。如果控制器方法上没有Shiro注释,请求将被传递。

A好的,我已经解决了。我在过滤器servlet领域已经有一段时间了,所以我没有考虑过滤器的执行顺序。我这样做很天真,Shiro过滤器链总是在我的定制CorsFilter(显然也是@CrossOrigin annotation的默认Spring处理器)之前执行。因为我还没有配置Shiro,所以任何请求都会被拒绝,因为既没有经过身份验证也没有经过授权,因此CorsFilter从未执行过,从而导致响应没有访问控制Allow Origin头

因此,我要么正确配置Shiro,要么使用Spring的FilterRegistrationBean确保在Shiro过滤器之前执行CorsFilter,如下所示(将顺序设置为零):

就像书中描述的那样。现在它可以与CorsFilter bean或Spring的@CrossOrigin注释一起使用。如果控制器方法上没有Shiro注释,则请求将被传递

shiroEventBusAwareBeanPostProcessor
shiroFilterChainDefinition
shiroFilterFactoryBean
@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();
}
@Bean
public ShiroFilterChainDefinition shiroFilterChainDefinition() {
    final DefaultShiroFilterChainDefinition chainDefinition = new DefaultShiroFilterChainDefinition();
    chainDefinition.addPathDefinition("/**", "anon");
    return chainDefinition;
}