Apache Shiro 正在破坏我的 Spring Boot Rest API 的 CORS 配置

Posted

技术标签:

【中文标题】Apache Shiro 正在破坏我的 Spring Boot Rest API 的 CORS 配置【英文标题】:Apache Shiro is breaking the CORS configuration of my Spring Boot RestAPI 【发布时间】: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。谷歌搜索这个问题也没有产生任何有用的结果,所以我怀疑我错过了一些大的东西,或者更糟糕的是,这里有一些小而明显的东西。虽然我正在尝试解决这个问题,但非常感谢任何帮助或提示,谢谢!

【问题讨论】:

【参考方案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 注解,则请求将被传递。

【讨论】:

以上是关于Apache Shiro 正在破坏我的 Spring Boot Rest API 的 CORS 配置的主要内容,如果未能解决你的问题,请参考以下文章

无法在自定义 Apache Shiro AuthorizingRealm 中 @Inject 我的 DAO

apache shiro 使用 Hashing Credentials 无法成功登录

Apache Shiro 隐式 [url] 限制

Apache Shiro,isPermitted() 不工作

使用 Apache Shiro 的 Spring Boot

解决自定义Shiro.Realm扩展类不能用注解(@Resource或@Autowire)自动装配的问题