---
url: /blog/lkzwru5c/index.md
---
## 问题背景

```java
org.apache.shiro.UnavailableSecurityManagerException: No SecurityManager accessible to the calling code, either bound to the org.apache.shiro.util.ThreadContext or as a vm static singleton.  This is an invalid application configuration.
```

堆栈异常如上：

## 解决思路

这个报错的原因花了一段时间查找，有如下几种可能：

`org.apache.shiro.UnavailableSecurityManagerException` 异常通常表示尝试获取 `SecurityManager` 时失败了，因为 `SecurityManager` 没有被正确地初始化或配置。这个异常可能由以下几个原因引起：

1. **ShiroFilterFactoryBean 配置问题**：如果你在使用 Spring 集成 Shiro 时遇到了这个问题，可能是因为 `ShiroFilterFactoryBean` 没有被正确配置。确保你已经将 `ShiroFilterFactoryBean` 设置为一个 `@Bean`，并在其内部设置了 `SecurityManager`。例如：

```java
@Bean
public ShiroFilterFactoryBean shiroFilter(DefaultWebSecurityManager securityManager) {
    ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
    shiroFilterFactoryBean.setSecurityManager(securityManager);
    // 其他配置...
    return shiroFilterFactoryBean;
}
```

2. **DelegatingFilterProxy 配置问题**：在 Spring Boot 应用中，如果你使用了 `DelegatingFilterProxy` 来代理 Shiro 的过滤器，确保你已经正确配置了这个代理。例如：

```java
@Bean
public FilterRegistrationBean delegatingFilterProxy() {
    FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean();
    DelegatingFilterProxy proxy = new DelegatingFilterProxy();
    proxy.setTargetFilterLifecycle(true);
    proxy.setTargetBeanName("shiroFilter"); // 注意这里的 bean 名称要与 shiroFilterFactoryBean 的名称一致
    filterRegistrationBean.setFilter(proxy);
    return filterRegistrationBean;
}
```

3. **SecurityManager 未注入**：确保 `SecurityManager` 已经被注入到你的应用上下文中，并且可以通过 `SecurityUtils` 获取到。如果没有注入，你需要配置一个 `SecurityManager` 的 bean，并确保它被 `ShiroFilterFactoryBean` 使用。

4. **Filter 执行顺序问题**：如果你定义了自定义的过滤器，并且这些过滤器在 Shiro 的 `ShiroFilterFactoryBean` 之前执行，可能会导致 `SecurityManager` 未被正确设置。确保自定义过滤器是在 Shiro 过滤器之后执行的。

5. **Spring Boot 集成问题**：如果你在使用 Spring Boot 集成 Shiro，可能需要额外注意配置的细节。例如，确保你的 `ShiroFilterFactoryBean` bean 名称与 `DelegatingFilterProxy` 中指定的名称一致。

6. **确保 Shiro 配置类被 Spring 管理**：如果你的 Shiro 配置类没有被 Spring 管理，那么 `SecurityManager` 可能不会被正确初始化。确保你的配置类上有 `@Configuration` 注解。

如果问题依然存在，建议检查 Shiro 配置类中的所有 `@Bean` 方法，确保它们都正确配置，并且没有遗漏任何必要的设置。同时，检查应用程序的日志，查看是否有其他相关的错误信息，这可能会提供更多关于问题的线索。

> 也看看有没有配漏@Bean 疏忽导致。

## 解决方案

最终找到一个方案解决：

`DelegatingFilterProxy` 是 Spring 框架提供的一个非常有用的类，它的作用是将 Spring 容器管理的 Filter 代理为一个 Servlet Filter，这样就可以利用 Spring 的依赖注入和生命周期管理功能。这个代理类允许 Filter 实例享受 Spring 的依赖注入，并且可以通过 Spring 来管理 Filter 的生命周期 。

在 Spring Boot 应用中，如果你使用了 `Filter`，并且希望这些 `Filter` 能够注入 Spring 容器中的其他组件，你可以通过 `DelegatingFilterProxy` 来实现。这样，你就可以在 `Filter` 中注入业务工具类或其他 Spring 管理的 Bean，而不会出现注入为 `null` 的问题 。

