Spring Security在web.xml文件中配置了springSecurityFilterChain过滤器链之后报错(已解决)

1、问题描述

在web项目的web.xml文件中配置了springSecurity中的关键bean对象springSecurityFilterChain之后,启动服务器运行的时候报错如下:

09-Mar-2022 10:20:39.888 严重 [RMI TCP Connection(3)-127.0.0.1] org.apache.catalina.core.StandardContext.startInternal 一个或多个筛选器启动失败。完整的详细信息将在相应的容器日志文件中找到
09-Mar-2022 10:20:39.888 严重 [RMI TCP Connection(3)-127.0.0.1] org.apache.catalina.core.StandardContext.startInternal 由于之前的错误,Context[/admin_webui_war_exploded]启动失败
[10:20:39.888] [DEBUG] [RMI TCP Connection(3)-127.0.0.1] [org.springframework.web.filter.CharacterEncodingFilter] [Filter 'CharacterEncodingFilter' configured successfully]
[10:20:39.888] [DEBUG] [RMI TCP Connection(3)-127.0.0.1] [org.springframework.web.filter.DelegatingFilterProxy] [Initializing filter 'springSecurityFilterChain']
10:20:39,888 |-INFO in ch.qos.logback.classic.servlet.LogbackServletContextListener@158852d9 - About to stop ch.qos.logback.classic.LoggerContext [default]
[2022-03-09 10:20:39,919] Artifact admin-webui:war exploded: Error during artifact deployment. See server log for details.

查找了网上的资源说,这个报错是因为有的jar包没有正确的导入,但是自己查看了一下,所有的包都导入了,所以就不是这个问题。

2、web项目中加入了spring security之后的运行机理分析

当我们在正常使用SSM架构进行web开发的时候,实际上这个context中有两个IOC容器,一个是spring(contextLoaderListener)的IOC容器,一个是spring MVC(springDispatcherServlet)的IOC容器,并且spring的容器是父容器,而spring MVC的是子容器,所以两个容器的启动顺序就是:
spring IOC——>spring MVC IOC
也就是说spring IOC的启动先于spring MVC的IOC。

当加入spring security之后,为了实现权限拦截校验的功能,需要在IOC容器中生成一个springSecurityFilterChain的对象,而这个对象的生成是有DelegatingFilterProxy初始化的时候创建的,而contextLoaderListener、DelegatingFilterProxy、DispatcherServlet三大组件的启动顺序是:
首先:完成contextLoaderListener初始化,创建的是spring的IOC容器
其次:DelegatingFilterProxy初始化,查找IOC容器,查找springSecurityFilterChain的对象
最后:DispatcherServlet初始化,完成spring MVC IOC容器的创建

**注意:**我们要想实现权限管理一般都是在浏览器访问服务器的时候,因此我们需要拦截的是请求,也就是在请求被服务器处理之前就拦截,那么我们的拦截器应该是在handler方法之前,所以springSecurityFilterChain应该是需要保存到spring MVC的IOC容器中的(spring 的IOC容器管理的是service和mapper对象;spring MVC的IOC管理的是handler方法的对象,也就是Controller对象)。对于springSecurityFilterChain对象的查找只能在spring MVC的IOC中才能找到,但是发现DelegatingFilterProxy初始化去找这个对象的时候,spring MVC的IOC容器还没出生呢!!!!!!!肯定就无法找到,但是spring security并不会立即报错,说找不到bean对象,而是在第一次初始化的时候去找没有找到的话就会自动放弃,并在第一次请求到来的时候再去找这个springSecurityFilterChain对象,这个时候这个应用里面就有两个IOC容器了,但是,但是,但是:这个时候再去找springSecurityFilterChain对象也还是只会去找spring的IOC容器,并不会去找spring MVC的的容器,所以肯定也会找不到,所以就会报错,我这里的报错信息和网上的报错信息有点不一样,网上的报错信息是说找不到springSecurityFilterChain这个bean对象的异常,我的直接就给我报错说启动错误,凭自己感觉应该就是springSecurityFilterChain找不到的原因。

网上的报错:

