【问题标题】:Apache Shiro is breaking the CORS configuration of my Spring Boot RestAPIApache Shiro 正在破坏我的 Spring Boot Rest API 的 CORS 配置
【发布时间】:2020-07-02 08:05:08
【问题描述】:

在修改了不同的安全框架之后,我决定在我的 Spring Boot RestAPI 中使用 Apache Shiro,因为它似乎提供了必要的灵活性而没有太多的官僚开销。到目前为止,除了将 maven 依赖项添加到我的项目之外,我什么都没做:

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

这迫使我定义一个 Realm bean 以启动应用程序:

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

除了实现 Realm 接口之外,该 bean 几乎什么也没做:

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 注释的优雅方式,还是定义 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 来证明它)。我还注意到出现了三个 Shiro bean:

shiroEventBusAwareBeanPostProcessor
shiroFilterChainDefinition
shiroFilterFactoryBean

因此,我认为可能是 shiroFilterFactoryBean 以某种方式破坏了它,需要额外注意和配置。不幸的是,Apache Shiro 文档似乎没有说明跨域请求,我认为这不是(必然)Shiro 安全问题的一部分,而是底层的 Restful API,即 Spring。谷歌搜索这个问题也没有产生任何有用的结果,所以我怀疑我错过了一些大的东西,或者更糟糕的是,这里有一些小而明显的东西。虽然我正在尝试解决这个问题,但非常感谢任何帮助或提示,谢谢!

【问题讨论】:

    标签: spring-boot spring-mvc cors shiro


    【解决方案1】:

    噢,我想通了。我在 filter-servlet 领域已经有一段时间了,所以我没有想到过滤器的执行顺序。我这样做的天真方式,Shiro 过滤器链总是在我的自定义 CorsFilter 之前执行(显然也是 @CrossOrigin 注释的默认 Spring 处理器)。由于我还没有配置 Shiro,任何请求都将被拒绝,因为既没有经过身份验证也没有授权,因此从未执行 CorsFilter 导致没有 Access-Control-Allow-Origin 标头的响应。 p>

    所以,要么我正确配置 Shiro,要么只是确保在 Shiro 过滤器之前执行 CorsFilter,方法是使用 Spring 的 FilterRegistrationBean 像这样(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;
    }
    

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

    【讨论】:

      猜你喜欢
      • 2018-09-03
      • 2015-03-05
      • 2019-01-06
      • 2018-05-06
      • 2018-03-15
      • 2021-07-18
      • 2021-03-07
      • 2019-05-05
      • 2021-06-14
      相关资源
      最近更新 更多