关于 `ShiroFilterFactoryBean` 的配置，它是用来配置 Shiro 过滤器链的入口点。通过这个 Bean，你可以设置安全管理器、登录 URL、权限不足时的跳转 URL 等。同时，你还可以自定义过滤器，并将其加入到 Shiro 的过滤器链中 。

在使用 Shiro 时，如果你遇到了 `No SecurityManager accessible` 这样的异常，通常是因为 Shiro 没有正确地获取到 `SecurityManager` 实例。这可能是因为 `SecurityManager` 没有被注册为 Spring 容器中的一个 Bean，或者 `ShiroFilterFactoryBean` 没有正确地设置 `SecurityManager` 属性。确保你在配置 Shiro 时，已经定义了一个 `SecurityManager` 的 Bean，并且通过 `shiroFilterFactoryBean.setSecurityManager(securityManager())` 方法将其设置给了 `ShiroFilterFactoryBean` 。

最后，确保你的自定义过滤器没有被 Spring 容器重复管理。如果你的自定义过滤器被声明为一个 Spring Bean，并且也通过 `FilterRegistrationBean` 注册到了 Spring 容器中，那么它可能会被执行两次。为了避免这个问题，你可以选择不让 Spring 管理你的自定义过滤器，而是直接在 `ShiroFilterFactoryBean` 中通过 `new` 关键字创建实例 。

## 相关问题

### 为什么使用 DelegatingFilterProxy 而不是直接使用 Filter？

`DelegatingFilterProxy` 是 Spring 提供的一个强大的工具，它允许你将 Spring 容器管理的过滤器（Filter）代理为一个 Servlet Filter。使用 `DelegatingFilterProxy` 而不是直接使用 Filter 主要有以下几个原因：

1. **依赖注入**：
   使用 `DelegatingFilterProxy` 可以让你的过滤器享受到 Spring 的依赖注入功能。这意味着你可以在过滤器中注入其他 Spring 管理的 Bean，例如服务（Service）、数据访问对象（Repository）等。这在直接使用 Filter 时是无法做到的，因为 Filter 不是由 Spring 管理的。

2. **生命周期管理**：
   Spring 管理的 Bean 有完整的生命周期管理，包括初始化和销毁回调。当你的过滤器作为 Spring Bean 时，你可以使用 `@PostConstruct` 和 `@PreDestroy` 注解来执行初始化和清理工作。这有助于管理资源，如数据库连接或文件句柄。

3. **集成 Spring Security**：
   如果你的应用使用 Spring Security，`DelegatingFilterProxy` 是集成 Spring Security 过滤器链的推荐方式。Spring Security 需要能够注入依赖项并管理生命周期，而 `DelegatingFilterProxy` 使得这一点成为可能。

4. **一致的配置方式**：
   使用 `DelegatingFilterProxy` 可以保持你的配置方式一致性。在 Spring 应用中，几乎所有的组件都是通过 Spring 容器来管理的。通过使用 `DelegatingFilterProxy`，你的过滤器可以像其他组件一样被声明和配置。

5. **避免 Filter 链问题**：
   直接在 web.xml 中定义 Filter 可能会导致 Filter 链的问题，特别是当与其他框架（如 Spring Security、Shiro 等）集成时。使用 `DelegatingFilterProxy` 可以避免这些问题，因为它允许 Spring 控制 Filter 的实例化和配置。

6. **简化 Filter 定义**：
   使用 `DelegatingFilterProxy`，你不需要在 web.xml 中定义 Filter。相反，你只需要定义一个 Filter Bean，并让 `DelegatingFilterProxy` 负责将请求委托给这个 Bean。这简化了配置，并减少了 web.xml 的复杂性。