org.springframework.beans.factory.NoSuchBeanDefinitionException: No bean named 'springSecurityFilterChain' available
	at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBeanDefinition(DefaultListableBeanFactory.java:687)
	at org.springframework.beans.factory.support.AbstractBeanFactory.getMergedLocalBeanDefinition(AbstractBeanFactory.java:1209)
	at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:284)
	at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:202)
	at org.springframework.context.support.AbstractApplicationContext.getBean(AbstractApplicationContext.java:1086)
	at org.springframework.web.filter.DelegatingFilterProxy.initDelegate(DelegatingFilterProxy.java:327)
	at org.springframework.web.filter.DelegatingFilterProxy.initFilterBean(DelegatingFilterProxy.java:235)
	at org.springframework.web.filter.GenericFilterBean.init(GenericFilterBean.java:236)
	at org.apache.catalina.core.ApplicationFilterConfig.initFilter(ApplicationFilterConfig.java:285)
	at org.apache.catalina.core.ApplicationFilterConfig.getFilter(ApplicationFilterConfig.java:266)
	at org.apache.catalina.core.ApplicationFilterConfig.<init>(ApplicationFilterConfig.java:108)
	at org.apache.catalina.core.StandardContext.filterStart(StandardContext.java:4615)
	at org.apache.catalina.core.StandardContext.startInternal(StandardContext.java:5262)
	at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:150)
	at org.apache.catalina.core.ContainerBase.addChildInternal(ContainerBase.java:754)
	at org.apache.catalina.core.ContainerBase.addChild(ContainerBase.java:730)
	at org.apache.catalina.core.StandardHost.addChild(StandardHost.java:734)
	at org.apache.catalina.startup.HostConfig.manageApp(HostConfig.java:1730)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:498)
	at org.apache.tomcat.util.modeler.BaseModelMBean.invoke(BaseModelMBean.java:287)
	at com.sun.jmx.interceptor.DefaultMBeanServerInterceptor.invoke(DefaultMBeanServerInterceptor.java:819)
	at com.sun.jmx.mbeanserver.JmxMBeanServer.invoke(JmxMBeanServer.java:801)
	at org.apache.catalina.mbeans.MBeanFactory.createStandardContext(MBeanFactory.java:483)

DelegatingFilterProxy 查找 IOC 容器然后查找 bean 的工作机制
在这里插入图片描述

3、解决方案

分析到问题的原因是DelegatingFilterProxy 查找对象的时候,只能去找spring的IOC容器,因此我们需要解决这个问题,让查找bean对象的时候去查找spring MVC的IOC就可以解决了。

修改源码:

第一步:创建和框架提供的DelegatingFilterProxy 相同的类,报名和类名都要完全相同,这是因为JVM在加载类的时候会优先加载我们用户自己写的,所以使用相同包名加类名之后,程序可以使用我们修改过的代码。

第二步:修改DelegatingFilterProxy 中的initFilterBean()这个方法

protected void initFilterBean() throws ServletException {
        synchronized (this.delegateMonitor) {
            if (this.delegate == null) {
                if (this.targetBeanName == null) {
                    this.targetBeanName = this.getFilterName();
                }
//这里我们在初始化的时候就不用去找springSecurityFilterChain这个对象了,因为找不到,没有必要
//                WebApplicationContext wac = this.findWebApplicationContext();
//                if (wac != null) {
//                    this.delegate = this.initDelegate(wac);
//                }
            }

        }
    }

第三步:修改DelegatingFilterProxy 中的doFilter()方法

public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        Filter delegateToUse = this.delegate;
        if (delegateToUse == null) {
            synchronized (this.delegateMonitor) {
                delegateToUse = this.delegate;
                if (delegateToUse == null) {
                    // 把原来的查找 IOC 容器的代码注释掉
//                     WebApplicationContext wac = findWebApplicationContext();
                    // 按我们自己的需要重新编写
                    // 1.获取 ServletContext 对象
                    ServletContext sc = this.getServletContext();
                    // 2.拼接 SpringMVC 将 IOC 容器存入 ServletContext 域的时候使用的属性 名
                    String servletName = "springDispatcherServlet";
                    String attrName = FrameworkServlet.SERVLET_CONTEXT_PREFIX + servletName;
                    // 3.根据 attrName 从 ServletContext 域中获取 IOC 容器对象
                    WebApplicationContext wac = (WebApplicationContext) sc.getAttribute(attrName);
                    if (wac == null) {
                        throw new IllegalStateException("No WebApplicationContext found: no ContextLoaderListener or DispatcherServlet registered?");
                    }

                    delegateToUse = this.initDelegate(wac);
                }

                this.delegate = delegateToUse;
            }
        }

        this.invokeDelegate(delegateToUse, request, response, filterChain);
    }
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值