7. **支持 Filter 之间的依赖关系**：
   在某些情况下，你可能需要定义 Filter 之间的依赖关系（例如，确保某些 Filter 在其他 Filter 之前或之后执行）。使用 `DelegatingFilterProxy` 和 Spring 的 `FilterChainProxy` 可以更容易地管理这些依赖关系。

总之，`DelegatingFilterProxy` 提供了一种将 Spring 管理的 Bean 转换为 Servlet Filter 的方式，这使得你可以利用 Spring 的依赖注入、生命周期管理和其他特性。这在构建大型、复杂的 Web 应用时尤其有用。

### 在 Spring 中，Filter 和 DelegatingFilterProxy 有什么区别？

在 Spring 框架中，`Filter` 和 `DelegatingFilterProxy` 都用于处理 Web 请求，但它们的使用场景和功能有所不同。以下是它们之间的主要区别：

1. **Servlet Filter vs. Spring Bean**:

   * `Filter`：是一个实现了 `javax.servlet.Filter` 接口的 Java 类，通常在 `web.xml` 中定义，由 Servlet 容器（如 Tomcat）管理。
   * `DelegatingFilterProxy`：是一个 Spring 提供的代理类，它将 Spring 容器管理的 Bean 代理为一个 Servlet Filter。这意味着`DelegatingFilterProxy` 本身是一个 Filter，但它内部委托给一个 Spring 管理的 Bean 来执行实际的 Filter 逻辑。

2. **依赖注入**:

   * `Filter`：由于是由 Servlet 容器管理，通常不能直接享受到 Spring 的依赖注入功能。
   * `DelegatingFilterProxy`：由于内部委托给 Spring 管理的 Bean，因此可以享受到 Spring 的依赖注入功能，包括自动装配、生命周期回调等。

3. **生命周期管理**:

   * `Filter`：其生命周期由 Servlet 容器管理，例如在 Web 应用启动和停止时创建和销毁 Filter 实例。
   * `DelegatingFilterProxy`：除了由 Servlet 容器管理外，还可以利用 Spring 的生命周期管理功能，例如使用`@PostConstruct`和`@PreDestroy`注解。

4. **配置方式**:

   * `Filter`：通常在 `web.xml` 中配置，或者使用 Servlet 3.0+的注解方式。
   * `DelegatingFilterProxy`：作为 Spring 容器中的一个 Bean 来配置，可以在 Spring 配置文件或使用注解的方式定义。

5. **使用场景**:

   * `Filter`：适用于那些不需要 Spring 特定功能，或者需要在 Spring 上下文之外运行的 Filter。
   * `DelegatingFilterProxy`：适用于需要访问 Spring 上下文中的其他 Bean，或者需要使用 Spring 特性（如依赖注入、生命周期管理）的 Filter。

6. **集成 Spring Security**:
   * 如果你使用 Spring Security，`DelegatingFilterProxy` 是集成 Spring Security 过滤器链的推荐方式，因为它可以确保 Spring Security 的 Filter 能够作为 Spring Bean 被管理。

总结来说，`DelegatingFilterProxy` 提供了一种将 Spring Bean 转换为 Servlet Filter 的方式，使得你可以在 Filter 中使用 Spring 的特性，而普通的`Filter`则不具备这些特性。在实际开发中，选择使用哪种方式取决于你的具体需求和场景。

## 总结

如果需要在 `Filter` 中使用 Spring 的特性，如依赖注入或生命周期管理，那么使用 `DelegatingFilterProxy` 是一个更好的选择。如果你的 `Filter` 不需要这些 Spring 特性，那么直接使用 `Filter` 可能是一个更简单直接的选择。

在 Shiro 与 Spring 集成时，通常建议使用 `DelegatingFilterProxy` 来代理 Shiro 的 `Filter`，这样可以确保 Shiro 的 `Filter` 能够享受到 Spring 的依赖注入和生命周期管理功能。例如，在 Spring 配置文件中配置 Shiro 的 `ShiroFilterFactoryBean` 时，可以通过 `DelegatingFilterProxy` 来确保 Shiro 的 `Filter` 被 Spring 容器管理 。
